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
| Field | Type | Required | Description |
|---|
name | string | No | Client name (defaults to oauth-client-{timestamp}) |
scopes | string[] | No | Permitted 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
| Parameter | Type | Required | Description |
|---|
id | string | Yes | OAuth 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
| Field | Type | Required | Description |
|---|
grant_type | string | Yes | Must be client_credentials |
client_id | string | Yes | OAuth client ID (kci_...) |
client_secret | string | Yes | OAuth client secret (kcs_...) |
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 — admin → admin, write → editor, otherwise viewer.
Error Responses
| Status | Error | Description |
|---|
| 400 | unsupported_grant_type | Only client_credentials is supported |
| 400 | invalid_request | Missing client_id or client_secret |
| 401 | invalid_client | Invalid or revoked client credentials |
| 429 | rate_limit_exceeded | Too 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.
| Variable | Required | Used at | Effect when missing |
|---|
JWT_SIGNING_SECRET | Yes (or NEXTAUTH_SECRET as fallback) | src/lib/oauth.ts:17 | Throws “JWT_SIGNING_SECRET or NEXTAUTH_SECRET must be set for OAuth operations” — token issue/verify is impossible |
API_KEY_PEPPER | Yes | src/lib/oauth.ts:39 | Throws “API_KEY_PEPPER environment variable is required for OAuth client secret hashing” — client create/verify both fail |
NEXTAUTH_SECRET | Optional fallback for JWT_SIGNING_SECRET | src/lib/oauth.ts:17 | Used 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