Skip to content

Events and webhooks for Octo integrations

Download .md

Most API calls let an agent observe a turn it started. Events let an agent listen for workspace state changes it did not start — a turn completing, a confirmation resolving, a run finishing — without polling.

There are two complementary surfaces. Use either or both:

  1. Outbound webhooks — register an endpoint; we POST a signed, retried delivery when a subscribed event fires.
  2. GET /v1/events — a long-lived Server-Sent Events stream of the same events, with a resume cursor, for agents that prefer to hold a connection open rather than host an endpoint.

Both carry the same events, and all payloads are snake_case. The stream sends the 2026-10-01 envelope, or v2 with ?api_version=v2; a webhook endpoint receives the format its api_version names (Payload versions).

typeFires whendata fields
conversation.createdA new conversation is openedconversation_id, channel, contact_id?
turn.completedAn agent turn finishesconversation_id, surface, reply_preview, finish_reason, model
confirmation.requestedThe agent parks a risky action for confirmationaction_id, action_code, summary, conversation_id?
confirmation.resolvedA parked action is approved/rejected/expiredaction_id, action_code, resolution
spend.recordedBillable spend is recorded (amounts in credits)amount_credits, category, conversation_id?, balance_credits?
run.completedA workflow run finishesrun_id, workflow_id, status
message.receivedAn SMS or WhatsApp message reaches one of your numbers (after STOP/START handling)message_id, conversation_id, contact_id, channel_id, from, to, body, media[], media_failed, received_at, campaign_id, keyword, channel (sms or whatsapp; absent on older events, meaning sms), quick_reply (a WhatsApp button tap’s payload, else null)
message.sentA text left for the carrier: an AI agent’s or a teammate’s reply, or an API sendmessage_id, conversation_id, author, agent_id, to, from, channel_id, body, send_ids[], sent_at
message.status_updatedA text’s or a WhatsApp message’s status changedchannel, status, send_id, campaign_id, message_id, to, from, state, display_status, segments, billed_credits, error, sent_at, delivered_at, updated_at (SMS). WhatsApp: channel, status, message_id, conversation_id, campaign_id, to, from, error, template, updated_at
campaign.startedA campaign was started (SMS: its send is confirmed)campaign_id, campaign_short_id, name, campaign_type, status, started_at
campaign.pausedA campaign stopped sending: a teammate, out of credits, its credit limit, the carrier’s daily limit, too many failures, the carrier refusing the number, a needed review, a card for the trial number, or a problem on our sidethe common fields, reason, message, paused_at, resumes_at
campaign.resumedA paused campaign is sending againthe common fields, resumed_at, previous_reason
campaign.completedA campaign finishedthe common fields, completed_at, totals, billed_credits
campaign.canceledA campaign was canceledthe common fields, canceled_at, in_flight, not_yet_sent
campaign.waitingAn SMS campaign is outside its sending hours and waits for them to open (not a pause)the common fields, reason, waiting_at, resumes_at, tz
campaign.window_openedIts sending hours started again and it goes on sendingthe common fields, opened_at, tz
meta_response.completedA background POST /v1/responses request finished or failedresponse_id, conversation_id, status, error
meta_chat.updatedA chat with Octo got a new question and reply, and was savedconversation_id, surface, title, preview, message_count
call.ringingAn outbound phone call started ringingcall_id, conversation_id, direction, from, to, agent_id, campaign_id, status, occurred_at
call.answeredA phone call was answered (by a person or a machine)the call fields, answered_at
call.completedA phone call ended (status completed, voicemail, no_answer or busy)the call fields, answered_at, outcome, duration_sec, ended_at, disconnect_reason
call.failedA phone call failed (a system or carrier failure)the same as call.completed

The three message.* events, the seven campaign.* events, the four call.* events, turn.completed, meta_response.completed and meta_chat.updated are emitted today; Receive webhooks says what each message.*, campaign.* and call.* event promises. The other types are published on the same log, webhooks and stream as their emitters roll out: you can subscribe now, but don’t build a hard dependency on them until the changelog announces them. message.status_updated for campaign texts, and the call.* events of a campaign’s calls, reach only endpoints that name the type; "*" does not include them. "*" includes every other type. The campaign.* events are a handful per campaign, so "*" includes them. They are delivered at least once and can, very rarely, be missing after a failure on our side or repeat after a race: act on the status in the payload, not on the number of events, and read GET /v1/campaigns/{id} when it matters (it is the source of truth). A status event we could not write at the time is written later, once (usually 10 to 15 minutes after the change); it carries the time it was written, not the time of the change, and a late campaign.resumed has previous_reason: null.

These two used to be sent by the older integration webhook (/api/users/me/integrations), which is retired: those routes now answer 410 integration_webhook_retired. Subscribe to the types on a webhook endpoint, or read them from /v1/events, like any other event. The payloads are snake_case, unlike the old webhook’s.

  • meta_response.completed: a background response finished (see background mode). response_id is the resp_… id GET /v1/responses/{id} takes, status is completed or failed, and error is null when it completed, otherwise { "type", "code", "message" }, the same error the stored response carries (code is null when there is none). A response failed by our restart sweep sends it too.
  • meta_chat.updated: one per saved Octo turn, wherever it came from (surface: web, api, mcp, or a channel such as email). title is the chat’s title, preview the start of Octo’s reply (about 120 characters), message_count the number of messages saved in the chat.

Every event — over a webhook or the SSE feed — has this shape:

{
"id": "evt_9f8c…",
"type": "turn.completed",
"workspace_id": "5b1d2c7e-…",
"created_at": "2026-07-15T22:31:04.001Z",
"seq": 48213,
"data": { "conversation_id": "cv-…", "surface": "api", "reply_preview": "On it —…", "finish_reason": "stop", "model": "octo" }
}

This is the 2026-10-01 envelope, which the SSE feed and endpoints that existed before release v1.84.0.0 receive. A new endpoint receives the v2 envelope (object, api_version, livemode, and data in the /v1 words): see Payload versions.

workspace_id is the workspace’s UUID. seq is the event’s position in the workspace’s log: the id: of its /v1/events frame, and what to order webhooks by (see Handle events).

Webhooks for these events work exactly as for the messaging events, and are documented once, in Receive webhooks:

tosee
register an endpoint (POST /v1/webhooks, the secret shown once)Register an endpoint
list, change, pause, delete, rotate the secret, send a test eventManage endpoints
check the webhook-signature of a deliveryVerify the signature
the retry schedule, and when an endpoint is disabledRetries
repeats and ordering by seqHandle events
the v2 or 2026-10-01 format an endpoint receivesPayload versions

Owners and admins can also manage endpoints in the dashboard, under Settings → API Keys & Webhooks, where the last 7 days of deliveries show what was sent and what your server answered.

The same events as a Server-Sent Events stream, with a resume cursor, for an agent that would rather hold a connection open than host an endpoint. Pass ?types=turn.completed,run.completed to receive only those. Connecting, resuming, the 7-day log and the stream’s limits: The event stream.