Introduction
Start conversations with your agents and exchange messages from your own website or backend.
The Dispatch API lets your website talk to a live agent: start a conversation, send the visitor's messages, and show the agent's replies as they are written.
Base URL
https://api.rundispatch.ai/v1Every request and response body is JSON, except replies to a message when you ask for another reply type: Server-Sent Events, Markdown or plain text.
Authentication
The API uses two kinds of credentials, both sent as a bearer token:
Authorization: Bearer <credential>| Credential | What it is | Used for |
|---|---|---|
| API key | Your Dispatch API key. It only allows starting conversations with the agents you chose. | Starting a conversation, Getting a conversation |
| Participant token | The visitor's token. Returned when you start or get a conversation, and works in every conversation the visitor takes part in for 90 days. | Sending a message, Listing messages |
To get an API key, contact the Dispatch team. Tell us which agents it should be able to start conversations with.
Your API key is visible
If you call the API from the visitor's browser, the API key is visible in your page's source. That is expected: it can only start conversations with the agents it allows. Keep each participant's token to that visitor.
The typical flow
Start a conversation with POST /agents/{agent}/conversations,
using your API key. Send what you know about the visitor in user, and anything the agent should know in context.
Keep the token. The response's _embedded.participants[0] is the visitor. Store its token: it works in every
conversation that visitor takes part in, until token_expires_at (90 days). A visitor you start another conversation
for with the same email keeps the same token, unless it expires within 7 days: then they get a new one.
Send messages with POST /conversations/{conversation}/messages
and the visitor's token. The Accept header picks the reply type; text/event-stream streams it back
as it is written.
Show the history with GET /conversations/{conversation}/messages,
newest first, for example when the visitor comes back.
Lost a token, or is it about to expire? Get the conversation again with
the API key that started it. From 7 days before token_expires_at, even once it has passed, that returns a new token.
Links
Responses carry _links: absolute URLs for what you can do next, such as list_messages and send_message on a
conversation.
Reply types
Sending a message returns the agent's reply in the type the Accept
header asks for:
Accept | Reply |
|---|---|
application/json (or none) | All at once, as JSON: data.markdown, the files the agent shared in data.attachments, and the web pages it cited in data.citations (each {url, title}, title may be null; empty when the agent's Show sources is off). |
text/event-stream | Streamed as it is written. |
text/markdown | All at once, as Markdown. Shared files appear as images or links where they were written. |
text/plain | All at once, as plain text without Markdown, for places such as SMS. The agent shares no files. |
Any other type returns 406.
Streaming replies
A streamed reply is a series of data: lines, each holding one JSON event, ending with data: [DONE]. Append the
delta of each text_delta event to show the reply as it is written:
const response = await fetch(conversation._links.send_message.href, {
method: 'POST',
headers: {
Authorization: `Bearer ${participant.token}`,
Accept: 'text/event-stream',
'Content-Type': 'application/json',
},
body: JSON.stringify({ content: 'Do you deliver on Sundays?' }),
});
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = '';
let reply = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const data = line.slice('data: '.length);
if (data === '[DONE]') break;
const event = JSON.parse(data);
if (event.type === 'text_delta') reply += event.delta; // render `reply` here
}
}An embed_file event is a file the agent shared, with its name, mime_type and url. Ignore event types you don't
use; new ones may be added.
Errors
Errors return a JSON body with a message:
{ "message": "This agent is not live." }| Status | Meaning |
|---|---|
401 | The credential is missing or not valid, or the token has expired. |
403 | The agent is in draft ("This agent is not live."), the API key doesn't allow this agent, or the token's visitor doesn't take part in the conversation. |
404 | The agent or conversation doesn't exist, or your credential can't see it. |
406 | The Accept header asks for no reply type the API returns. |
422 | The request is not valid. errors lists the messages for each field: { "message": "…", "errors": { "content": ["…"] } } |
429 | Too many requests. Wait for the Retry-After seconds and try again. |
502 | The agent could not reply (any reply type but streaming). Your message was saved. |
Rate limits
Each IP address can make 60 requests per minute. Past that, requests return 429 until the minute is up.
OpenAPI
The full API is described in openapi.yaml, which you can load into a code generator. To try the API in Postman, Insomnia or Bruno, download the collection.