Skip to main content

OAuth 2.0

OAuth 2.0 is an alternative to wpk_live_… API keys for authenticating to the Management API. Instead of a long-lived key, an OAuth client exchanges its credentials for a short-lived access token (oat_…, valid about one hour) that carries a specific set of scopes.

Which one should you use?

  • API key (wpk_live_…) — simplest for a single first-party integration you control.
  • OAuth client credentials — server-to-server access with short-lived, independently revocable tokens; register several clients with different scopes.
  • OAuth authorization code — for interactive apps (such as MCP clients) that act on a user's behalf after a browser consent.

Discovery

The authorization server publishes its metadata (RFC 8414) at:

GET https://www.wizchat.com/.well-known/oauth-authorization-server

It advertises the token, registration, and introspection endpoints, the supported scopes, and that PKCE uses S256. A conformant OAuth client can bootstrap from this document.

Server-to-server: client credentials

The client-credentials grant (RFC 6749 §4.4) is the path for backend integrations. You register an OAuth client once, then exchange its client_id + client_secret for access tokens.

1. Register a client

As the account owner, register an OAuth client. Registration is owner-authenticated — it accepts a Firebase ID token (see Owner tokens):

POST /api/v1/oauth/clients
{
"name": "My integration",
"scopes": ["chatbots:read", "documents:write"],
"allowedChatbotIds": null
}
  • scopes — the scopes this client may request.
  • allowedChatbotIds — restrict the client to specific chatbots, or null for every chatbot you own.

The response returns the credentials once — the clientSecret is never shown again:

{
"clientId": "occ_xxxxxxxxxxxxxxxx",
"clientSecret": "ocs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "My integration",
"scopes": ["chatbots:read", "documents:write"],
"allowedChatbotIds": null,
"status": "active",
"createdAt": "2026-07-08T00:00:00.000Z",
"lastUsedAt": null
}
warning

The clientSecret (ocs_…) is shown exactly once, on creation. Store it somewhere safe — if you lose it, revoke the client and register a new one.

2. Exchange for an access token

The token endpoint is public — no bearer is required, because the client credentials authenticate the request. It accepts JSON or application/x-www-form-urlencoded:

curl -X POST https://www.wizchat.com/api/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "occ_xxxxxxxxxxxxxxxx",
"client_secret": "ocs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}'
{
"access_token": "oat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "chatbots:read documents:write"
}
  • Optionally pass "scope": "chatbots:read" (space-separated) to request a subset of the client's registered scopes.
  • Invalid credentials return 401; a malformed request or unsupported grant type returns 400; exceeding the rate limit returns 429 with a Retry-After header.

3. Call the API

Use the access token as a Bearer credential — exactly where you would put a wpk_live_… key:

curl https://www.wizchat.com/api/v1/chatbots \
-H "Authorization: Bearer oat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

The token is valid for expires_in seconds (currently 3600 — one hour). When it expires, request a new one: the client-credentials grant does not issue a refresh token, so just call the token endpoint again.

info

A token that authenticates but lacks the scope an operation requires returns 403 Forbidden, the same as an under-scoped API key. Each operation's required scope is listed on its API-reference page.

Managing clients

  • List your clients: GET /api/v1/oauth/clients
  • Fetch one: GET /api/v1/oauth/clients/{clientId}
  • Revoke one: DELETE /api/v1/oauth/clients/{clientId} — its tokens stop working immediately.

These endpoints are owner-authenticated and appear in the API reference under the OAuth tag.

Interactive apps: authorization code + PKCE

Interactive clients that act on a user's behalf — such as MCP clients like Claude connecting through claude mcp add — use the authorization-code grant with PKCE instead of a static secret. At a high level:

  1. Register a public client dynamically (RFC 7591): POST /api/v1/oauth/register with your redirect_uris. You receive a public client_id (dyn_…) and no secret.
  2. Send the user to consent at the authorization endpoint (https://www.wizchat.com/oauth/authorize) with a PKCE code_challenge (S256) and a scope tier — view (read-only) or manage (read + write, including deploy). The user signs in and approves.
  3. Exchange the returned code at POST /api/v1/oauth/token (grant_type=authorization_code) with your code_verifier. You receive an access token (oat_…) plus a rotating refresh token (ort_…, valid about 30 days).
  4. Refresh with grant_type=refresh_token. Refresh tokens rotate on every use; reusing a rotated token revokes the entire family.
info

Authorization-code tokens are bound to the WizChat management MCP endpoint (RFC 8707 resource binding) and this flow is used primarily for MCP-client onboarding. For your own server-to-server integrations, prefer the client-credentials flow above.

You can list and revoke the apps you have authorized this way with the owner-authenticated OAuth grants endpoints (GET / DELETE /api/v1/oauth/grants).

Credential reference

PrefixWhat it isLifetime
occ_OAuth client id (client credentials)until revoked
ocs_OAuth client secret — shown onceuntil revoked
oat_Access token (Bearer)about 1 hour
ort_Refresh token (authorization code only)about 30 days, rotating
dyn_Public client id (dynamic registration)until revoked
warning

Treat ocs_… secrets and oat_… / ort_… tokens like passwords — never commit them or expose them in client-side code. Secrets and tokens are stored only as hashes, so a lost secret cannot be recovered; rotate by revoking and re-creating.