Base URL
Authentication
The SDK API uses the same authentication as the embedded widget. Requests are authorized by theX-User-Anon-Id header and validated against your allowed domains list, which you configure in Widget & SDK Configuration.
Include these headers in every request:
*When you send a JWT in the
Authorization header, X-User-Anon-Id can be omitted: the user identity is taken from the token’s sub claim.
Speech to Text
POST /messages.
Speech to text must be enabled in the tenant configuration; it is off by default. Check permissions[].speech_to_text_enabled in GET /config before showing a recording control. Disabled tenants receive 403 when uploading audio.
Send a multipart/form-data body with one required file field. Supported file extensions: .flac, .mp3, .mp4, .mpeg, .mpga, .m4a, .ogg, .wav, .webm. Maximum file size: 25 MB. The same widget or JWT authentication headers used for messages apply here.
POST /messages, include transcription_ids with the returned ID (or several IDs if the draft combines recordings):
transcription_ids as a JSON array string. Each ID must belong to the authenticated user and can be attached to only one message; unavailable IDs return 400. Up to 100 IDs can be attached to one message. Conversation history returns used_speech_to_text: true for messages with linked dictations, including edited drafts. Discard the IDs if the user clears the draft. Omitting the field preserves the ordinary text-message behavior.
Errors use the standard SDK error envelope. Browser recordings require HTTPS (or localhost), microphone permission, and a browser that supports audio recording. Embedded applications also need microphone permission on their iframe.
Conversations
List Conversations
Example request:
Get Unread Count
Get Conversation
Example request:
tool_call_data array listing the tool calls the agent
made while producing that message, in the order they ran. The array is empty
when no tool was used. Each entry carries the tool’s status, tool_name, a
display name, and a type that tells your interface how to render it (for
example status_message, progress_indicator, confirmation_input, or
form). Calls to your own client-side functions also include an arguments
object with the values the function was called with, so you can show what ran.
This is the same shape used by the built-in chat interface, so you can replay a
past conversation — including its tool calls — in a custom UI.
Messages
Send Message
This endpoint returns a
text/event-stream response. You need an SSE-compatible client to consume the stream. If the stream disconnects, use the Get Message Status endpoint to poll for the final result.
Example request (JSON):
multipart/form-data with content, conversation_id, and agent_id as form fields and files for file attachments.
text/event-stream. Each event is a line of text prefixed with data: . Events that belong to a conversation carry it as thread.id; error events such as a rejected file upload carry type and error instead, and end the stream.
Forked conversations
A conversation belongs to the user who started it. If someone else sends a message to it — most often because a conversation link was shared with them — AGO does not add their message to the original. It creates a copy owned by the new sender, seeded with the conversation history, and continues there. The original owner never sees the new sender’s messages, and the new sender’s replies stay private to them. When this happens, the stream opens with aconversation_forked event before any content:
to conversation.
What your integration must do: store the returned ID and use it for subsequent requests. If you keep sending the original conversation_id, your messages are not lost — repeated sends from the same user resolve to the same copy — but GET /conversations/{original_id} keeps returning the original conversation, which never gains the new turns. Your interface would appear frozen while the agent answers into a conversation you never read.
The same conversation_forked object is returned by Get Message Status, so a client that polls after a dropped stream sees it too.
Forking does not apply when the sender is a staff member replying from the agent console, nor to conversations that arrive over Slack, WhatsApp, Messenger or HelpScout, which are shared by design.
What a copy contains: the message text and roles of the conversation so far, capped at the most recent 200 messages. Knowledge sources, file attachments, tool call details and follow-up suggestions are not copied, so messages in a copy are thinner than in the original.
A copy is seeded that way only when the sender could already read the original — with conversation link sharing on, that is anyone holding the link. If your workspace has link sharing turned off, the sender still keeps their message and still gets an answer, but the copy starts empty: no history and no title, since the title is written from the original’s content. They were never able to open that conversation, so nothing from it reaches them.
Carried-over messages are attributed to the new sender, not to whoever shared the conversation, and are returned exactly like any other message. From the moment the copy exists it is that sender’s own conversation, and nothing in a later GET /conversations/{id} marks it as a copy. If you want to show your users that a fork happened, do it once from the conversation_forked event on the stream — it is a notice about the moment, not a permanent property of the conversation.
Stop Message
CANCELED.
Closing the SSE connection on its own does not stop the agent — it keeps
generating and the full answer appears the next time you load the conversation.
Call this endpoint as well as closing the stream.
Path parameters:
Response:
status is stopping when the request was accepted, not_running when the turn
had already finished — stopping a completed turn is harmless, so you can call
this without checking first — or not_supported for a background agent run,
which cannot currently be stopped once started.
If the turn is paused waiting for client functions, cancellation is immediate:
the response is {"status": "stopped", "message_status": "CANCELED"}.
Example:
Continue Message
client_functions_mode: "pause".
The two modes differ in when the agent sees your function results:
placeholder(default): when the agent calls one of your client functions, the turn continues immediately without the result. Submitted results are recorded, but the agent’s conversation history retains the invocation placeholder. Usepausewhen the agent needs the function result to continue.pause: the turn stops when the agent calls your client function(s). The SSE stream ends with astatus: "WAITING_CLIENT"event that listswaiting_tool_call_ids. Execute each function, submit its result viaPOST /tool-calls/{id}/submit(body:{"formData": {...}}), then call this endpoint. The agent continues the same turn with the real results — useful when a function changes what the agent should do next (for example a page navigation followed by filling the destination page).
status: "WAITING_CLIENT" and tool calls in waiting_input status, then
submit and continue as usual.
Path parameters:
JSON request body:
Example flow:
resume.ready flag flips to
true on the last one. Sending a new message to the conversation abandons any
paused turn. The endpoint returns 409 if the message is not paused, if some
tool calls are still waiting, or if the conversation moved on.
Response: a text/event-stream, same format as Send Message.
Get Message Status
Example request:
status field is one of:
Background Agent Results
Message status responses and conversation history messages includebackground_agent, the current state of the latest linked background run, or null when none is available. It can initially be null while queued work has not started.
Use
outcome to display the result. Message status: "DONE" means a report was delivered; that report can describe incomplete work, even after an execution error. A "failed" outcome has explanatory fallback content and message status "ERROR". Neither an ended execution nor all checklist steps being completed is sufficient to infer a completed objective.
Replace the displayed checklist when updated run state arrives, including an empty list. The final checklist replaces the live plan; do not match or merge steps by name or position. Keep each message’s run state separate so a new task does not replace the previous task’s results. Treat missing historical outcomes as unknown, rather than deriving success from message status.
Submit Feedback
Request body:
rating on its own records the thumbs up or thumbs down. Sending reasons, a
comment, or both also files a feedback report, which appears in the Feedback
tab of the dashboard alongside reports submitted from the chat interface — see
Feedback Analytics.
A message holds one report. Submitting feedback again for the same message
replaces the previous reasons and comment rather than filing a second report.
A reason outside the four listed values is rejected with 422 and named in the
response, so a typo fails rather than being dropped silently.
Example request:
204 No Content
Tickets
Create Ticket
multipart/form-data to support file attachments.
Form fields:
Example request:
Configuration
Get Config
home_page block carries what you configured for the widget home screen: the title and subtitle, the widget auto-starter (the first message the widget sends when it opens) and the conversation starter cards (home_starters). Each card gives the label to display (description, falling back to title) and the message to send when the card is clicked (initial_message), so a custom UI can send that message straight away without another request. Handle home_page as optional; an unconfigured home screen can contain null fields and an empty home_starters array. Cards and the auto-starter are already filtered to the agents this user can access.
The proactive block tells the SDK whether proactive mode is enabled for the workspace. When enabled is false, the SDK never shows a nudge and never calls the proactive endpoints.
Get Pre-Chat Form Status
Submit Pre-Chat Form
Example request:
Get Active Incident Banners
agent_ids is empty, the banner applies to all agents. When populated, it only appears for conversations with the listed agents.
Proactive Mode
Used by the SDK’s proactive mode. The evaluate endpoint requires proactive mode to be enabled for the workspace; the events endpoint accepts lifecycle reports regardless, so batches sent around a configuration change are not lost.Evaluate an Intervention
intervene: "agent". A fast model decides whether a nudge is worth showing right now and drafts its message.
Request body:
client_nudge_id is a UUID generated by the SDK for this nudge instance; lifecycle events sent later reference it. agent (optional) is the agent id or name. signals includes counters, page paths, and any registered page_state. Page state is sent to the evaluation model without redaction, so include only values you intend to share with the assistant.
Example response:
intervene is false, message and action are null and the SDK stays silent. action is only ever one of the client_functions declared in the request.
Returns 403 (proactive_mode_disabled) when the feature is off for the workspace or the agent, and 429 (rate_limited) when the per-user evaluation cap is reached — the SDK treats both as “do not intervene”.
Report Nudge Events
source is "static" or "agent". type is one of shown, dismissed, accepted, converted. Events are idempotent per nudge and type: the first reported timestamp wins.
Example response:
Error Format
SDK errors generally use this structure:
Some ticket errors use a string-valued
error and a top-level message instead. For example, creating a ticket on a disabled conversation as a non-staff user returns 410:
error.message for an error object, or the top-level message or error string otherwise.
Related
- Public API v1 — the admin-facing REST API for managing your workspace
- API Key Authentication — how to generate and manage API keys
- SDK Integration — guide to embedding AGO in your application
