> ## Documentation Index
> Fetch the complete documentation index at: https://docs.serval.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Coupa

> Connect Serval to your Coupa business spend management instance so workflows can read and update procurement data on demand.

## About Coupa

Coupa is a business spend management platform covering procurement, suppliers, invoices, and purchase orders. The Serval Coupa integration connects to a single Coupa instance and calls the Coupa REST API on demand from workflow steps - there are no background jobs or scheduled imports.

**Authentication:** OAuth 2.0 client credentials. You create an OAuth client in your Coupa instance and enter its Instance URL, Client ID, Client Secret, and Scopes in Serval. This is a server-to-server connection - no user signs in, and there is no redirect URL to configure.

**Data sync:** On demand only. Serval contacts your Coupa instance when a workflow step runs, exchanging the configured client credentials and scopes for an access token issued by your instance.

## What the Coupa integration enables

| Capability                                 | Description                                                                                                                                                                                                                                              |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Call any Coupa REST endpoint               | A single generic "Coupa API request" action sends authenticated requests to your instance's API, supporting any method, path, query parameters, and request body. There are no prebuilt resource modules - every workflow step goes through this action. |
| Read users, suppliers, and purchase orders | List and look up Coupa records such as users, suppliers, and purchase orders, subject to the read scopes granted to your OAuth client.                                                                                                                   |
| Create and update records                  | Write to Coupa objects, such as updating a user, subject to the scopes granted to your OAuth client.                                                                                                                                                     |
| Paginate large result sets                 | Coupa returns list results in pages controlled by a limit and offset; workflows request successive pages until a page comes back smaller than the page size.                                                                                             |

Anything defined in the [Coupa Core API](https://docs.coupa.com/en/developer-documentation/the-coupa-core-api/coupa-core-api-overview) can be accessed through Serval.

## Get your credentials

Serval authenticates with an OAuth 2.0 / OpenID Connect client that you create in your Coupa instance with the **Client Credentials** grant. Coupa's guides on [OpenID Connect clients](https://compass.coupa.com/en-us/products/product-documentation/integration-technical-documentation/the-coupa-core-api/oauth-2.0-and-oidc/openid-connect-clients) and [setting up an OpenID Connect client](https://docs.coupa.com/en/developer-documentation/the-coupa-core-api/oauth-2.0-and-oidc/set-up-an-openid-connect-client) cover this in full detail.

<Steps>
  <Step title="Open the OAuth client setup page">
    In Coupa, go to **Setup > Oauth2/OpenID Connect Clients**.
  </Step>

  <Step title="Create a client with the Client Credentials grant">
    Click **Create** and set **Grant Type** to **Client Credentials**. Give the client a recognizable name such as "Serval Integration" and complete the required contact fields.
  </Step>

  <Step title="Select the scopes Serval should use">
    Coupa scopes follow the pattern service.object.right - for example core.common.read or core.purchase\_order.read. Select every scope Serval should be able to use; the client creation page lists the available scopes.
  </Step>

  <Step title="Save the client and copy its credentials">
    Save the client, then copy the generated **Identifier** and **Secret**. You will enter these in Serval as the Client ID and Client Secret.
  </Step>
</Steps>

<Warning>
  Store the secret securely as soon as you see it - Coupa may not show it again after you leave the client details page.
</Warning>

<Note>
  Grant the client at least core.user.read, core.supplier.read, and core.purchase\_order.read so Serval's connection checks can pass, plus any additional scopes your workflows need.
</Note>

## Connect in Serval

<Steps>
  <Step title="Open the Coupa connect form">
    In Serval, add a new Coupa integration to open the connection form.
  </Step>

  <Step title="Enter your Instance URL (required)">
    Your Coupa instance hostname (e.g. acme.coupahost.com or acme.coupacloud.com), matching the form's placeholder "acme.coupahost.com". You can paste the full web address - Serval strips any https\:// or http\:// prefix and trailing slash before saving. Leaving it blank fails with "Instance URL is required".
  </Step>

  <Step title="Enter the Client ID (required)">
    The Identifier generated for your OAuth client in Coupa. Leaving it blank fails with "Client ID is required".
  </Step>

  <Step title="Enter the Client Secret (required)">
    The Secret generated for your OAuth client, entered in a password field. Leaving it blank fails with "Client Secret is required". When you reopen the form later, the saved secret appears as a masked value - bullet characters plus its last 4 characters - alongside a **Replace** button.
  </Step>

  <Step title="Add Scopes (optional)">
    Per the form: "OAuth 2.0 scopes to request. Coupa scopes follow the pattern service.object.right (e.g. core.common.read)." Type or paste scopes - commas or spaces split them into individual entries, so you can paste a whole list at once. The field does not offer autocomplete suggestions. These are the scopes Serval includes when requesting access tokens. Although the field is technically optional, enter at least core.user.read, core.supplier.read, and core.purchase\_order.read so the connection health checks below can pass, plus any other scopes your workflows need.
  </Step>

  <Step title="Save the connection">
    Save to finish connecting. Serval then verifies the connection against your instance.
  </Step>
</Steps>

<Note>
  When editing an existing connection, any field you leave blank keeps its stored value - the Instance URL, Client ID, Client Secret, and Scopes are only replaced when you enter something new. To rotate the secret, click **Replace** next to the masked value and enter the complete new secret.
</Note>

## Verifying the connection

Serval runs three health checks against your instance, each requesting a single record to confirm authentication and the matching read scope:

* **List Coupa Users** - confirms Serval can authenticate and list users. Requires the core.user.read scope.
  * Success: "Successfully listed users from Coupa"
  * Failure: "Unable to list users from Coupa. Please verify your OAuth client is valid and has the 'core.user.read' scope."
* **List Coupa Suppliers** - confirms Serval can list supplier records. Requires the core.supplier.read scope.
  * Success: "Successfully listed suppliers from Coupa"
  * Failure: "Unable to list suppliers from Coupa. Please verify your OAuth client has the 'core.supplier.read' scope."
* **List Coupa Purchase Orders** - confirms Serval can list purchase orders. Requires the core.purchase\_order.read scope.
  * Success: "Successfully listed purchase orders from Coupa"
  * Failure: "Unable to list purchase orders from Coupa. Please verify your OAuth client has the 'core.purchase\_order.read' scope."

<Tip>
  Green health checks only prove the three read scopes above. If a workflow step fails while the checks pass, the most likely cause is that the step touches an object or performs a write the client has no scope for - grant the missing scope on the OAuth client in Coupa and list it in Serval.
</Tip>

## Gotchas and troubleshooting

<AccordionGroup>
  <Accordion title="Scopes must be granted on the Coupa client, not just listed in Serval">
    The Scopes field in Serval controls which scopes are requested when tokens are minted, but a scope only works if the OAuth client in Coupa was actually granted it. A scope listed in Serval but missing from the client will not unlock the endpoint - missing read scopes are the most common cause of health check failures.
  </Accordion>

  <Accordion title="Connection verification needs the core.user.read scope">
    Serval's baseline connectivity check lists users, so the OAuth client effectively needs core.user.read for connection verification to pass - even if your workflows never read users.
  </Accordion>

  <Accordion title="Credentials are only attached for standard Coupa hostnames">
    Serval injects access tokens only into requests bound for a direct subdomain of coupahost.com or coupacloud.com, such as acme.coupahost.com. Requests to any other domain - such as a vanity or custom domain - are not authenticated, so the Instance URL must be the standard Coupa hostname.
  </Accordion>

  <Accordion title="Instance URL is normalized on save">
    Serval strips the https\:// or http\:// prefix and any trailing slash before storing the Instance URL. Entering [https://acme.coupahost.com/](https://acme.coupahost.com/) and acme.coupahost.com produces the same stored hostname, which Serval also uses to build the address it requests tokens from.
  </Accordion>

  <Accordion title="Re-saving with a blank secret keeps the stored secret">
    On edit, an empty Client Secret - or the masked bullet value the form submits when you never click Replace - preserves the previously stored secret. Blank Instance URL, Client ID, or Scopes likewise keep their existing values. This is intended behavior, so you can safely update one field without re-entering the others.
  </Accordion>

  <Accordion title="Workflow API paths are relative to your instance's API base">
    For anyone building workflows: the "Coupa API request" action automatically prefixes every request with your instance hostname and the API base, so paths should be entered as bare resource names (such as users) without any leading prefix. List responses come back as plain arrays paged with limit and offset - a page shorter than the limit signals the last page.
  </Accordion>
</AccordionGroup>

***

Need help? Contact **[support@serval.com](mailto:support@serval.com)** for assistance with your Coupa integration.
