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 -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.
See also
Attachments
Every file on a message comes back as metadata you can assert on, and the bytes download from one endpoint — so a PDF invoice test is two calls, not a MIME parser.
Message propertiesThe envelope around the body: who sent it, exactly which address it reached, what the subject line said and when it landed.
TroubleshootingThe message never arrived, no OTP was extracted, 401 with a valid key, 429, domain verification stuck: causes and fixes.