Dokumentation

Die Entwicklerdokumentation ist auf Englisch.

Doku

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.