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.
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 can be accessed through Serval.
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.
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.
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.
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.
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.
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.
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/ and acme.coupahost.com produces the same stored hostname, which Serval also uses to build the address it requests tokens from.
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.
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.
Need help? Contact support@serval.com for assistance with your Coupa integration.