Skip to main content

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

Anything defined in the Coupa Core API 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 and setting up an OpenID Connect client cover this in full detail.
1

Open the OAuth client setup page

In Coupa, go to Setup > Oauth2/OpenID Connect Clients.
2

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.
3

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.
4

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.
Store the secret securely as soon as you see it - Coupa may not show it again after you leave the client details page.
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.

Connect in Serval

1

Open the Coupa connect form

In Serval, add a new Coupa integration to open the connection form.
2

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”.
3

Enter the Client ID (required)

The Identifier generated for your OAuth client in Coupa. Leaving it blank fails with “Client ID is required”.
4

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.
5

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.
6

Save the connection

Save to finish connecting. Serval then verifies the connection against your instance.
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.

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.”
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.

Gotchas and troubleshooting

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.
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.
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.
Serval strips the https:// or http:// prefix and any trailing slash before storing the Instance URL. Entering 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.
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.
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.

Need help? Contact support@serval.com for assistance with your Coupa integration.