Engine API reference
Everything the SDKs do, you can do over HTTPS — run a turn, execute a confirmed action, synthesize speech, or hold a live voice socket open.
Every endpoint returns the same thing conceptually: a render spec. That is a speak string for the user to hear, an optional widgets array for the client to draw, and optional suggestions or follow-up copy. Clients render the spec; they never decide what goes in it.
Headers & auth
There is no single key that grants data access. Two headers do different jobs: X-Voqal-Key says which assistant to be, and X-Token says who is asking. Both travel on every request.
curl https://your-engine.example.com/voqal \ -H "X-Voqal-Key: pk_live_…" \ # which assistant -H "X-Token: <user backend token>" \ # who is asking -H "X-Request-ID: prod-84213" \ # prod- or stg- -H "X-Client-Metadata: {\"country_code\":\"EGY\"}" \ -H "Content-Type: application/json" \ -d '{"transcript":"what is my balance","mode":"text"}'Request headers
| Header | Required | Purpose |
|---|---|---|
X-Voqal-Key | Required | Your publishable key. Resolves the tenant — prompt, tools, theme. Unknown keys are rejected with 401. |
X-Token | Required | The end user's token for your own backend. The engine forwards it to your tools; it does not store it. |
X-Request-ID | Required | Routes by prefix and correlates logs across a turn: prod- reaches production, stg- reaches staging. |
X-Client-Metadata | Optional | JSON client context — country code, user id, anything your prompt or tools need. |
/api/v1/create-session, the engine mints a session token bound to that key and expects later requests to be signed with it. A 401 means the client should re-bootstrap the session and retry once — the SDKs already do this silently.Create a session
/api/v1/create-sessionBootstraps a conversation and, in the background, opens the connection to your tools and primes the model's prompt cache. Call it at app launch so the first real turn is not a cold one.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
public_key | object | Optional | The device's ES256 public key as a JWK. Supplying it mints a key-bound session token. |
metadata | object | Optional | Client context stored with the session. |
Response
{ "session_id": "6f2a1c84-3b70-4e51-9d2f-0a71c4e5b810", "created_at": "2026-08-01T09:24:07Z", "session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"}Run a turn
/voqalOne conversational turn. The reply streams back as server-sent events — speak deltas first so audio can start, then the widgets, then done. Assembled, it is a render spec.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
transcript | string | Optional | Typed text, or the final STT result. |
audio_base64 | string | Optional | 16 kHz mono WAV, when you want the engine to transcribe. |
mode | "voice" | "text" | "home" | Optional | Defaults to text. home is the silent opening glance, with no user input. |
messages | array | Optional | Prior turns, oldest first. |
context | object | Optional | sessionId, screen, country_code, device info. |
meta | object | Optional | sdkVersion, platform, locale (BCP-47). |
Response
{ "speak": "Your settleable balance is 42,150 EGP.", "widgets": [ { "type": "stat", "label": "Settleable", "value": "42,150 EGP", "delta": "+8.2%" }, { "type": "list", "title": "Recent", "items": [ { "title": "Card payment", "subtitle": "Today", "trailing": "1,200 EGP" } ] } ], "follow": "Want me to settle it now?", "meta": { "conversationId": "conv_7Zp3Kd10", "direction": "ltr" }}Execute an action
/voqal/executeRuns an action the user has just confirmed. Only allow-listed action tools are runnable here, and the body carries the client's proof that the device gate passed. The reply is a terminal card, not a conversation.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
execute | object | Required | The tool and arguments carried by the confirm widget. |
auth | object | Required | method (faceid, touchid, passcode) and verifiedAt. |
context | object | Optional | sessionId and locale for the reply. |
Response
{ "done": { "type": "success", "title": "Settlement requested", "line1": "42,150 EGP", "line2": "Arrives within one business day" }, "speak": "Done — your settlement is on its way."}Synthesize speech
/voqal/ttsTurns a line of text into audio using the tenant's configured voice. It replies with WAV bytes rather than JSON, so the sample below shows the request body.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
text | string | Required | The line to speak. |
voice | string | Optional | Overrides the tenant's default voice. |
Response
{ "text": "Your settleable balance is 42,150 EGP.", "voice": "default"}Live voice socket
/voqal/streamThe whole voice loop over one socket: microphone frames go up, the engine transcribes on end of speech, runs the turn, and sends the answer and its audio back down. Messages are JSON envelopes with a type.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Required | audio, end_of_speech, or cancel. |
chunk | string | Optional | Base64 PCM frame, on audio messages. |
context | object | Optional | Sent once, on the opening message. |
Response
{ "type": "spec", "spec": { "speak": "You have three payouts pending.", "widgets": [ { "type": "progress", "label": "Payouts cleared", "value": 0.62 } ] }}