Skip to main content
Agent Mailbox pushes events rather than making you poll. Two transports carry the same payloads: webhooks for server-side handlers, WebSocket for a long-lived agent process.

Event types

Three further types — message.delivered, message.bounced and message.complained — are accepted on subscriptions but never fire on the current deployment. They need asynchronous feedback from the mail transport that is not available today. They are accepted so that client code stays portable; do not build logic that waits for them.

Webhooks

Response
secret is returned once, on create. Later reads of the webhook omit it. Store it when you receive it.
event_types is required and must be non-empty. Omitting inbox_ids subscribes to every inbox the key can see — and a scoped key’s webhook is narrowed to that key’s scope automatically, whatever the request asked for.

Delivery headers

Verifying the signature

Compute the HMAC over the raw request body, before any JSON parsing. Re-serializing the parsed object will not reproduce the same bytes.
Express
Compare with a timing-safe function, not ===.

The payload

message.received payloads also carry matched_by — which threading layer resolved the message — and auto_reply, flagging vacation responders. Check auto_reply before letting an agent answer.

Retries

A delivery is successful on any 2xx. Anything else is retried: Six attempts in total, then the delivery is marked failed. Each attempt times out after 30 seconds.
Because retries are driven by durable alarms rather than an in-memory queue, a delivery scheduled for twelve hours from now survives restarts and deploys.
Return 2xx before you call a model. A handler that does its work synchronously and takes 40 seconds will hit the 30-second timeout, be recorded as failed, and be retried while the first attempt is still running — so your agent answers the same email twice.

Managing webhooks

PATCH { "enabled": false } pauses a webhook without losing its configuration or secret.

WebSocket

For an agent process that stays up, a socket avoids needing a public HTTPS endpoint at all.
Connect, then subscribe:
The server replies {"type":"subscribed", ...} and then streams {"type":"event", ...} frames in the same shape webhooks receive. {"type":"ping"} gets {"type":"pong"} for keepalive. A per-inbox endpoint exists too, if you want one socket per agent:
Subscriptions are filtered against the key’s scope. Requesting an inbox outside it returns an error frame rather than silently widening the stream.
The socket delivers events while you are connected. It does not replay what you missed while disconnected — for that, read GET /v0/inboxes/{inbox_id}/events on reconnect, or use a webhook, which retries.

Choosing between them

For anything you would be unhappy to silently miss, use a webhook. The retry queue is the difference.