OAuth for apps
Use an app as an OAuth client of Kustomer so your service can act as a user, or configure auth.oauth2 so Kustomer connects to a third-party API on the admin's behalf.
OAuth works in both directions on the apps platform:
Call Kustomer as a user
Every app has a client ID. To let users sign in to your service with Kustomer and call the API on their behalf, declare your redirect URIs, register and install the app, and mint a client secret.
Diagram source
sequenceDiagram
autonumber
participant Dev as You
participant Apps as Kustomer
participant User as Kustomer user
Dev->>Apps: POST /v1/apps/available (with redirectUris)
Apps-->>Dev: clientId
User->>Apps: Install the app in their org
Dev->>Apps: POST /v1/apps/available/{app}-{version}/secret
Apps-->>Dev: clientSecret (shown once)
User->>Apps: Authorize your service (OAuth authorization code flow)
Apps-->>Dev: access_token + refresh_tokenDeclare redirect URIs
Definition{ "app": "acme_assistant", "version": "1.0.0", "title": "Acme Assistant", "visibility": "private", "redirectUris": ["https://assistant.acme.example/oauth/callback"] }titleis what users see on the consent screen, so make it recognizable.Register and install
Register with
POST /v1/apps/available(see Register and install). The response'sattributes.clientIdis your client ID. Then install the app in the org. Authorization fails with anInvalid Clienterror (400) in any org where the app isn't installed.Mint a client secret
post/v1/apps/available/{app}-{version}/secretcURLcurl -X POST "https://acme.api.kustomerapp.com/v1/apps/available/acme_assistant_${ORG_ID}-1.0.0/secret" \ -H "Authorization: Bearer $KUSTOMER_API_KEY" \ -H "Content-Type: application/json" \ -d '{}'Private app names carry your org ID suffix, so use the registration response's
id, which is<app>-<version>. The response contains theclientSecret, shown only this once; store it in your secrets manager. Calling the endpoint again generates a new secret.Run the authorization code flow
Send the user to their org's authorize URL, exchange the code, and refresh tokens as described in OAuth 2.0. Request the roles you need as scopes, such as
org.permission.customer org.permission.conversation, and addoffline_accessto get a refresh token.
access_tokenstring- Use it as
Authorization: Bearer …, exactly like an API key, limited to both the scopes granted and the user's own permissions. Lasts about 24 hours; readexpires_in. refresh_tokenstring- Returned with
offline_access. Each refresh returns a new refresh token, and a refresh token that goes unused for two weeks expires, so the user must sign in again.
When an admin disables the app, Kustomer revokes the tokens it issued to the app's client. Users sign in again after it's re-enabled.
Call third-party APIs with OAuth
To call an API that uses OAuth 2.0 (a CRM, a commerce platform, a ticketing tool) add auth.oauth2. When an admin connects the app, Kustomer sends them through the provider's consent screen, stores the resulting token as the app's app account, keeps it refreshed, and injects it into every action and command that names that account.
{
"auth": {
"oauth2": {
"clientId": "acme_9f2c41",
"clientSecret": "s3cr3t-from-acme",
"clientCredentialsMethod": "basicAuth",
"authorizationUrl": "https://acme.example/oauth/authorize",
"tokenUrl": "https://api.acme.example/oauth/token",
"authorizationScope": "orders:read refunds:write",
"revokeUrl": "https://api.acme.example/oauth/revoke",
"expiresIn": 3600
}
},
"actions": [
{
"name": "kustomer.app.acme_orders.refund",
"type": "rest_api",
"appAccountName": "default",
"inputTemplate": { "uri": "https://api.acme.example/orders/{{orderId}}/refunds", "method": "POST", "json": true }
}
],
"commands": [
{
"name": "acme_orders.app.lookup-order",
"url": "https://api.acme.example/orders/{{{orderId}}}",
"httpMethod": "get",
"permittedArgs": ["orderId"],
"appAccountName": "default"
}
]
}clientIdstringrequired- The client ID the provider issued for your app.
clientSecretstring- The provider's client secret. Kustomer encrypts it at registration. Keep it out of public repositories: build the definition with the secret injected from your CI secrets.
clientCredentialsMethodstringrequired- How Kustomer sends the client credentials to
tokenUrl:basicAuth(anAuthorization: Basicheader, per RFC 6749 §2.3.1) orform(fields in the form body). authorizationUrlURLrequired- The provider's authorization endpoint, where admins grant access.
tokenUrlURLrequired- The provider's token endpoint, for code exchange and refresh.
authorizationScopestring- The scopes to request, space-separated as the provider expects.
customHeaderstring- Send the token as
<customHeader>: <token>instead ofAuthorization: Bearer <token>. expiresInnumber- Access token lifetime in seconds, for providers that don't return
expires_in. Kustomer refreshes the token before it expires when the provider issued a refresh token. revokeUrlURL- Called when an admin disconnects the account or deactivates the app: an RFC 7009 form
POSTwithtokenandtoken_type_hint.200and400both count as revoked, and a failure doesn't block the disconnect.
Register Kustomer's redirect URI with the provider
Providers only redirect to URIs you've registered with them. Kustomer uses one static URI per app, so it works with providers that require an exact match:
https://api.apps.kustomerapp.com/v1/apps/<app>/authorize/oauth2/redirectReplace <app> with your app's name, including the _<orgId> suffix for private apps.
What admins see
- An admin connects the app from its page in Kustomer, which opens the provider's consent screen with your
authorizationScope. - After the admin approves, the provider redirects back with a code, which Kustomer exchanges at
tokenUrl. The admin has 10 minutes to complete this before the attempt expires. - The token is stored, encrypted with the org's own key, as the app account named
default. Each app has one app account.
How the token is used
Any action or command with "appAccountName": "default" gets the token added to its request:
If a request fails because the provider rejected the token, Kustomer records the error on the app account so the broken connection is visible. The admin fixes it by connecting again. Disabling the app deletes its OAuth connections, so admins must reconnect after re-enabling it.
Without OAuth
If the third-party API uses a static key instead, skip auth and store the key in a secret setting, referenced through appSettings in your actions and commands.