External API
Integrate WizChat chatbots into your applications using our REST API.
Overview
The External API allows you to:
- Send messages to your chatbot
- Retrieve responses programmatically
- Integrate chatbot functionality into your apps
- Build custom interfaces
Getting API Keys
- Go to "Settings" > "API"
- Click "Generate API Key"
- Copy and securely store the key
- Note: Keys are shown only once
Authentication
Every request is authenticated with your chatbot's API key in the x-api-key header:
curl -H "x-api-key: YOUR_API_KEY" \
https://your-chatbot-domain/api/external/chat
An Authorization: Bearer YOUR_API_KEY header is also accepted.
Base URL
The External API is served from your chatbot's own domain as a single endpoint:
https://your-chatbot-domain/api/external/chat
Use your chatbot's deployment URL (shown under Settings > API) in place of your-chatbot-domain.
Sending a Message
Request
POST https://your-chatbot-domain/api/external/chat
Content-Type: application/json
x-api-key: YOUR_API_KEY
{
"question": "What is your return policy?",
"sessionId": "optional-session-id"
}
Request Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
question | string | Yes | The message to send to the chatbot |
sessionId | string | No | Session id for conversation continuity — reuse it to keep a caller's turns linked. Omit it and one is generated and returned. |
history | array | No | Prior turns to seed context, if you manage conversation history yourself |
images | array | No | Images for multimodal queries (max 5) — each { "url": "https://…" } or { "base64": "…", "mimeType": "image/png" } |
scope | string | No | Knowledge scope ID to query a specific namespace (Business/Enterprise only) |
Response
{
"success": true,
"answer": "Our return policy allows returns within 30 days of purchase...",
"sessionId": "sess_abc123",
"sources": [
{
"documentId": "doc_xyz",
"title": "Return Policy",
"relevance": 0.95
}
],
"metadata": {
"model": "gpt-4o",
"responseTimeMs": 1234
}
}
Conversation Management
Continue a Conversation
Reuse the sessionId returned by the previous response:
{
"question": "What if the item is damaged?",
"sessionId": "sess_abc123"
}
Start New Conversation
Omit sessionId — a fresh one is generated and returned:
{
"question": "Hello!"
}
On-behalf-of (OBO): act for your own users
By default, every External API request is authenticated only by your chatbot's API key, so all traffic shares a single anonymous identity. On-behalf-of (OBO) lets your own backend tell WizChat which of your end users a request is for, by attaching a short-lived signed token that identifies that user. Each verified end-user identity then gets its own isolated conversation history, conversation memory, and in-chat uploads — one user can never read or write another user's conversation, even by reusing a conversation id.
Most integrations don't need OBO — use your API key, or end-user sign-in for interactive chatbots. Reach for OBO when your application already authenticates its users and you want WizChat to keep per-user context without those users signing in to WizChat separately.
How it works
- Your backend authenticates your user (however you already do).
- Your backend mints a short-lived JSON Web Token (JWT) identifying that user and signs it with your private key.
- Your backend calls the External API with the token in the
X-Wizchat-OBOheader, alongside your API key. - WizChat verifies the token's signature against the public key you registered and scopes the request to that user.
WizChat only ever holds your public key — it never sees your signing key or your users' credentials.
Step 1 — Register your token issuer
- Open your chatbot in the WizChat dashboard.
- Go to the API tab and make sure the External API is enabled.
- Under On-behalf-of (OBO) issuers, click Add issuer and fill in:
| Field | Description |
|---|---|
| Label | Optional name to recognize this issuer (e.g. Production backend). |
| Issuer | The value your backend puts in the token's iss claim (e.g. https://auth.example.com). |
| Algorithm | The signing algorithm: RS256 (RSA, ≥ 2048-bit) or ES256 (EC P-256). |
| Key source | Either a JWKS URL — an HTTPS endpoint serving your public keys, e.g. https://auth.example.com/.well-known/jwks.json — or a public key (PEM) you paste directly. |
- Click Add issuer.
You can register up to 10 issuers per chatbot. Toggle an issuer to Disabled (or delete it) to revoke it — tokens from that issuer stop working within about a minute. OBO stays completely off until at least one active issuer is registered.
Use a JWKS URL if you rotate signing keys — WizChat refetches your key set automatically, including on key rotation. Paste a public key (PEM) if you have a single static key.
Step 2 — Mint a token per request
For each API call, mint a JWT signed with your private key and these claims:
| Claim | Required | Description |
|---|---|---|
iss | Yes | Must match the Issuer you registered. |
sub | Yes | A stable, unique id for the end user in your system. This is what WizChat uses to keep that user's context separate. |
aud | Yes | Must be your chatbot's ID. |
exp | Yes | Expiry (Unix seconds), in the future. |
iat | Yes | Issued-at (Unix seconds). |
nbf | No | Not-before (Unix seconds), if you use it. |
Tokens must be short-lived: the span between iat and exp may be at most 10 minutes. Mint a fresh token per request (or per short burst) rather than reusing a long-lived one.
import jwt from 'jsonwebtoken';
const token = jwt.sign(
{
iss: 'https://auth.example.com', // must match the registered issuer
sub: user.id, // your stable id for this end user
aud: 'YOUR_CHATBOT_ID',
},
privateKeyPem, // your RS256 or ES256 private key
{ algorithm: 'RS256', expiresIn: '5m' } // iat + exp set for you; keep ≤ 10m
);
Step 3 — Call the API with the header
Send the signed token in the X-Wizchat-OBO header. Pass a stable sessionId for each end user's conversation so their turns stay linked and their private history and memory persist across requests. sessionId is optional — omit it and the API generates one and returns it in the response — but reuse the same value on that user's later turns to keep their context.
curl -X POST https://your-chatbot-domain/api/external/chat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Wizchat-OBO: YOUR_SIGNED_JWT" \
-H "Content-Type: application/json" \
-d '{"question": "What did we discuss last time?", "sessionId": "user-42-thread-1"}'
Per-user isolation
Once a request carries a verified OBO token:
- The end user identified by the token gets their own conversation history and conversation memory, separate from every other user and from anonymous API-key traffic.
- Files that user uploads into a conversation are visible only on that user's turns.
- A user can't reach another user's conversation by reusing a conversation id — isolation is enforced on the server.
- For streaming or agentic chatbots, the same token lets a user resume their own in-progress run and approve their own tool-approval prompts; a user can only act on runs they started.
Without an OBO token, plain API-key traffic shares a single anonymous context: cross-session memory and stored history are disabled for those requests (a caller-supplied email can't be trusted), so answers are ephemeral. Registering an issuer and sending OBO tokens is what gives each of your users durable, private per-user context over the API.
OBO errors
| HTTP Status | When |
|---|---|
401 | The token is missing a required claim, is expired, exceeds the 10-minute lifetime, or fails signature verification. |
403 | A user tries to resume a run or approve a tool call that belongs to a different user. |
503 | WizChat temporarily can't load the issuer configuration to verify the token — retry. |
Security notes
- WizChat stores only your public key; your signing key never leaves your systems.
- Keep tokens short-lived (≤ 10 minutes) and mint one per request.
- Disabling or removing an issuer revokes its tokens within about a minute.
Knowledge Scopes
Knowledge scopes are available on Business and Enterprise plans.
If your chatbot has Knowledge Scopes configured, you can target a specific scope by passing the scope parameter. Each scope queries its own dedicated namespace with optional behavior overrides (system prompt, persona, response length).
Discovering Available Scopes
Send a GET request to see which scopes are available:
GET /api/external/chat
The response includes an availableScopes array when scopes are configured:
{
"name": "External Chat API",
"version": "1.2.0",
"enabled": true,
"availableScopes": [
{ "id": "product-a", "label": "Product A" },
{ "id": "product-b", "label": "Product B" }
],
"documentation": { ... }
}
Querying a Scope
Pass the scope ID in the request body:
{
"question": "What features does Product A have?",
"scope": "product-a"
}
The response metadata will include the scope that was used:
{
"success": true,
"answer": "Product A includes...",
"metadata": {
"model": "gpt-4o",
"responseTimeMs": 1234,
"scope": "product-a"
}
}
Scope Errors
| Error Code | HTTP Status | Description |
|---|---|---|
SCOPE_NOT_AVAILABLE | 403 | Scope requested but plan is not Business or Enterprise |
INVALID_SCOPE | 400 | Scope ID does not exist on this chatbot |
Code Examples
JavaScript/Node.js
const response = await fetch(
'https://your-chatbot-domain/api/external/chat',
{
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
question: 'What are your business hours?',
}),
}
);
const data = await response.json();
console.log(data.answer);
Python
import requests
response = requests.post(
'https://your-chatbot-domain/api/external/chat',
headers={
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
json={
'question': 'What are your business hours?',
},
)
data = response.json()
print(data['answer'])
cURL
curl -X POST https://your-chatbot-domain/api/external/chat \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"question": "What are your business hours?"}'
Rate Limits
| Plan | Requests/Minute | Requests/Day |
|---|---|---|
| Starter | 30 | 1,000 |
| Pro | 60 | 10,000 |
| Business | 120 | Unlimited |
Rate Limit Headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1640000000
Error Handling
Error Response Format
{
"success": false,
"error": "Too many requests. Please try again later.",
"errorCode": "RATE_LIMITED"
}
Common Error Codes
| HTTP Status | errorCode (examples) | Description |
|---|---|---|
400 | MISSING_QUESTION, INVALID_JSON, TOO_MANY_IMAGES | Missing or empty question, malformed JSON body, or more than 5 images |
401 | — | API key is missing or invalid |
403 | — | The External API is disabled for this chatbot |
429 | RATE_LIMITED | Rate limit exceeded |
500 | — | Internal server error |
Knowledge-scope and OBO requests can return additional codes — see Scope Errors and OBO errors above.
API Key Management
Viewing Keys
- Go to "Settings" > "API"
- See all active keys
- View usage statistics
Revoking Keys
- Go to "Settings" > "API"
- Find the key
- Click "Revoke"
- Confirm revocation
Revoked keys stop working immediately.
Best Practices
- Store API keys securely (environment variables)
- Rotate keys periodically
- Use separate keys for different environments
- Implement proper error handling
- Respect rate limits