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:

You wantKustomer is theConfigure
Your hosted service to call the Kustomer API as the user who connected itOAuth providerredirectUris, plus a client secret
Your app's actions and commands to call a third-party API with the admin's grantOAuth clientauth.oauth2

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.

Kustomer userKustomerYouKustomer userKustomerYouPOST /v1/apps/available (with redirectUris)1clientId2Install the app in their org3POST /v1/apps/available/{app}-{version}/secret4clientSecret (shown once)5Authorize your service (OAuth authorization code flow)6access_token + refresh_token7
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_token
  1. Declare redirect URIs

    Definition
    {
      "app": "acme_assistant",
      "version": "1.0.0",
      "title": "Acme Assistant",
      "visibility": "private",
      "redirectUris": ["https://assistant.acme.example/oauth/callback"]
    }

    title is what users see on the consent screen, so make it recognizable.

  2. Register and install

    Register with POST /v1/apps/available (see Register and install). The response's attributes.clientId is your client ID. Then install the app in the org. Authorization fails with an Invalid Client error (400) in any org where the app isn't installed.

  3. Mint a client secret

    post/v1/apps/available/{app}-{version}/secret
    cURL
    curl -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 the clientSecret, shown only this once; store it in your secrets manager. Calling the endpoint again generates a new secret.

  4. 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 add offline_access to get a refresh token.

Tokens
access_tokenstring
refresh_tokenstring

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 (excerpt)
{
  "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"
    }
  ]
}
auth.oauth2
clientIdstringrequired
clientSecretstring
clientCredentialsMethodstringrequired
authorizationUrlURLrequired
tokenUrlURLrequired
authorizationScopestring
customHeaderstring
expiresInnumber
revokeUrlURL

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:

Text
https://api.apps.kustomerapp.com/v1/apps/<app>/authorize/oauth2/redirect

Replace <app> with your app's name, including the _<orgId> suffix for private apps.

What admins see

  1. An admin connects the app from its page in Kustomer, which opens the provider's consent screen with your authorizationScope.
  2. 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.
  3. 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:

customHeaderHeader sent
Not setAuthorization: Bearer <token>
"X-Acme-Token"X-Acme-Token: <token>

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.