Client Credentials

The client credentials grant is designed for server-to-server communication — no human interaction required. Use this for backend services, data pipelines, and automated integrations.

When to Use

  • Backend service calling Admin API
  • ETL / migration pipelines
  • Automated reporting or monitoring scripts
  • Service-to-service calls within your infrastructure

Token Request

curl -X POST http://localhost:5000/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=svc_my_backend" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=admin.read"

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiJ9...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "admin.read"
}

Token Caching

Tokens are valid for 1 hour. Cache the token and re-issue only when it expires to avoid unnecessary load on the Identity API:

let cachedToken: { value: string; expiresAt: number } | null = null;

async function getToken(): Promise<string> {
  if (cachedToken && Date.now() < cachedToken.expiresAt - 60_000) {
    return cachedToken.value;
  }
  const res = await fetch("http://localhost:5000/connect/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: process.env.CLIENT_ID!,
      client_secret: process.env.CLIENT_SECRET!,
      scope: "admin.read",
    }),
  });
  const data = await res.json();
  cachedToken = { value: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
  return cachedToken.value;
}

Rotating Client Secrets

Client secrets are returned once on creation. To rotate:

POST/admin/api/v1/clients/{id}/rotate-secret
curl -X POST "http://localhost:5002/admin/api/v1/clients/CLIENT_ID/rotate-secret" \
  -H "Authorization: Bearer ADMIN_TOKEN"

Response:

{
  "clientSecret": "new_secret_returned_once"
}

The new secret is returned exactly once. Store it immediately — there is no way to retrieve it again.

Registering a Client Credentials App

curl -X POST http://localhost:5002/admin/api/v1/clients \
  -H "Authorization: Bearer ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "appType": "IntegrationApp",
    "name": "My Backend Service",
    "scopes": ["admin.read"]
  }'

The response includes the clientId and a one-time clientSecret.