MailFlatDocs
Documentation/Common test cases/Raw headers

Reading raw email headers

Every header the sender set comes back as a dictionary, so you can assert on Message-ID, follow the Received chain, or prove which relay signed the mail.

How it works

When mail does not behave, the answer is almost never in the body. The headers object holds every header as it arrived, and headers that legitimately repeat come back as a list rather than collapsing to the last one, because the Received chain is the delivery path and only the whole chain tells you where a message actually went.

Read a header

Same access pattern as attachments: untyped in the SDKs today, so read it off raw.
msg = inbox.wait_for_message(timeout=30)
assert msg.raw["headers"]["Message-ID"].endswith("@shop.example>")

Follow the delivery path

Received repeats once per hop, newest first, so the list is the route the message took to reach us.
hops = msg.raw["headers"]["Received"]
assert len(hops) >= 2, "message did not pass through the relay"

Encrypted inboxes report no headers

Headers carry the Subject, so leaving them in the clear would hand back the very thing encryption hides. On an encrypted inbox they travel inside the envelope instead.
curl
curl -s "$MAILFLAT_API/inboxes/$ADDRESS/latest" \
-H "X-API-Key: $MAILFLAT_API_KEY" | jq '.email | {is_encrypted, headers}'
# { "is_encrypted": true, "headers": null }
#
# Decrypt the envelope in your client and the headers are in there.

Worth knowing

Headers are capped, and they say so
A message can arrive with thousands of Received lines. We keep the first 16 KB of headers and, when there is more, add x-mailflat-headers-truncated so you never have to wonder whether a missing header was absent or dropped.