Docs/Webhooks & events

Events

The event stream is every delivery and engagement event for your sent mail, queryable on demand. Where a webhook pushes each event to you as it happens, the events API lets you pull a window of history back whenever you need it.

Events vs webhooks

Both surfaces carry the same event types. They differ in how you consume them.

  • Webhooks push. Anypost POSTs each event to your endpoint as it happens. Best for reacting in real time. See Webhooks.
  • Events pull. You query the stored stream when you want it. Best for backfilling a gap, reconciling your own records, or one-off lookups.

The two compose. When a webhook endpoint is down long enough to be disabled, events that occur while it is disabled are not queued. Page the events API once the endpoint is healthy to recover that gap.

Event types

Only customer-facing events are returned. Operational events are excluded.

TypeEmitted when
email.sentAnypost accepted the message and handed it to the outbound mail system.
email.deliveredThe receiving server accepted the message.
email.delayedA delivery attempt failed temporarily and will be retried.
email.bouncedDelivery ended without reaching the recipient: the receiving server refused the address, or the message ran out its retry window.
email.complainedThe recipient marked the message as spam.
email.suppressedThe send was dropped because the address is on your suppression list.
email.unsubscribedThe recipient used a one-click unsubscribe link.
email.openedThe recipient opened the message.
email.clickedThe recipient clicked a tracked link.

Listing events

GET /v1/events returns events newest-first, with cursor pagination. The TypeScript list returns a page you can iterate to walk every page:

curl https://api.anypost.com/v1/events \
  -H "Authorization: Bearer $ANYPOST_API_KEY"

Events are read-only and not addressable by id — there is no GET /v1/events/{id}.

Time window

The start and end parameters take ISO 8601 timestamps. The default window is the last 24 hours. The window is clamped to your plan's event retention — 3, 7, or 30 days. A request for 30 days of history on a 3-day-retention plan silently narrows to the last 3 days rather than erroring, so the same call keeps working as you upgrade.

Filters

All filters are exact-match, except tags. A value that matches no stored row returns an empty page; there is no substring search.

ParameterRestricts to
event_typeOne of the nine event types above.
recipientOne exact recipient address, lowercased server-side.
email_idOne message's email_<uuidv7> id.
message_idAn exact Message-ID: header match.
domainA sending domain hostname (not the domain_<uuid> id). An unknown domain returns 400.
topicThe send-time topic the message was tagged with.
campaignThe send-time campaign value. Case-sensitive.
template_idThe template the originating send used.
ip_poolMail that egressed from one of your named IP pools. Dedicated IPs only; returns an empty page otherwise.
tagsEvents carrying any of the given tags. Pass comma-separated (tags=a,b); up to 10.

Pass several at once to narrow further. To trace one message end to end, filter by its email_id:

curl -G https://api.anypost.com/v1/events \
  -H "Authorization: Bearer $ANYPOST_API_KEY" \
  --data-urlencode "email_id=email_019e1972-e87e-7000-bf74-ba09e0ed0d62" \
  --data-urlencode "limit=100"

The event object

Every event carries the same fields. The ones that do not apply to a given type are null rather than omitted: smtp_code is null on an open, bounce_type and bounce_classification are set only on email.bounced, attempt is null on non-delivery events, and tracking is null except on opens and clicks. The Events API reference lists every field.

bounce_type separates the three ways a send can end in a bounce. permanent is a 5xx refusal from the receiving server, and is the only one that suppresses the address. transient is a temporary failure reported on a final delivery report. expired means no verdict ever arrived: the message retried for 72 hours and was dropped from the queue, which points at the receiving side being down or throttling rather than at a bad address. Filter on bounce_type = "permanent" when you are measuring list quality.

The correlation dimensions — tags, topic, campaign, and template_id — travel onto every event for a message, engagement events included, so you can measure a campaign or template at open and click time, not just at send.

ip_pool is not one of those dimensions. It records which of your dedicated IP pools carried the message rather than labeling it, and it travels onto every event for the same reason: a bounce or complaint rate per pool is the number that tells you whether one stream's reputation is slipping. It is null on accounts sending from the shared pool, and on any send that named no pool.

On an open or click, a non-null tracking object carries a bot block when the interaction came from a mailbox image proxy rather than the recipient's own client. Machine traffic that is pure noise — prefetchers and scanners — never becomes an event at all, so it cannot inflate a count here, in the dashboard, or on your webhooks. See Open & click tracking for the full model.

Where to go next