Appearance
The email object
This page documents the email object's fields and the lifecycle a message moves through after it's accepted.
Fields
The object returned by GET /v1/emails/{id}.
A send returns only { object, id } — echoing the whole message back would just hand you a copy of what you sent. Fetch the object when you want it.
| Field | Type | Description |
|---|---|---|
object | string | Always "email". |
id | string | Message identifier — a UUID. Stable for the life of the message; used in the dashboard and in webhook events. |
message_id | string | null | The RFC Message-ID handed to the recipient's server — the id that appears in the recipient's headers. null until the message is actually sent. Distinct from id. |
to | string[] | Recipients, always an array. |
from | string | The sender address, as submitted. |
created_at | string | ISO 8601 timestamp of when SendGrail accepted the request. |
subject | string | The subject line. |
html | string | null | The HTML body, or null if the message was sent text-only. |
text | string | null | The plain-text body, or null if the message was sent HTML-only. |
bcc | string[] | Blind-carbon-copy recipients — [] when not supplied. |
cc | string[] | Carbon-copy recipients — [] when not supplied. |
reply_to | string[] | Reply-To addresses — [] when not supplied. |
headers | object | null | The custom headers object, or null when none were supplied. |
last_event | string | The message's current lifecycle state — see below. |
scheduled_at | string | null | ISO 8601 send time for a scheduled message; null for immediate sends. |
tags | array | The { name, value } labels sent with the message; [] when none were supplied. |
suppressed_reason | string | Only present when last_event is suppressed — one of bounce, complaint, manual, unsubscribe. |
attachments | array | Present when the message carried files: filename, content_type and size for each. Never the bytes — download them with List attachments. |
POST /v1/emails/batch returns a trimmed object per item — id and last_event (plus suppressed_reason when relevant).
GET /v1/emails omits html and text: a page of a hundred bodies would be megabytes you didn't ask for.
Lifecycle
last_event is one value at a time. The send response gives you the initial state; it changes as delivery progresses, and you observe later changes through webhooks or the dashboard.
| Value | Stage | Meaning |
|---|---|---|
queued | initial | Accepted and waiting to be handed to AWS SES — usually under a second. |
scheduled | initial | Held until scheduled_at. Becomes queued at the due time. Can still be rescheduled or cancelled. |
cancelled | terminal | A scheduled message you cancelled before it went out. Nothing was sent. |
suppressed | initial · terminal | A recipient was on your suppression list. The message was recorded but never sent. |
sent | after dispatch | Handed to AWS SES for delivery. |
delayed | after dispatch | AWS SES couldn't deliver yet (recipient mailbox full, greylisting, a transient error) and is retrying. Resolves to delivered, or bounce once SES gives up. |
delivered | terminal | The recipient's mail server accepted the message. Also set when an open or click is recorded, since engagement proves delivery. |
bounce | terminal | The message was returned as undeliverable. |
complaint | terminal | The recipient marked the message as spam. |
failed | terminal | SendGrail could not send the message (validation or AWS-side error). |
rejected | terminal | AWS SES dropped the message before delivery (its own suppression list, content scan, size limit). |
Typical successful path:
queued ──▶ sent ──▶ deliveredA scheduled message begins one step earlier:
scheduled ──▶ queued ──▶ sent ──▶ deliveredA slow recipient server passes through delayed before settling:
queued ──▶ sent ──▶ delayed ──▶ deliveredAnd the unhappy endings:
queued ──▶ sent ──▶ bounce
queued ──▶ sent ──▶ complaint
queued ──▶ failedlast_event vs. webhook event names
last_event carries bounce and complaint. The matching webhook events are named email.bounced and email.complained — past tense. Same lifecycle event, two naming conventions.
Observing changes
The token API does not have a "retrieve email" endpoint — once a message is accepted, watch its progress one of two ways: