Everything that happens to an email, a contact, a domain, a broadcast or an automation is one event on the project's timeline. Webhooks deliver the same event object the feed returns, { "type", "created_at", "data" }, with the id and the mode added in the feed.

Where events come from

Source Events
Amazon SES email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.failed (rejected or not rendered), email.suppressed (an address on SES's own suppression list, recorded as a suppression too)
Sending email.suppressed (a recipient on the project's list), email.failed, email.scheduled, email.canceled
Tracking links email.opened, email.clicked (tracking)
Test keys the sequence each test recipient plays (Test mode)
Inbound, audience, broadcasts, automations email.received, contact.*, domain.*, broadcast.*, automation.*

Each source event is recorded once: SES redelivers events now and then, and a redelivered one changes nothing. Events carry the mode of the email (live or test) and its environment; project-level events have no environment. Bounces and complaints also count towards the project's rates (When sending pauses), and the daily counts behind the overview (sent, delivered, bounced, complained, failed, opened, clicked, suppressed) are kept per project, environment, stream and mode. An SES event counts only for mail the platform sent for that project, through the project's own configuration set; anything else is dropped, so mail that sets the platform's tags itself cannot write to another project's timeline.

Email status

An event moves the email's status to the one in the event table, unless the email is already further along. The order is queued, scheduled, sent, delivery_delayed, delivered, opened, clicked, then suppressed, canceled and failed, then bounced, then complained. A later event with a lower rank never demotes the status, so a delivered that arrives after an open leaves the email opened.

The feed

Webhooks push; the feed lets you pull. GET /v1/events returns the key's events, newest first, cursor-paginated, filtered by types (comma-separated), email_id, contact_id and since:

curl
curl "https://api.minket.com/v1/events?types=email.bounced,email.complained&since=2026-10-23T00:00:00Z&limit=100" \
  -H "Authorization: Bearer $MINKET_API_KEY"
JSON
{
  "object": "list",
  "data": [
    {
      "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a90",
      "mode": "live",
      "type": "email.bounced",
      "created_at": "2026-10-24T18:00:04.000Z",
      "data": {
        "email_id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5c",
        "created_at": "2026-10-24T18:00:00.000Z",
        "from": "Acme <billing@mail.acme.example>",
        "to": ["jane@example.com"],
        "subject": "Your receipt for order 10452",
        "bounce": { "type": "Permanent", "subType": "General", "message": "smtp; 550 5.1.1 user unknown" }
      }
    }
  ],
  "has_more": false
}
  • A key sees its own mode only (a test key sees test events), its environment's events and the project-level ones. Full-access keys only.
  • Events from the last 5 seconds are left out. Ids are time-ordered, and an event recorded a moment ago can commit after a later one; the lag means a reader never steps over it.
  • To read older events, pass after with the last id you have. To follow new events, keep the newest id you have seen and ask for before=<that id>: the answer holds the events after it, newest first, with has_more when there are more than limit.
  • GET /v1/events/{id} returns one event, without the lag, so an event id from a webhook can be read at once.

Use the feed to reconcile after your receiver was down, to backfill a new system, or instead of webhooks altogether. Events are kept for 13 months; test events from POST /v1/webhooks/{id}/test are not part of it.

Updated