Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt

Use this file to discover all available pages before exploring further.

KaireonAI supports OAuth 2.0 client credentials grant (RFC 6749 section 4.4) for machine-to-machine authentication. Create OAuth clients with specific scopes, then exchange credentials for access tokens. Tokens are signed HS256 JWTs with a 1-hour lifetime (src/lib/oauth.ts:60-76).

POST /api/v1/oauth/clients

Create a new OAuth 2.0 client. Admin only.

Request Body

FieldTypeRequiredDescription
namestringNoClient name (defaults to oauth-client-{timestamp})
scopesstring[]NoPermitted scopes: read, write, admin (default: ["read"])
Validated against the static set VALID_SCOPES = {"read","write","admin"} at src/app/api/v1/oauth/clients/route.ts:10. Unknown scopes are silently dropped; an empty result falls back to ["read"].

Example

curl -X POST https://playground.kaireonai.com/api/v1/oauth/clients \
  -H "Content-Type: application/json" \
  -H "X-Tenant-Id: my-tenant" \
  -H "X-User-Role: admin" \
  -d '{
    "name": "Data Pipeline Client",
    "scopes": ["read", "write"]
  }'

Response (201)

{
  "id": "clx...",
  "name": "Data Pipeline Client",
  "clientId": "kci_a1b2c3d4...",
  "clientSecret": "kcs_e5f6g7h8...",
  "scopes": ["read", "write"],
  "createdAt": "2026-03-18T12:00:00.000Z",
  "warning": "Store the client secret securely. It will not be shown again."
}
clientId is kci_ + 16 random bytes hex; clientSecret is kcs_ + 32 random bytes hex (src/lib/oauth.ts:32-33). Only the HMAC-SHA256 hash of the secret is persisted.
The clientSecret is only returned on creation. Store it securely.

GET /api/v1/oauth/clients

List active (non-revoked) OAuth clients for the tenant. Admin only. Returns at most 1000 rows ordered by createdAt desc (src/app/api/v1/oauth/clients/route.ts:85-96).

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "Data Pipeline Client",
      "clientIdPrefix": "kci_a1b2c3d4...",
      "clientId": "kci_a1b2c3d4...",
      "scopes": ["read", "write"],
      "createdAt": "2026-03-18T12:00:00.000Z"
    }
  ],
  "total": 1
}

DELETE /api/v1/oauth/clients?id=

Revoke an OAuth client (soft delete — sets revokedAt). Admin only.

Query Parameters

ParameterTypeRequiredDescription
idstringYesOAuth client record ID

Response

204 No Content on success. 404 Not Found when the id does not belong to the caller’s tenant (src/app/api/v1/oauth/clients/route.ts:137-139).

POST /api/v1/oauth/token

Exchange client credentials for an access token. Follows the OAuth 2.0 client credentials grant. Rate limited to 20 requests per 60 seconds per client_id (or per IP when client_id is absent). The rate limiter is fail-closed — if Redis is unreachable the request is denied to prevent brute-force (src/app/api/v1/oauth/token/route.ts:48). Accepts both application/x-www-form-urlencoded and application/json request bodies.

Request Body

FieldTypeRequiredDescription
grant_typestringYesMust be client_credentials
client_idstringYesOAuth client ID (kci_...)
client_secretstringYesOAuth client secret (kcs_...)

Example — Form Encoded

curl -X POST https://playground.kaireonai.com/api/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=kci_abc&client_secret=kcs_xyz"

Example — JSON

curl -X POST https://playground.kaireonai.com/api/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "kci_abc",
    "client_secret": "kcs_xyz"
  }'

Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read write"
}
The JWT carries { sub: clientId, tid: tenantId, role, scopes, iss: "kaireon", iat, exp } and is signed HS256 with the JWT_SIGNING_SECRET (src/lib/oauth.ts:60-76). role is derived from scopes — adminadmin, writeeditor, otherwise viewer.

Error Responses

StatusErrorDescription
400unsupported_grant_typeOnly client_credentials is supported
400invalid_requestMissing client_id or client_secret
401invalid_clientInvalid or revoked client credentials
429rate_limit_exceededToo many token requests; Retry-After header set

Using the Token

Include the access token in the Authorization header for subsequent API calls:
curl https://playground.kaireonai.com/api/v1/offers \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Configuration

The OAuth subsystem reads three environment variables. The token route hard-fails on startup when the required ones are missing — there is no silent dev-only fallback.
VariableRequiredUsed atEffect when missing
JWT_SIGNING_SECRETYes (or NEXTAUTH_SECRET as fallback)src/lib/oauth.ts:17Throws “JWT_SIGNING_SECRET or NEXTAUTH_SECRET must be set for OAuth operations” — token issue/verify is impossible
API_KEY_PEPPERYessrc/lib/oauth.ts:39Throws “API_KEY_PEPPER environment variable is required for OAuth client secret hashing” — client create/verify both fail
NEXTAUTH_SECRETOptional fallback for JWT_SIGNING_SECRETsrc/lib/oauth.ts:17Used only if JWT_SIGNING_SECRET is unset
Rotating either secret invalidates all in-flight access tokens (HS256 signature verify fails) and breaks clientSecret verification for every existing client. Plan rotations during a maintenance window and re-issue client secrets after the rotation. See also: Authentication | Environment variables