Events and live streams
Every intent and session keeps a timeline: its status changes and its steps, oldest first. Read it as a page, or follow it live as Server-Sent Events.
The event object
{
"id": "evt_3b9f1a",
"object": "event",
"type": "status",
"created_at": "2026-10-13T08:30:04Z",
"data": { "status": "running" }
}type says what data holds. New types may appear: skip the ones you don't
know.
type | Of | data |
|---|---|---|
status | intents, sessions | { status, reason? }: the new status, and why when there is a why (a failed intent's error code, say). |
step | intents | { action }: what the agent did: open, look, click, type, read. Never what it typed. |
checkout | intents | { merchant, total, currency }: the payment step was reached. |
page | sessions | { url, title }: the session's page changed. |
command | sessions | { id, type, status, typed_chars? }: a command ran. |
An intent that moves to a fresh browser shows { "status": "queued", "reason": "browser_lost" }, and one tried again to get past a shop's check,
{ "status": "queued", "reason": "retrying" }. One that waits for its
customer to sign in shows { "status": "requires_login", "reason": "login_required" }, then { "status": "queued", "reason": "signed_in" }.
One whose agent asks your customer something to sign them in shows
{ "status": "requires_input", "reason": "code" } (or phone, email,
approval), then { "status": "running", "reason": "answered" } (or
declined, approved, expired). Never the answer itself.
A page of events
{
"object": "list",
"data": [{ "id": "evt_3b9f1a", "object": "event", "type": "status", "data": { "status": "queued" } }],
"next_cursor": "evt_3b9f1a"
}Pass the next_cursor back as cursor for the next page.
Live streams
Ask the same endpoint for text/event-stream:
curl -N https://api.openintents.io/v1/intents/int_7f3a2c/events \
-H "Authorization: Bearer $OPENINTENTS_API_KEY" \
-H "Accept: text/event-stream"id: evt_3b9f1a
data: {"id":"evt_3b9f1a","object":"event","type":"status","created_at":"2026-10-13T08:30:00Z","data":{"status":"queued"}}
id: evt_3b9f2c
data: {"id":"evt_3b9f2c","object":"event","type":"status","created_at":"2026-10-13T08:30:01Z","data":{"status":"running"}}
: keep-alive- Past events come first, then new ones as they happen. Lines starting with
:only keep the connection open. - Once the intent or session is over and its last event is sent, the stream closes.
- To resume, reconnect with the last event's
idinLast-Event-ID: you get what came after it, without repeats. A stream may also close on its own after a while; resuming picks it up. - Asking for the stream of something over, with nothing left to send,
answers
204 No Content: stop there. - Too many open streams answer
429withcode: too_many_streams; try again shortly.
A browser's EventSource can't send an Authorization header, so follow
streams from your server, or read the response body as a stream with fetch.
Events or webhooks
Webhooks tell your server about the changes that matter (completed, failed, payment required), whether or not anyone is watching. Event streams show everything as it happens, to whoever follows: a progress view for your users, say.