# description
The JSON body MailKite POSTs to your webhook when mail arrives at an address on a verified domain. The same body is re-sent verbatim on an automatic retry or a manual replay, so every field describes the original message — never the moment of the POST. Verify the x-mailkite-signature header over the raw body before trusting it.

# properties.id.description
Stable message id (msg_…). Identical across retries — use it as your idempotency key.

# properties.type.description
The event type. Always email.received for inbound mail.

# properties.from.properties.address.description
Sender email address, taken from the SMTP envelope (MAIL FROM) — the address we actually routed on.

# properties.from.properties.name.description
Sender display name, decoded from the MIME From: header. OMITTED (not null) when the message carried no display name, or when the From: header names a different address than the envelope did — as happens with mailing lists, forwarders, and spoofed mail. It is sender-asserted and unverified, like the Date: header: never use it for authorization, and check the auth block before showing it as an identity.

# properties.to.description
The recipient address this delivery is for. One entry per event: a message addressed to several of your addresses is routed and delivered once per matching address.

# properties.to.items.properties.address.description
Recipient email address, taken from the SMTP envelope (RCPT TO).

# properties.to.items.properties.name.description
Recipient display name, decoded from the MIME To: header. Omitted (not null) when absent, or when the To: header does not name this exact address (e.g. mail delivered via Bcc or an alias).

# properties.subject.description
Decoded subject line. null when the message had no Subject header.

# properties.text.description
Plain-text body, already MIME-decoded. null when the message had no text/plain part.

# properties.html.description
HTML body, already MIME-decoded. null when the message had no text/html part.

# properties.threadId.description
The conversation root: the message's In-Reply-To/References root, falling back to its own Message-ID. Pass it back as send()'s inReplyTo to reply in-thread. null when the message carried no usable id.

# properties.receivedAt.description
When MailKite accepted the message, in Unix epoch milliseconds (UTC). This is arrival time, not delivery time — it is read from the stored message, so a retry hours later or a replay months later reports the same instant. Distinct from the sender-supplied Date: header, which is self-asserted and spoofable.

# properties.receivedAtIso.description
The same instant as receivedAt, rendered as an RFC 3339 / ISO 8601 UTC timestamp.

# properties.auth.description
Authentication and spam verdicts computed at our receiving edge, where the connecting IP and SMTP envelope are still visible. Any field is null when that check did not run or produced no verdict — treat null as 'unknown', never as 'pass'. Values are passed through from the edge rather than re-mapped, so match the ones you care about and treat anything else as unknown.

# properties.auth.properties.spf.description
SPF result for the sending IP. Typically pass, fail, softfail, or neutral.

# properties.auth.properties.dkim.description
DKIM signature result. Typically pass or fail.

# properties.auth.properties.dmarc.description
DMARC alignment result, derived from SPF and DKIM. Typically pass or fail.

# properties.auth.properties.spam.description
Spam verdict. Typically ham or spam; edges running rspamd may pass through its action name instead.

# properties.attachments.description
Decoded attachments. Empty when the message had none. Each entry carries EXACTLY ONE of `url` (the normal case: a signed, credential-free GET link valid for 7 days) or `content` (base64 bytes inlined, used on zero-retention and at-rest-encrypted domains where no object is stored). Handle both.

# properties.attachments.items.properties.id.description
Attachment id, `<messageId>:<index>`. Absent when the bytes are inlined (nothing was stored to reference).

# properties.attachments.items.properties.filename.description
Filename as sent, decoded. null when the part had no name.

# properties.attachments.items.properties.contentType.description
MIME type as declared by the sender. null when absent.

# properties.attachments.items.properties.url.description
Signed, time-limited GET link — fetch it with no credentials. Valid for 7 days, after which the object is deleted (410 Gone). Present unless `content` is.

# properties.attachments.items.properties.content.description
The file bytes, base64-encoded, inlined because nothing was stored. Present only on zero-retention or at-rest-encrypted domains, in place of `url`.