Skip to content

WebhooksEvent queues

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_id on message.received and mailbox.rule_matched can be null when you read the event right after it happens.
  • Filters work as they do on webhooks: mailbox_ids, domain_ids, include_reply_text, authenticated_senders_only and allowed_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:read and webhooks:write.
  • Reading and acknowledging need a key whose member can read every mailbox the queue covers, plus messages:read when the queue includes reply text. Otherwise the request returns 403.

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

  1. 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"}'
  2. 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
    }
  3. Handle them, acknowledge them, repeat

    Acknowledge only what you handled, and read again while has_more is true.

    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

POST/v1/event-queuesscope webhooks:writetier write

Takes 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

GET/v1/event-queuesscope webhooks:readtier read

Returns { data, pagination }, newest first. Takes cursor and limit (1-100, default 25), like every other list.

Retrieve an event queue

GET/v1/event-queues/:idscope webhooks:readtier read

Returns the event queue, or 404 when it does not exist in your organization.

Delete an event queue

DELETE/v1/event-queues/:idscope webhooks:writetier write

Returns 204. The events it still holds are deleted with it.

Read events

GET/v1/event-queues/:id/eventsscope webhooks:readtier read

Returns { 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

POST/v1/event-queues/:id/events/ackscope webhooks:writetier write

Send { "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.