GET /v2/calls/{id}/events, sent as
Server-Sent Events,
and it is read-only. The events and their format are the same for outbound
phone calls, campaign calls, inbound calls on your numbers, browser calls and
realtime sessions.
The base URL is https://sandbox.voice.miraiminds.co. API requests use
Authorization: Bearer sk_live_….
Which calls have a stream
Every call has one. How you open it depends on how the call started:
A campaign call or an inbound call has no create response, so it has no
events_url. Read its stream from your backend with your key.
Follow a phone call
Place the call from your backend as usual. The202 carries events_url, the
call’s stream:
202 Accepted
202 has no ws_url and no transport. Those belong to
browser calls, where the page also needs an audio link.
You can open the stream as soon as the call is placed. It stays quiet while the
phone rings, and the first event arrives when the call is answered.
A call nobody answers
A call that is never answered (busy, no answer, or failed at the carrier) sends exactly one event,call.ended, as soon as the call ends:
status is the call’s final status, such as busy,
no_answer or failed. synthesized: true means no conversation ran, so we
wrote the end of the stream ourselves.
Open the stream
From a browser, with events_url
events_url carries its own read-only token for this one call. The token is
valid for the call’s maximum duration plus one hour. Hand the link to the page
that shows the call and open it with EventSource. Your API key never reaches
the page.
Access-Control-Allow-Origin: *) and uses no
cookies, so a page on your own domain can open it directly. The token in the
link is the credential.
- Use the link exactly as returned, and do not build it yourself.
- If you retry the create request with the same
Idempotency-Key, the replayed202repeatsevents_urlwhile its token is valid. After that, open the stream with your key.
From your backend, with your API key
Your key opens the stream of any call in your workspace. This is how you follow campaign and inbound calls, which have noevents_url.
- cURL
- Python SDK
-N turns off curl’s buffering, so each event prints when it arrives.Responses
Show phone calls on your dashboard
This server places phone calls and relays each call’s stream to your dashboard with your API key. The dashboard reads the relay from your own origin, so your own sign-in decides who can watch which call. The relay works for campaign and inbound calls too, because it only needs the callid. Add those calls to
db.calls when their call.started webhook arrives (data.call.id).
It is written in Node.js 18 or later with Express. db and requireUser stand
in for your own code.
server.js
EventSource and shows the agent’s
state and the conversation:
call.html
- For a call you place yourself, you can skip the relay: return
events_urlfromPOST /api/callsand open it in the page withEventSource. The relay is for campaign and inbound calls, and for when your own sign-in should decide who can watch. - If a proxy sits in front of your server, turn off response buffering for the relay route. Otherwise events reach the page in bursts.
- A call nobody answers ends at once on the page, with a line such as
Call ended: no_answer.
Framing
Each event is one SSE message with three lines:idis the position in the stream. Treat it as opaque: it is what a reconnect resumes from.eventis the event type. Because every event is named, listen withaddEventListener(type, …).onmessagereceives nothing.datais a JSON envelope:
Treat the envelope as additive: new fields and new event types can appear.
Ignore what you do not recognise.
Event types
agent.state
On a browser call, the page’s own playback buffer can add a short delay
between
speaking and the moment sound comes out of the speaker.
transcript.turn
One event per completed turn, for both speakers.
Text arrives once per completed turn: the person’s after they finish
speaking, the agent’s after its turn ends. There are no word-by-word captions
yet.
turn.metrics
How fast one agent reply came. It is sent when the reply’s audio starts, and
again when later figures arrive, such as when the reply finishes playing. Each
event carries everything known so far for that reply, so keep the latest one
per reply.
A field is left out until it is known.
tool.started and tool.completed
The agent ran one of its on-call tools. tool.started is sent when
the request to your endpoint goes out, and tool.completed when the result is
back. Only names and timings are sent, never the tool’s arguments or results.
tool.missing
The model asked for a tool this agent does not have. The agent was told so and
carried on. This usually means the prompt mentions a tool that was never added.
flow.node.entered
Sent only for a node-based agent: the call entered a node.
The start node sends one too, with previous_id: null. It arrives before the
reply that node produces, so a page can show the current step as it changes.
flow.variables.extracted
Sent only for a node-based agent: a node’s extraction saved values. It arrives
shortly after the call leaves a node with extraction_enabled, and only when
something was extracted.
call.ended
Always the last event. The stream closes after it.
call.ended is sent even when the call ends abnormally. Use it to update what
your page shows. The webhook is the record your backend acts
on.
Reconnecting
- Automatic resume.
EventSourcereconnects on its own and sends theLast-Event-IDheader. The stream continues after that event, so no turn is missed or repeated. - Manual resume. Add
after=<id>toevents_url, or to the stream URL you open with your key, to start after a given event, for example after a page reload. - One-hour buffer. Events are kept for one hour after the call ends. Opening the stream for a finished call replays what happened, then closes.
- Past the end. A reader that has already received
call.endedgets204 No Content, andEventSourcestops reconnecting. - Close it yourself. Call
events.close()when you receivecall.ended, soEventSourcedoes not try to reconnect. - Keep-alives. The server sends an SSE comment every 15 seconds.
EventSourceignores these; they keep proxies from closing an idle stream, for example while a phone rings.
Steering or stopping a call
The stream is read-only. To steer a browser call or a realtime session while it runs, send a live control. A phone call answers live control with400 unsupported_channel. To stop a phone call, use
POST /v2/calls/{id}/abort.
Privacy
- The stream carries what your customer says. Show it only to people who need it, behind your own sign-in.
- Live events work the same with
recording_enabled: false. No audio is stored, and the text still streams. - Events are kept for one hour after the call ends. For the stored transcript,
use
GET /v2/calls/{id}/transcriptor final results.
Related
Calls
Place calls, read status, transcripts and recordings.
Browser calls
Audio, live events and live control in your user’s browser.
Campaigns
Dial a whole list. Each call has its own stream.
Webhooks
Signed events for each step of a call, and the final transcript.