Events API
Browse delivery, open, click, bounce, and complaint events captured for the team's sent mail.
Every request needs a bearer token and goes to a path under https://api.anypost.com/v1. See API conventions for the shared request, error, and pagination rules.
List events
GET /v1/events
Page through delivery, open, click, bounce, and complaint events for the authenticated team, newest-first. Operational events (admin bounces, queue rebinds, …) are excluded.
Time window
The start / end query params accept ISO 8601 timestamps.
The default window is the last 24 hours. The window is
clamped to your plan's event retention — 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 works as customers upgrade.
Filters
All filters are exact-match on the indexed columns. A
recipient or email_id that doesn't match any stored row
returns an empty list; we do not support substring search
because the events table holds billions of rows and
wildcard scans would be prohibitively expensive.
Pagination
Cursor pagination on (occurred_at, event_id). Echo the
response's next_cursor back as after to fetch the next
page. Cursors are opaque — do not parse them.
Parameters
limitintegerin query[optional]Number of items to return.
afterstringin query[optional]Opaque cursor from a previous response's
next_cursor. Do not parse.startstring (date-time)in query[optional]ISO 8601 timestamp marking the start of the window (inclusive). Defaults to 24 hours before
end. Will be moved forward if it falls outside your plan's retention window.endstring (date-time)in query[optional]ISO 8601 timestamp marking the end of the window (exclusive). Defaults to now. Future timestamps are clamped to now.
event_typestringin query[optional]Restrict to events of this customer-facing type.
One of:
email.sent,email.delivered,email.delayed,email.bounced,email.complained,email.suppressed,email.unsubscribed,email.opened,email.clicked.recipientstringin query[optional]Restrict to events delivered to this exact recipient address (lowercased server-side).
email_idstringin query[optional]Restrict to events for the email message with this id. The canonical shape is
email_<uuidv7>in hyphenated form (e.g.email_019e1972-e87e-7000-bf74-ba09e0ed0d62) — minted identically by SMTP and HTTP submissions. A malformed value returns an empty list without hitting the data store.message_idstringin query[optional]Restrict to events whose
Message-ID:header matches this value exactly.domainstringin query[optional]Restrict to events sent from this domain. Pass the domain hostname (not the internal
domain_<uuid>public id). Unknown domains return400.topicstringin query[optional]Restrict to events whose originating send was tagged with this topic. Exact match against the lowercase
[a-z0-9_.-]{1,64}shape enforced at send time. Values that don't match the shape are dropped from the filter set server-side (no 400) — the call returns the unfiltered slice rather than an error a customer can't act on.campaignstringin query[optional]Restrict to events whose originating send set this
campaignvalue. Exact match, case-sensitive — campaign values are stored verbatim. A value that doesn't match any stored row returns an empty list.template_idstringin query[optional]Restrict to events whose originating send used this stored template. Exact match against the
template_idpassed toPOST /email. A value that doesn't match any stored row returns an empty list.ip_poolstringin query[optional]Restrict to events for mail that egressed from this named IP pool — the
ip_poolpassed toPOST /email. Only meaningful on accounts with dedicated IPs; on any other account every event has a nullip_pooland this filter returns an empty list.Exact match against the lowercase
[a-z0-9]([a-z0-9-]*[a-z0-9])?pool-name shape. A value outside that shape returns an empty list rather than being dropped from the filter set — unliketopic, a mistyped pool name must not silently widen to "all pools", which would answer "which of my IPs sent this?" with mail from every one of them.Retiring a pool does not make its history unqueryable: events keep the
ip_poolthey were sent with, so this filter still returns them after the pool is gone.tagsarray of stringin query[optional]Restrict to events whose originating send carried any of these tags (
hasAny— an event matches if it has at least one of the listed values, not all of them). Pass a comma-separated list (tags=onboarding,welcome) or repeat the parameter (tags[]=onboarding&tags[]=welcome). Up to 10 tags; values outside[A-Za-z0-9_-]{1,64}are dropped from the filter rather than erroring.
Example request
curl https://api.anypost.com/v1/events \
-H "Authorization: Bearer $ANYPOST_API_KEY"Response body
On success (200), the response body is:
dataarray of Eventhas_morebooleannext_cursorstringMay be null.
{
"data": [
{
"id": "evt_8f2c1b3e6a5d4f7c9a3e1d2b4c5e6f7a",
"type": "email.sent",
"occurred_at": "string",
"email_id": "email_019e1972-e87e-7000-bf74-ba09e0ed0d62",
"message_id": "string",
"from": "string",
"from_domain": "string",
"recipient": "string",
"subject": "string",
"campaign": "string",
"ip_pool": "marketing",
"template_id": "template_019e2d74-df2a-72ed-a105-7ce7500ef2bc",
"topic": "marketing",
"tags": [
"onboarding",
"welcome"
],
"smtp_code": 0,
"bounce_type": "permanent",
"bounce_classification": "string",
"attempt": 0,
"tracking": {
"bot": {
"source": "google",
"kind": "proxy"
}
}
}
],
"has_more": true,
"next_cursor": "string"
}Responses
| Status | Description |
|---|---|
200 | Paginated list of events. |
400 | Request validation failed. |
401 | Missing or invalid credentials. |