Skip to content
Esc
  • OverviewGuidesWhat exists today, and where to start.
  • QuickstartGuidesKey, domain, first send — in that order.
  • AuthenticationGuidesBearer keys, the mandatory User-Agent, and what each refusal means.
  • ErrorsGuidesThe whole vocabulary, with the status each name carries.
  • IdempotencyGuidesRetry a send without sending it twice.
  • PaginationGuidesCursors are item IDs, not page numbers.
  • Rate limitsGuidesTen a second per team, and the headers that tell you where you are.
  • EventsGuidesEvery event a webhook can carry, with one real payload each.
  • DomainsGuidesThe records, where they go at each registrar, and what the page does while you wait.
  • TrackingGuidesOpens and clicks: one record, two toggles, and what an open really means.
  • ReceivingGuidesInbound mail, and the Inbox: a webhook fires, you read it, you answer it.
  • InboxGuidesChannels, personal mailboxes and seats: who sees what, and where a reply goes.
  • Node SDKGuidesThe rasket package: typed from the API's own document, retries only what is safe.
  • Python SDKGuidesThe rasket package on PyPI: the Node client's methods, in snake_case, over httpx.
  • MCP serverGuidesLet an assistant act on your account: ten tools, your scopes, no key.
  • AI assistGuidesSubject lines, drafts and diagnosis — in the dashboard and over the API, off until you allow it.
  • AgentsGuidesLet an AI agent set Rasket up: the skill, the rules file, MCP, and the recipe they share.
  • OAuthGuidesLet another app act for a team: register, authorize with PKCE, exchange, refresh.
  • Single sign-onGuidesOIDC login for your team, a domain proved by DNS, enforcement and break-glass.
  • IntegrationsGuidesVercel, Netlify and Cloudflare: a key in the environment, or an OAuth app.
  • SMTPGuidesSend from anything that speaks SMTP: settings, setup guides, limits and replies.
  • EmailsAPI referenceSend, batch, retrieve, list, reschedule, cancel, attachments.
  • DomainsAPI referenceAdd a domain, publish its records, verify it.
  • API keysAPI referenceCreate, list, rename and revoke credentials.
  • WebhooksAPI referencePayloads, signature verification, retries and replay.
  • SuppressionsAPI referenceAddresses we will not send to, and why.
  • LogsAPI referenceEvery request made with this team's credentials.
  • MetricsAPI referenceDelivery, bounce, complaint and engagement counts.
  • TemplatesAPI referenceVersioned email content with typed variables, addressed by ID or alias.
  • ContactsAPI referenceYour audience: contacts, their typed properties, segments and topic choices.
  • SegmentsAPI referenceAudiences defined by a filter, by hand, or both.
  • TopicsAPI referenceWhat contacts subscribe to, and the preference page's list.
  • CampaignsAPI referenceCampaigns, at /broadcasts: one message to a segment, from draft to results.
  • ImportsAPI referenceCSV uploads: column mapping, conflicts and counts.
  • AutomationsAPI referenceWorkflows that run per contact: the graph, its versions, and every run.
  • Custom eventsAPI referenceThe names your product fires, and what starts a workflow.
  • ReceivingAPI referenceMail sent to you: the message, its attachments, its raw source.
  • OAuthAPI referenceClient registration, the token endpoint, and the grants a team has given.
  • TeamAPI referenceThe team a credential belongs to: its plan, sender identity, AI flag and members.
  • BillingAPI referencePlan, usage, invoices and add-ons, and the hosted pages where a customer pays.
  • AI helpersAPI referenceSubject lines, a first draft, and why an email did what it did.

API referenceEmails

Emails

Send one message or a hundred, look up what happened to it, reschedule it while it is still waiting, and read what was attached.

Before you send

  • The domain in from must be verified on this team. An unverified domain is 403 validation_error, not a silent drop.
  • to, cc and bcc together may not exceed 50 addresses.
  • Send html, text, or both. A message with neither is refused.

Two deviations worth knowing

  • scheduled_at takes an ISO 8601 instant or a phrase — "in 2 hours", "tomorrow at 9am", "next tuesday 9am" — between 1 minute and 30 days from now. Phrases are read as UTC, and one that names a day but no time means 09:00. Our grammar is narrower than the reference API's: anything outside it is refused with both accepted forms rather than guessed at.
  • Custom headers cannot override From, To, Cc, Bcc, Subject, Date, Message-ID or Return-Path. Those belong to the envelope we sign; letting a request set them would break DKIM or forge our own identifiers.

Reliability

A 200 from POST /emails means we have durably recorded your message and taken responsibility for sending it. It does not mean the message has reached the mail provider yet, and it certainly does not mean it has been delivered.

Between our call to the mail provider and its answer there is a window where the function can be torn down or the response lost. When that happens we do not know whether the provider holds the message, and we do not send it again: a duplicate email is worse than a visible gap. Instead we look for our own Message-ID in the delivery events that follow. Most such sends resolve within seconds. One that has found no matching event after two hours is marked failed with the reason ambiguous_submission, and both the dashboard and your webhook say so plainly.

So the honest contract is: we will not send your message twice on our own, and if we cannot confirm we sent it at all, we will tell you rather than guess. We do not claim exactly-once delivery, because nobody can.

Finding one again

GET /emails takes six optional filters beside the page parameters — status, api_key_id, start_date, end_date, search and tags — and they are the same ones the dashboard's Sending list uses, so a view you filtered there is a question this endpoint answers identically. search matches the subject, the sender address and the recipient addresses; it does not read the message body.

Counts and rates for the whole account are a different endpoint, on the metrics reference.

Mail sent to you is elsewhere

Everything on this page is mail you sent. Inbound mail is its own resource with its own IDs and its own reads — Receiving — and a received message is never an emails row.

The received figure on GET /emails/metrics is the count of messages the mail provider accepted from you for sending. It is not a count of inbound mail, and it does not move when someone emails you.

Sharing one

POST /emails/{email_id}/share returns a link that opens a read-only view of one sent message, with no sign-in. It is what you paste into a support thread instead of a screenshot.

  • Treat the URL as a secret. Anyone holding it can read the message, and it is the only credential the page asks for.
  • It is returned once. Only a hash of it is stored, so it cannot be retrieved again — create another if you lose it.
  • expires_in is a duration such as 10m or 1 day, defaulting to and capped at 48 hours. An unknown, expired or revoked link all answer the same 404, which says nothing about which of the three it was.

Endpoints

Send an email

POST /emails

Queue one message for delivery and get its ID back.

Headers

  • Idempotency-Keystring

    1–256 characters, unique to this send. Replaying it inside 24 hours returns the original response instead of sending again.

Body

  • fromstring

    Required unless template supplies it. The sender, as an address or Name <address>; its domain must be one this team has verified.

  • tostring | string[]Required

    One recipient or a list. to, cc and bcc together may not exceed 50 addresses.

  • subjectstring

    Required unless template supplies it. Up to 998 bytes of UTF-8, on one line.

  • htmlstring

    The HTML body. Send html, text or both; at least one is required.

  • textstring

    The plain-text body.

9 more fields (cc, bcc, reply_to, scheduled_at, headers, attachments, tags, template, topic_id)
  • ccstring | string[]

    Visible copies. Counts toward the recipient cap.

  • bccstring | string[]

    Blind copies. Counts toward the recipient cap.

  • reply_tostring | string[]

    Where replies go. Not counted toward the recipient cap.

  • scheduled_atstring

    An ISO 8601 instant, or a phrase such as "in 2 hours", "tomorrow at 9am" or "next tuesday 9am" — phrases are read as UTC, and a day with no time means 09:00. Between 1 minute and 30 days from now.

  • headersobject

    Custom message headers, name to value. Headers we own — From, To, Cc, Bcc, Subject, Date, Message-ID, Return-Path, the MIME and DKIM headers, trace and authentication headers such as Received, Sender, Authentication-Results, ARC-* and Resent-*, delivery-control headers, and any List-Unsubscribe* — are refused. Values may not contain control characters other than a tab.

  • attachmentsobject[]

    Up to 100 items, each with a filename and exactly one of content (base64) or path (an https URL we fetch). Optional content_type (a MIME type such as image/png) and content_id (printable ASCII, no spaces or angle brackets).

  • tagsobject[]

    Up to 50 { name, value } pairs of A-Z, a-z, 0-9, _ and -. A name is 1–252 characters and a value 1–256; a name may appear only once. Tags come back on the email and on every event for it, as an object.

  • templateobject

    { id, variables }. id is a template ID or alias; the template must be published, and its published version supplies html, text and, where the request omits them, subject, from and reply_to. A send where neither sets from or subject is 422 missing_required_field. Cannot be combined with html or text. variables maps each declared key to a string or number of at most 2000 characters; a missing key takes its fallback.

  • topic_idstring

    Reserved for subscription topics in a later phase.

Request

curl -X POST "https://api.rasket.com/emails" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{
  "from": "Acme <orders@send.acme.example>",
  "to": ["ronald.williams@example.com"],
  "subject": "Your order has shipped",
  "html": "<p>Order 1042 left the warehouse this morning.</p>"
}'

Response 200

{
  "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"
}
  • A 200 means we have accepted and durably recorded the message, not that it has been delivered. Delivery is reported by events.
  • Bodies are capped at 2 MB of html and text combined.
  • Without an Idempotency-Key, a retried request sends a second email.

Send a batch

POST /emails/batch

Up to 100 messages in one request, validated all or nothing.

Headers

  • Idempotency-Keystring

    1–256 characters, unique to this send. Replaying it inside 24 hours returns the original response instead of sending again.

Body

  • []object[]Required

    The body is a JSON array of 100 or fewer send objects, each shaped exactly like POST /emails.

Request

curl -X POST "https://api.rasket.com/emails/batch" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nightly-digest-2026-09-09" \
  -d '[
  {
    "from": "Acme <orders@send.acme.example>",
    "to": ["ronald.williams@example.com"],
    "subject": "Your order has shipped",
    "html": "<p>Order 1042 left the warehouse this morning.</p>"
  },
  {
    "from": "Acme <orders@send.acme.example>",
    "to": ["ada@example.com"],
    "subject": "Your order has shipped",
    "html": "<p>Order 1043 left the warehouse this morning.</p>"
  }
]'

Response 200

{
  "data": [
    {
      "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"
    },
    {
      "id": "9c1d3f28-6b04-4f77-a5e2-1c8d05b3e9a7"
    }
  ]
}
  • One invalid element refuses the whole batch and nothing is sent; the error names the element, as in emails[3].to.
  • One Idempotency-Key covers the whole batch, not each message in it.
  • IDs come back in the order they were sent.

List emails

GET /emails

Newest first, cursor paginated.

Query parameters

  • limitinteger

    How many items to return, 1–100. Defaults to 20.

  • afterstring

    Return the page that follows this item ID. Mutually exclusive with before.

  • beforestring

    Return the page that precedes this item ID. Mutually exclusive with after.

  • statusstring

    The last event the email reached: queued, scheduled, sent, delivery_delayed, delivered, opened, clicked, bounced, complained, failed, suppressed or canceled.

  • api_key_idstring

    Only emails sent with this API key. A revoked key still filters the emails it sent.

  • start_datestring

    ISO 8601, inclusive. Emails created before this instant are excluded.

  • end_datestring

    ISO 8601, inclusive. Emails created after this instant are excluded.

  • searchstring

    Case-insensitive substring of the subject, the sender address or any recipient address. 1–200 characters.

  • tagsstring

    Comma-separated, each written name:value. Up to 10 of them, and an email has to carry every one. Repeating the parameter does the same.

Request

curl -X GET "https://api.rasket.com/emails?limit=20&search=invoice" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": true,
  "data": [
    {
      "object": "email",
      "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
      "from": "Acme <orders@send.acme.example>",
      "to": ["ronald.williams@example.com"],
      "cc": [],
      "bcc": [],
      "reply_to": [],
      "subject": "Your order has shipped",
      "html": "<p>Order 1042 left the warehouse this morning.</p>",
      "text": null,
      "message_id": "<01000199a3c4d5e6-7f8a9b0c@send.acme.example>",
      "created_at": "2026-09-09T10:14:02.118Z",
      "last_event": "delivered",
      "scheduled_at": null,
      "tags": {
        "invoice": "1042"
      }
    }
  ]
}
  • Every filter is optional and additive. Sending none returns the same page it always did.
  • Filters are applied to the query, not to the page, so has_more describes the filtered list and paging through it never skips a row.
  • search looks at the subject, the sender address and the recipient addresses — not the message body. % and _ match literally.
  • tags is written name:value and repeats. An email has to carry every tag you name, and a send that carried none is never a match.

Retrieve an email

GET /emails/{email_id}

The stored message and the last event it reached.

Path parameters

  • email_idstringRequired

    The email's ID.

Request

curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "email",
  "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
  "from": "Acme <orders@send.acme.example>",
  "to": ["ronald.williams@example.com"],
  "cc": [],
  "bcc": [],
  "reply_to": [],
  "subject": "Your order has shipped",
  "html": "<p>Order 1042 left the warehouse this morning.</p>",
  "text": null,
  "message_id": "<01000199a3c4d5e6-7f8a9b0c@send.acme.example>",
  "created_at": "2026-09-09T10:14:02.118Z",
  "last_event": "delivered",
  "scheduled_at": null,
  "tags": {
    "invoice": "1042"
  }
}
  • last_event is the furthest state this email has reached, not a history. The full timeline is on the dashboard and on your webhook.

Reschedule an email

PATCH /emails/{email_id}

Move a scheduled send to a new time.

Path parameters

  • email_idstringRequired

    The email's ID.

Body

  • scheduled_atstringRequired

    An ISO 8601 instant, or a phrase such as "in 2 hours", "tomorrow at 9am" or "next tuesday 9am" — phrases are read as UTC, and a day with no time means 09:00. Between 1 minute and 30 days from now.

Request

curl -X PATCH "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "scheduled_at": "2026-09-10T12:00:00.000Z"
}'

Response 200

{
  "object": "email",
  "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"
}
  • scheduled_at is the only field this endpoint changes.
  • It works only while last_event is scheduled. Once the message has been dispatched the answer is 409 resource_locked.

Cancel a scheduled email

POST /emails/{email_id}/cancel

Stop a send that has not gone out yet.

Path parameters

  • email_idstringRequired

    The email's ID.

Request

curl -X POST "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/cancel" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "email",
  "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"
}
  • Cancelling an email that has already been dispatched answers 409 resource_locked; there is no recall.

List attachments

GET /emails/{email_id}/attachments

What was attached to a sent email, with a signed link for each.

Path parameters

  • email_idstringRequired

    The email's ID.

Query parameters

  • limitinteger

    How many items to return, 1–100. Defaults to 20.

  • afterstring

    Return the page that follows this item ID. Mutually exclusive with before.

  • beforestring

    Return the page that precedes this item ID. Mutually exclusive with after.

Request

curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/attachments" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "5f2c9a1b-7e3d-4c8a-9b1f-2e6d0a4c8b73",
      "filename": "invoice-1042.pdf",
      "content_type": "application/pdf",
      "content_disposition": "attachment",
      "size": 48213,
      "download_url": "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/attachments/5f2c9a1b-7e3d-4c8a-9b1f-2e6d0a4c8b73/download?expires=1789200000&token=1f0c…",
      "expires_at": "2026-09-09T10:29:02.118Z"
    }
  ]
}
  • Cursors on this list are attachment IDs.

Retrieve an attachment

GET /emails/{email_id}/attachments/{attachment_id}

One attachment's metadata and a fresh signed link.

Path parameters

  • email_idstringRequired

    The email's ID.

  • attachment_idstringRequired

    The attachment's ID.

Request

curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/attachments/5f2c9a1b-7e3d-4c8a-9b1f-2e6d0a4c8b73" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "attachment",
  "id": "5f2c9a1b-7e3d-4c8a-9b1f-2e6d0a4c8b73",
  "filename": "invoice-1042.pdf",
  "content_type": "application/pdf",
  "content_disposition": "attachment",
  "size": 48213,
  "download_url": "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/attachments/5f2c9a1b-7e3d-4c8a-9b1f-2e6d0a4c8b73/download?expires=1789200000&token=1f0c…",
  "expires_at": "2026-09-09T10:29:02.118Z"
}

Download an attachment

GET /emails/{email_id}/attachments/{attachment_id}/download

Follow the signed link and get the bytes.

Path parameters

  • email_idstringRequired

    The email's ID.

  • attachment_idstringRequired

    The attachment's ID.

Query parameters

  • expiresintegerRequired

    Part of the signature. Copy the whole download_url; do not build this yourself.

  • tokenstringRequired

    The link's signature, valid for fifteen minutes and for this attachment only.

Request

curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/attachments/5f2c9a1b-7e3d-4c8a-9b1f-2e6d0a4c8b73/download?expires=1789200000&token=1f0c9d3b8a72e5461c0d" \
  -H "User-Agent: acme-billing/1.0"
  • This is the only route in the public API that takes no Authorization header: the link carries its own signed authorization so a browser can follow it.
  • A User-Agent is still required, as it is on every other route.
  • The response is the file itself, not JSON.

Share an email

POST /emails/{email_id}/share

Create a link that opens a read-only view of one sent email, with no sign-in required.

Path parameters

  • email_idstringRequired

    The ID of the email.

Body

  • expires_instring

    How long the link stays valid, as <number><unit> with optional whitespace — 10m, 2 hours, 1 day. Units: s/sec/secs/second/seconds, m/min/mins/minute/minutes, h/hr/hrs/hour/hours, d/day/days. Defaults to 48h and cannot exceed 48 hours.

Request

curl -X POST "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/share" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "expires_in": "24h"
}'

Response 200

{
  "object": "email",
  "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
  "url": "https://share.example.com/nQ8vK2xW5yB7dF1hJ4mP6rT9uA3cE0gL2iO",
  "expires_at": "2026-09-11T12:00:00.000Z"
}
  • Treat the URL as a secret: anyone holding it can read the message, and it is the only credential the page asks for.
  • The link is returned once and is not stored — only a hash of it is kept, so it cannot be retrieved again. Create another if you lose it.
  • id is the ID of the email, not of the share, so it is the same value you would pass to GET /emails/{email_id}.
  • expires_at is additive: the reference spec documents only object, id and url.
  • The page is served on a separate origin from the dashboard and renders the message in a sandboxed frame with remote images blocked until the reader loads them.
  • An unknown, expired, or revoked link answers the same 404 page, with nothing said about which of the three it was.

List shared links

GET /emails/{email_id}/shares

Every link ever created for one email, live and withdrawn alike, newest first.

Path parameters

  • email_idstringRequired

    The ID of the email.

Query parameters

  • limitinteger

    How many items to return, 1–100. Defaults to 20.

  • afterstring

    Return the page that follows this item ID. Mutually exclusive with before.

  • beforestring

    Return the page that precedes this item ID. Mutually exclusive with after.

Request

curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/shares" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "0198f4c1-0000-7000-8000-000000000000",
      "email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
      "status": "active",
      "created_at": "2026-09-09T12:00:00.000Z",
      "expires_at": "2026-09-11T12:00:00.000Z",
      "revoked_at": null,
      "created_by_user_id": null,
      "created_by_api_key_id": "a4d2f0c8-5b31-4e7a-9c62-8f0b1d4e6a75"
    }
  ]
}
  • **There is no url here, and no endpoint can give one back.** Only a hash of each token is stored, so a link that has been lost is revoked and replaced, never re-shown.
  • status is active while the link still opens the page, expired once expires_at has passed, and revoked once it was withdrawn. A link that was withdrawn and has also expired reads revoked.
  • Exactly one of created_by_user_id and created_by_api_key_id is set, recording which credential created the link. Both are null once that member or key is gone; neither restricts who may revoke it.
  • Cursors are share IDs. A cursor naming a share of a different email is 422 invalid_parameter, not an empty page.
  • Revoked links stay in this list, and are deleted only when the email itself is.

Revoke a shared link

DELETE /emails/{email_id}/shares/{share_id}

Stop a link working, without deleting the record that it existed.

Path parameters

  • email_idstringRequired

    The ID of the email.

  • share_idstringRequired

    The ID of the share, as GET /emails/{email_id}/shares reports it.

Request

curl -X DELETE "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/shares/{share_id}" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "email_share",
  "id": "0198f4c1-0000-7000-8000-000000000000",
  "revoked_at": "2026-09-10T09:00:00.000Z"
}
  • The link stops working on the next request. There is no cache to wait for.
  • Idempotent: revoking a link that is already revoked answers the instant it *first* stopped working, not the instant of this call.
  • The response says revoked_at rather than the deleted: true other deletes answer with, because the row is kept — a link that was shared and withdrawn is a fact about this email.
  • A share belonging to a different email, or to another team, is 404 — the same answer as one that never existed.
  • Any full_access key or dashboard session may revoke any of this team's links, whichever credential created it: a customer whose key has leaked needs the dashboard to be able to shut the link off.

List an email's events

GET /emails/{email_id}/events

Everything recorded for one email — sent, delivered, bounced, opened — oldest first.

Path parameters

  • email_idstringRequired

    The email's ID.

Request

curl -X GET "https://api.rasket.com/emails/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/events" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "email_event",
      "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5f01",
      "type": "email.delivered",
      "recipient": "ronald.williams@example.com",
      "occurred_at": "2026-09-09T09:20:33.412Z",
      "data": {}
    }
  ]
}
  • Ordered by when each event happened, not when it arrived. recipient is null for an event about the whole message.
  • data is the event-specific object in the shape a webhook carries it. To ask what went wrong, see Diagnose an email on the AI reference page.