API authentication
How to authenticate requests to the SUPERWISE Chat API. This is a reference: pick the mechanism that matches the surface you’re calling, then copy the example.
At a glance
Section titled “At a glance”SUPERWISE Chat exposes two API surfaces, each with its own authentication:
| Surface | Base path | Authenticates with | Use it for |
|---|---|---|---|
| Product API | /v1/* | Authorization: Bearer <JWT> | Conversations, knowledge, memory, admin, and every tenant-scoped resource |
| OpenAI-compatible API | /openai/v1/* | Authorization: Bearer swk_<key> | Drop-in OpenAI client integrations against a published chat endpoint |
| Embed widget | /v1/embed/* | X-Embed-Key: <key> | The chat bubble embedded on a public website |
| Inbound webhooks | /v1/webhooks/{channel_id} | X-Webhook-Signature (HMAC) | Posting external alerts into a conversation |
Every request — authenticated or not — is tagged with an x-request-id (generated if you
don’t send one) and echoed back in error responses. Include it in support reports.
Base URLs
Section titled “Base URLs”| Environment | Base URL |
|---|---|
| Production | https://api.superwise-chat.company.com |
| Local development (Docker Compose) | http://localhost:3080 |
| Local development (bare Node) | http://localhost:3000 |
All product routes are mounted under the /v1 prefix. There is no /v2.
1. JWT — the Product API
Section titled “1. JWT — the Product API”Every endpoint under /v1/* requires a signed JSON Web Token issued by your identity
provider (Frontegg or Auth0). Send it as a bearer token:
GET /v1/me HTTP/1.1Host: api.superwise-chat.company.comAuthorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...What the token resolves to on the server:
| Claim → resolved value | Used for |
|---|---|
| Tenant ID | Pins every database query to your tenant (row-level isolation) |
| User ID | Attributes the request and scopes per-user resources |
| Roles | Evaluated against RBAC capabilities for each endpoint |
A request with a missing or malformed Authorization header is rejected before it reaches
any handler. The token’s audience is verified in production to prevent a token issued for
another service from being replayed against Chat.
Getting a JWT
Section titled “Getting a JWT”JWTs are issued by your identity provider, not by SUPERWISE Chat. In a browser session the web app obtains and refreshes the token for you. For a service or script, request a token from your Frontegg/Auth0 tenant using your provider’s standard OAuth 2.0 client-credentials or login flow, then present it as shown above. Tokens expire; refresh them per your provider’s lifetime.
Local development identity
Section titled “Local development identity”For local development and tests, the server can run with AUTH_PROVIDER=local, which
bypasses JWT verification and resolves identity from request headers (x-tenant-id,
x-user-id, x-user-roles), falling back to environment defaults (DEV_TENANT_ID,
DEV_USER_ID, and optional DEV_USER_ROLES) when a header is absent.
2. swk_ API keys — the OpenAI-compatible API
Section titled “2. swk_ API keys — the OpenAI-compatible API”The OpenAI-compatible surface (/openai/v1/chat/completions, /openai/v1/models) lets you
point an existing OpenAI client library at a SUPERWISE Chat endpoint. It authenticates with
a tenant API key beginning with the swk_ prefix, passed as a standard bearer token.
POST /openai/v1/chat/completions HTTP/1.1Host: api.superwise-chat.company.comAuthorization: Bearer swk_a1b2c3d4_0123456789abcdef0123456789abcdefContent-Type: application/jsonKey format and storage
Section titled “Key format and storage”| Property | Value |
|---|---|
| Format | swk_<8-char-tenant-slug>_<32 hex chars> |
| Visible | Once, at creation. The full key is never returned again. |
| Stored | As a SHA-256 hash. Only the prefix (swk_ + 8 chars) is kept in cleartext. |
| Scopes | A non-empty list, set at creation |
| Expiry | Optional expires_at timestamp |
| Revocation | Soft revoke — the key is marked revoked and stops validating immediately |
Because only a hash is stored, a lost key cannot be recovered. Issue a new one and revoke the old.
Issuing a key (admin)
Section titled “Issuing a key (admin)”Managing API keys requires the rbac.manage capability (a tenant administrator role).
List keys
Section titled “List keys”Returns metadata only — never the full key value.
GET /v1/tenant/api-keysAuthorization: Bearer <admin JWT>{ "keys": [ { "id": "f0e9d8c7-...", "key_prefix": "swk_a1b2c3d4", "description": "Reporting integration", "scopes": ["chat.completions"], "expires_at": null, "is_revoked": false, "created_at": "2026-06-01T12:00:00.000Z", "last_used_at": "2026-06-12T09:30:00.000Z" } ]}Create a key
Section titled “Create a key”scopes is required and must be a non-empty array. An Idempotency-Key header is required
so a retried create never mints a duplicate key.
POST /v1/tenant/api-keysAuthorization: Bearer <admin JWT>Idempotency-Key: 5b9c2a14-...Content-Type: application/json
{ "description": "Reporting integration", "scopes": ["chat.completions"], "expires_at": "2026-12-31T00:00:00.000Z"}The response includes the full key under key — this is the only time it is shown.
Copy it immediately and store it in your secret manager.
{ "id": "f0e9d8c7-...", "key_prefix": "swk_a1b2c3d4", "description": "Reporting integration", "scopes": ["chat.completions"], "expires_at": "2026-12-31T00:00:00.000Z", "is_revoked": false, "created_at": "2026-06-13T10:00:00.000Z", "last_used_at": null, "key": "swk_a1b2c3d4_0123456789abcdef0123456789abcdef"}Revoke a key
Section titled “Revoke a key”Soft-revokes by id (the UUID, not the key value). Idempotent — revoking an
already-revoked or unknown key returns 404.
DELETE /v1/tenant/api-keys/{id}Authorization: Bearer <admin JWT>Returns 204 No Content on success.
| Method | Path | Capability | Returns |
|---|---|---|---|
GET | /v1/tenant/api-keys | rbac.manage | Key metadata (no secret) |
POST | /v1/tenant/api-keys | rbac.manage | Created key including the full secret, once |
DELETE | /v1/tenant/api-keys/{id} | rbac.manage | 204 (soft revoke) |
Calling the OpenAI-compatible endpoint
Section titled “Calling the OpenAI-compatible endpoint”The model field selects a published chat endpoint in your tenant (an endpoint
exposed under a slug), not a raw model name. The endpoint must be enabled, or the call
returns 404 with code: model_not_found.
curl https://api.superwise-chat.company.com/openai/v1/chat/completions \ -H "Authorization: Bearer swk_a1b2c3d4_0123456789abcdef0123456789abcdef" \ -H "Content-Type: application/json" \ -d '{ "model": "support-assistant", "messages": [{"role": "user", "content": "What is your refund policy?"}], "stream": false }'To discover the endpoints your key can address:
curl https://api.superwise-chat.company.com/openai/v1/models \ -H "Authorization: Bearer swk_a1b2c3d4_0123456789abcdef0123456789abcdef"3. Embed key — the website widget
Section titled “3. Embed key — the website widget”The embeddable chat bubble (/v1/embed/*) authenticates with a per-tenant embed key in the
X-Embed-Key header. Because the widget runs on customer websites, this surface allows
cross-origin requests.
POST /v1/embed/sessions HTTP/1.1X-Embed-Key: <tenant embed key>Idempotency-Key: 7c1a...Content-Type: application/jsonAn invalid or revoked embed key returns 401. Creating a session requires an
Idempotency-Key. The embed key is configured per tenant by an administrator; it is not the
same as an swk_ API key.
4. Webhook signatures — inbound alerts
Section titled “4. Webhook signatures — inbound alerts”Posting an external alert into a conversation (POST /v1/webhooks/{channel_id}) is
authenticated by an HMAC signature rather than a token. When you register a channel you
receive a secret once; sign the raw request body with it and send the result in
X-Webhook-Signature.
POST /v1/webhooks/{channel_id} HTTP/1.1X-Webhook-Signature: <hmac-sha256-hex of the raw body>Content-Type: application/jsonA missing or invalid signature returns 401. See the webhooks guide (linked below) for the
full registration flow and signing details.
Error responses
Section titled “Error responses”| Status | Meaning | Common cause |
|---|---|---|
400 | Bad request | Malformed body, or scopes empty on key create |
401 | Unauthenticated | Missing/invalid JWT, swk_ key, embed key, or webhook signature; expired key |
403 | Forbidden | Authenticated, but lacks the required capability (e.g. rbac.manage) |
404 | Not found | Resource missing, or it belongs to another tenant; unknown OpenAI endpoint slug |
429 | Rate limited | Over the per-tenant or per-key limit; retry after X-RateLimit-Reset |
The Product API returns errors as problem+json ({ type, title, status, detail, request_id }) at the framework layer; some handlers return a flatter { error, message }.
The OpenAI-compatible surface returns errors in OpenAI’s shape:
{ "error": { "message": "...", "type": "invalid_request_error", "code": "invalid_api_key" } }.
Operational notes
Section titled “Operational notes”- Keep keys secret. Treat
swk_keys, embed keys, and webhook secrets like passwords. Store them in a secret manager, never in client-side code or version control. - Rotate on a schedule and revoke immediately on suspected compromise. A revoked
swk_key stops validating right away. - One token type per surface. A JWT never works on
/openai/v1; answk_key never works on/v1. - Rate limits apply per surface. The OpenAI-compatible API is limited per key; the
Product API per tenant. Watch
X-RateLimit-RemainingandX-RateLimit-Reset.
Related pages
Section titled “Related pages”- Gateway API reference — full endpoint, request, and response reference
- Integrate with the API — a worked end-to-end integration walkthrough
- Webhooks — registering channels and signing inbound alerts