La documentation pour développeurs est en anglais.
Docs
API reference
Send the conversation you already have. If WebKnox can answer the last user turn with a sourced, structured answer, you get a typed card plus plain text. If not, you get matched: false and reply with your own model. You are not billed for unmatched turns.
Machine-readable versions of everything below live in schemas/. Every example in fixtures/ is a complete request/response pair that validates against them. The Answer Lab and the renderer tests read those same files, and the backend's conformance tests will too.
Endpoint
POST https://api.webknox.com/v2/answers (proposed host)
Headers:
Authorization: Bearer <api key>. Call from your server; never put the key in a browser bundle.Content-Type: application/json.
The API is stateless. There is no session and no conversation memory: each request carries the conversation it needs. An answer is stored only when you ask for hosted render URLs, and is deleted within a day after render.expires_at.
Request
{
"messages": [
{ "role": "user", "content": "What is the weather in Dresden?" },
{ "role": "assistant", "content": "15° and partly cloudy in Dresden right now…" },
{ "role": "user", "content": "Do I need an umbrella tomorrow?" }
],
"locale": "en",
"context": {
"timezone": "Europe/Berlin",
"now": "2026-10-21T14:00:00+02:00",
"location": { "lat": 51.05, "lon": 13.74 },
"units": "metric"
},
"accept": ["weather.forecast", "quick_answer"],
"render": { "theme": "midnight", "layout": "glance", "formats": ["embed", "image"] }
}
| Field | Required | Meaning |
|---|---|---|
messages |
yes | Up to 100 turns, oldest first. Pass your history as it is: user and assistant turns are read; system, tool and other roles, and extra fields such as name, are ignored. content is plain text up to 20,000 characters (long turns are truncated for routing); null or structured content, such as tool calls or image parts, is ignored. The last user/assistant turn must be from the user: that is the turn being answered. Earlier turns are only used to resolve references ("and tomorrow?", "a large one") and to continue a quiz, which lives in the conversation: send our answers' text back unchanged as the assistant turns. The schema cannot express the last-turn rule; the server checks it and answers 400 invalid_request. |
locale |
no | en (default), de, fr, es, pt, it. The language of text, speech and localized card fields. Content that exists only in English (jokes, quotes, life hacks, recipes, observances) is answered in English, and the response's locale says en. |
context.timezone |
no | IANA zone. Used for "today", "tomorrow" and local times. |
context.now |
no | The user's current time (ISO 8601 with offset). Defaults to the server time in timezone. |
context.location |
no | {lat, lon} of the user, for "here" questions such as weather or sunset. Never inferred from IP. |
context.units |
no | metric (default) or imperial. |
accept |
no | Card types your client can render. If the best answer has another type, card is null and text still answers. Omit it to accept all types. |
render |
no | Ask for hosted render URLs (see Rendering). Without it the response is pure data. |
Response
HTTP 200 for every answered or unmatched turn.
{
"v": 2,
"id": "ans_7a1c9e2b40d8",
"matched": true,
"locale": "en",
"skill": "weather",
"confidence": 0.97,
"text": "15° and partly cloudy in Dresden right now…",
"speech": "It is 15 degrees and partly cloudy in Dresden…",
"card": { "type": "weather.forecast", "version": 1, "data": { } },
"sources": [{ "name": "MET Norway", "url": "https://api.met.no", "license": "CC BY 4.0" }],
"follow_ups": ["Do I need an umbrella tomorrow?"],
"actions": [],
"usage": { "billable": true }
}
| Field | Meaning |
|---|---|
v |
Always 2 for this version. New optional fields can appear without a version change; ignore what you don't know. |
id |
Random answer id of at least 12 characters. Support requests use it. |
matched |
true: WebKnox answered. false: no rich answer; use your own model. |
reason |
Only when unmatched: no_rich_answer (outside what WebKnox does) or source_unavailable (a data source failed; retry later or fall back). More reasons may be added; treat unknown ones like no_rich_answer. |
locale |
Language of the answer: the request's, or en for English-only content. Renderers use it for their labels. |
skill |
Which skill answered, e.g. weather, nutrition, math. |
confidence |
0–1. How sure the router is that this skill was meant. |
text |
Plain-text answer. Always present when matched. It is the accessible alternative to the card and enough on its own for a text-only chat. |
speech |
Shorter text for text-to-speech. |
card |
{type, version, data} or null. data follows the schema for that type and version, documented in schemas/cards/. New card types can appear: show text for a type you don't render. |
sources |
Where the data came from. license is set when the source requires attribution. Show them; the WebKnox renderer always does. |
follow_ups |
Up to 3 suggested next user turns. |
actions |
Client actions you may execute, e.g. {type: "timer.start", label, duration_s}. Never required for display. |
usage.billable |
Whether this turn counts toward your plan. |
attribution |
{text, url}: a credit such as "Answer by WebKnox" in the answer's locale (up to 80 characters) and an https link for it. Set on the Free plan: show it; the WebKnox renderer does. Absent or null on paid plans and on unmatched answers. |
render |
Only when requested, and only on a matched answer with a card: {embed_url, image_url, expires_at} with the URLs of the formats asked for (see Hosted render). Both URLs carry a random token: they are capability URLs, so anyone holding one can view that answer until it expires. |
An unmatched answer:
{ "v": 2, "id": "ans_5d0f3d9e7a21", "matched": false, "reason": "no_rich_answer", "locale": "en",
"text": null, "card": null, "sources": [], "follow_ups": [], "actions": [], "usage": { "billable": false } }
WebKnox deliberately does not answer what a general chat model already does well: open-ended explanations, smalltalk, writing help, stories. Those come back unmatched, so your own model replies at no extra cost.
Errors
Non-2xx responses carry {"error": {"code", "message", "retry_after_s"?}}. Every 429 carries retry_after_s, in whole seconds.
| HTTP | code |
When |
|---|---|---|
| 400 | invalid_request |
Schema violation: no messages, last message not from the user, unknown locale… |
| 401 | unauthorized |
Missing or revoked key. |
| 403 | plan_required |
The request asks for something its plan doesn't include: hosted render URLs (render) need Pro or Scale. |
| 429 | rate_limited |
Too many requests; wait retry_after_s. |
| 429 | quota_exceeded |
No tokens left today: the daily quota and any packs are used up; wait retry_after_s (until 00:00 UTC) or buy a pack (packs need a paid plan). |
| 500, 503 | internal, unavailable |
Our fault; retry with backoff. A 503 also means hosted render URLs you asked for could not be made; nothing was charged. |