Send a chat message
Sends a message to the AI and creates a quest to process it. By default (async) the call returns immediately with a quest id; poll GET /api/v1/quests/{id} for the reply. Send wait: true to block until the reply is ready and receive it inline. A wait: true turn is bounded at about 45 seconds: one that runs longer is ended (unused credits refunded) and answered with a 504 naming the quest in quest_id. Send long or tool-heavy turns (large attachments, tool calls) in the default async mode and poll instead. A tool that produced machine-readable state reports it under toolPayloads - an array of { type, payload } entries in emission order, alongside (never instead of) the prose reply - on the wait: true body and on the polled quest. A turn can FAIL after the ACK - reported on the polled quest as type: "error", since the ACK was already sent. A terminal status: "stopped" (a missing session, a user-cancelled turn) is ALSO a failure even without type: "error" - it carries an explanatory string in reply/replies rather than an answer. type is the failure signal for the classified failure classes (an abort, a provider timeout, credit exhaustion); errorCode is an optional refinement present only when the failure is a classified billing reason. A recovered stuck quest is NOT in that list: one that still has renderable content resolves as a success by design. QUEST_ERROR_CODES has two members, but only insufficient_credits is raised as a quest errorCode by any current throw site on this endpoint - spend_cap_exceeded is thrown only by the embed chat route's pre-flight, which fires outside the process try/catch that would classify it onto a quest. A caller must treat type: "error" OR a terminal status: "stopped" as failure even when errorCode is absent, and must not read reply as an answer without checking those first. Authenticate with an API key (b4m_live_) or a JWT. Session resolution: pass sessionId to continue an existing notebook. An API-key caller that omits it gets a BRAND-NEW notebook rather than the notebook the human last had open, and newConversation: true forces a new notebook for any auth mode; both return the new id as sessionId, so the caller can continue the conversation by passing it back. A first-party JWT caller that omits sessionId keeps the last-opened-notebook fallback. Sending sessionId and newConversation together is a 422.
Request
- Base URL: https://your-deployment.example.com
- URL: https://your-deployment.example.com/api/chat
- Auth: one of:
- HTTP bearer
- API key in header x-api-key
Request body
Example request
{
"message": "How do I reset my password?",
"toolMode": "smart"
}Response
Message accepted - NOT a completed turn. The default (async) path returns this queued ACK; the outcome arrives on GET /api/v1/quests/{id} (see the sendChatMessage200PollResult schema). With wait: true the body is instead the completed turn: the ACK fields plus the reply (response/responses), toolPayloads, createdAt, performance timings and, when requested, promptDetails/promptText. A turn that FAILS still resolves with 200, never a 4xx, on both that wait: true body and the polled quest (GET /api/v1/quests/{id}) - the prose explaining why lands in reply/response like any other answer, so the reply text alone cannot tell a failure from an answer. type is the field that can: both surfaces carry it unconditionally, so match on type: "error" first - it covers credit exhaustion, a provider timeout or overload, and an in-process aborted turn; a real answer carries the turn's actual completion type instead ("message" for an ordinary reply). Two related states do NOT set type: "error": a user-cancelled turn resolves as status: "stopped" with type left at "message", and a recovered stuck quest that still has renderable content resolves as a success (status: "done", no error) by design, to avoid destroying content to report a failure. errorCode then names the failure reason, but only for the billing failures that have one - "insufficient_credits" today; it is absent on every other type: "error" turn, so never use its absence to infer success. On a real answer errorCode is absent from the wait: true body. The body also carries sessionId, the notebook the turn was recorded in - for an API-key caller that sent none (or any caller sending newConversation: true), that is the freshly created notebook's id; pass it back as the request's sessionId to continue. Contrast the tts/music/soundEffects contracts, which reject synchronously with a 422 carrying the same errorCode vocabulary.
Changes
No recorded changes to this operation across all 1 revision of this API.