open_thread— start (or re-open) a chat thread and get aconnect_urlto stream the agent’s messages.submit_messages— hand it each batch of inbound messages; it returns aspeak/stay_silentdecision.respond— when the decision isspeak, submit your agent’s drafted reply; it is paced out and delivered on the thread’s WebSocket.
record_event is an optional fourth
call for reporting non-message activity (a user starts or stops typing, edits a
message). To verify your token before sending any traffic, see
whoami.
Turn-taking is a per-message API: submit_messages on every inbound message,
respond on every reply. That is the opposite of components like
extract and analyze,
which belong on a slow clock. See
When to call each API for how the pieces fit together.
The integration loop
A typical client wires turn-taking into a chat UI like this:- Open a thread when the conversation starts. Keep the returned
thread.id, and open a WebSocket torealtime.connect_url. - Stream the agent’s messages from that WebSocket for the life of the thread (see Receiving messages from the agent).
- On every inbound human message (or small batch), call
submit_messages. Keep theturn_epochit returns. - If the decision is
stay_silent, do nothing — wait for the next inbound message. If it isspeak, have your agent draft a reply and callrespondwith that draft and theturn_epochfrom step 3. - The reply is paced into chat messages and pushed to you over the WebSocket — render them as they arrive. Repeat from step 3.
Threads
A thread is one conversation and the unit of state. Open it once withopen_thread:
- Omit
thread_idto start a fresh thread — a new id is minted and returned. - Pass a
thread_idto re-open a specific thread. The id is an idempotency key: opening an id that doesn’t exist creates it; opening one that does re-issues itsconnect_urlwithout creating a duplicate (this is the reconnect path). Opening a thread you don’t own reads as absent.
Receiving messages from the agent
open_thread returns a realtime grant with a short-lived connect_url. Open
a WebSocket to that URL exactly as given — it already carries everything
needed to attach to the thread’s channel:
string
A unique id for this delivery.
string
The event type — one of the events below.
string
The thread channel the event belongs to,
turn-taking-thread/{thread_id}.string
When the event was emitted (ISO 8601).
object
The event payload, shaped by
type (see below).turn_taking.message.position is the message’s 0-based order within the reply,
so you can render a multi-message reply in sequence.
turn_taking.message.metadata is the opaque object you passed as metadata on
the respond that produced the reply,
echoed back verbatim. It is identical on every message of that reply, and
null when you sent none.
The
connect_url is short-lived. If the socket drops or the URL expires,
re-open the thread with its thread_id
(open_thread) to get a fresh
connect_url, then reconnect. Re-opening does not create a new thread or lose
state.Interruptions and turn_epoch
People talk over each other, and so do real users. Every submit_messages
returns a turn_epoch — a counter for the thread’s current turn. You pass that
same turn_epoch back in the matching respond.
If a newer batch of messages arrives (a higher turn_epoch) before your draft
is submitted, the conversation has moved on and your reply is stale. respond
detects this, schedules nothing, bills nothing, and returns
superseded: true. Draft against the latest decision and submit promptly.
Behavioural signals
When you open a thread you can enable behavioural signals by sending anintegrations.social_signals block. With it on, turn-taking watches the timing
of the conversation and surfaces two extra things:
- Per-batch
tagson thesubmit_messagesresponse — short behavioural labels for the messages in that batch (for example["fast", "comeback"]). turn_taking.signalevents on the WebSocket for activity that isn’t a message (for example a user going silent, or typing without sending).
record_event, and pass client_ts
on inbound messages so timing is measured from the client clock. Without the
integrations.social_signals block, turn-taking runs standalone: tags is
always empty and no turn_taking.signal events are sent.
Social memory
When you open a thread you can enable social memory by sending anintegrations.social_memory block. With it on, the agent remembers what it
learns about the people in the conversation across turns, and each
submit_messages response carries a recalled_context field: a short prose
block of what the agent knows about the people involved in the current message —
their preferences, history, and situation — ready to inject into a reply.
Memory is written and read automatically as part of the decision, so there is no
separate call to make. The context is recalled once per turn and handed back
to you. Reuse that single value everywhere the reply is shaped:
- Your own draft — prepend
recalled_contextto the prompt your agent uses to write the reply, so the reply is grounded in what you know about the person. respond— foldrecalled_contextinto thesystem_promptyou pass torespond. That samesystem_promptreaches the reply-refinement step, so the whole reply path stays consistent without a second lookup.
recalled_context is empty until the memory bank has history to draw on, and on
a stay_silent decision you can simply ignore it. Enabling social memory adds a
memory read to the billable decision call.
By default each thread has its own memory bank (isolated to that thread) —
the service never merges banks by guessing, because only you know what “the
agent” is (an account may run several). To let one agent remember a person
across all its channels, open its threads with the same stable
memory_bank_id. Keep distinct agents on distinct bank ids so their memories
never mix.
Authentication
Every request authenticates with a bearer token. See Authentication.Billing
submit_messages (the decision) and respond (the reply) are billable and
metered in credits; open_thread and record_event are not. Two calls that
look billable are not:
- A
respondthat comes backsuperseded: true. Nothing was scheduled. - A
submit_messageswhose decision was short-circuited byskip_decideor by a message withhas_media. No decision was computed.
Errors
All actions return the standard error envelope. Common status codes across the turn-taking actions:Next
- Open a thread — start a conversation and connect.
- Submit messages — get a speak / stay-silent decision.
- Respond — pace out the agent’s reply.
- Record an event — report typing and edits.
- Who am I — verify a token before sending traffic.

