Event queues
An event queue holds the same events a webhook would send, so an agent can fetch them with its API key instead of receiving them.
Last updated
When to use an event queue
- Use an event queue when you have no public HTTPS endpoint: a cron job, a serverless function, or an agent on a laptop.
- Use a webhook when you can receive one. It arrives sooner, since nobody has to poll.
How it works
- Delivery is at least once: an event can come back more than once. Deduplicate on its
event_id. - Reading returns the oldest unacknowledged events and changes nothing. Acknowledging removes them. There is no cursor and no lease, so use one reader per queue. A second consumer needs a second event queue.
- Events not acknowledged within 30 days are deleted.
- A queue only gets events that happen after it was created.
conversation_idonmessage.receivedandmailbox.rule_matchedcan benullwhen you read the event right after it happens.- Filters work as they do on webhooks:
mailbox_ids,domain_ids,include_reply_text,authenticated_senders_onlyandallowed_senders. There is no update: to change a filter, delete the queue and create a new one. - Max 10 event queues per organization. Scopes are
webhooks:readandwebhooks:write. - Reading and acknowledging need a key whose member can read every mailbox the queue covers, plus
messages:readwhen the queue includes reply text. Otherwise the request returns403.
The event queue object
{
"object": "event_queue",
"id": "evq_abc123",
"events": ["message.received", "message.bounced"],
"description": "Inbound for my agent",
"mailbox_ids": null,
"domain_ids": null,
"include_reply_text": false,
"authenticated_senders_only": false,
"allowed_senders": null,
"created_at": "2026-11-04T10:00:00.000Z"
}Read and acknowledge events
Create a queue
curl -X POST https://shipmail.to/api/v1/event-queues \ -H "Authorization: Bearer sm_live_..." \ -H "Content-Type: application/json" \ -d '{"events": ["message.received"], "description": "Inbound for my agent"}'Read the oldest events
Each event is the exact envelope a webhook with the same settings receives.
{ "data": [ { "version": "2026-09-10", "event_id": "evt_abc123", "event_type": "message.received", "created_at": "2026-11-04T10:02:00.000Z", "test": false, "data": { "...": "the same payload a webhook gets" } } ], "has_more": false }Handle them, acknowledge them, repeat
Acknowledge only what you handled, and read again while
has_moreistrue.QUEUE=evq_abc123 while :; do PAGE=$(curl -s "https://shipmail.to/api/v1/event-queues/$QUEUE/events?limit=100" \ -H "Authorization: Bearer sm_live_...") IDS=$(echo "$PAGE" | jq -c '[.data[].event_id]') [ "$IDS" = "[]" ] && break # Handle each event in $PAGE here, skipping event_ids you already handled. curl -s -X POST "https://shipmail.to/api/v1/event-queues/$QUEUE/events/ack" \ -H "Authorization: Bearer sm_live_..." \ -H "Content-Type: application/json" \ -d "{\"event_ids\": $IDS}" [ "$(echo "$PAGE" | jq .has_more)" = "true" ] || break done
Create an event queue
/v1/event-queuesscope webhooks:writetier writeTakes events (required), description, mailbox_ids, domain_ids, include_reply_text, authenticated_senders_only and allowed_senders. Returns 201 with the event queue. Returns 403 quota_exceeded, with limit.resource event_queues, when the organization already has 10.
List event queues
/v1/event-queuesscope webhooks:readtier readReturns { data, pagination }, newest first. Takes cursor and limit (1-100, default 25), like every other list.
Retrieve an event queue
/v1/event-queues/:idscope webhooks:readtier readReturns the event queue, or 404 when it does not exist in your organization.
Delete an event queue
/v1/event-queues/:idscope webhooks:writetier writeReturns 204. The events it still holds are deleted with it.
Read events
/v1/event-queues/:id/eventsscope webhooks:readtier readReturns { data, has_more }: the oldest unacknowledged events, oldest first, for the API key's mode (live or test). limit is 1-100, default 50. Reading changes nothing.
Acknowledge events
/v1/event-queues/:id/events/ackscope webhooks:writetier writeSend { "event_ids": [...] } with 1 to 100 IDs. Deletes those events and returns { "acknowledged": n }, the number deleted. Unknown or already acknowledged IDs are not an error.