# Get email content

> Text, HTML, headers, raw MIME, and plus-address threads.

Stored inbound mail and the JSON webhook share one payload. This page lists its fields and explains how threads work.

## Payload fields

| Field | What it holds |
| --- | --- |
| `id` | The message id |
| `inbox_id` | The inbox that received it |
| `to`, `from`, `subject` | Addressing and subject |
| `text`, `html` | The plain-text and HTML bodies |
| `in_reply_to` | The `In-Reply-To` header |
| `headers` | `from`, `to`, `cc`, `subject`, `date`, `message-id`, `in-reply-to`, `references`, `auto-submitted`, `precedence`, `x-auto-response-suppress`, `x-autoreply`, `list-id`, and `x-loop`, when present |
| `envelope` | `mail_from`, `rcpt_to`, and `plus_tag` from the SMTP session |
| `authentication` | SPF, DKIM, DMARC, and ARC results |
| `attachments` | File metadata. See [Receiving attachments](/docs/dashboard/receiving/attachments). |
| `expires_at` | When PostShiba deletes the stored message |
| `thread_id` | The thread key. See below. |

API responses also include `drain_status`, and message show adds `raw` with the full MIME while PostShiba keeps it; with retention off, `raw` is `null` once the webhook delivers. See [Retention and storage](/docs/dashboard/receiving/introduction#retention-and-storage). Full field rows are on [Inbound messages](/docs/api-reference/messages).

## Threads and plus-addresses

Plus-addressing routes `agent+thread@` to the inbox `agent@` and sets `thread_id` to `thread`. Give each conversation its own tag to keep replies together.

Without a plus tag, `thread_id` is the first `In-Reply-To` value, then the first `References` value, then the message's own `Message-ID`.

## Prevent mail loops

A loop starts when your app emails someone about inbound mail and that address forwards back to the inbox. Each copy triggers another email.

- Mark every automated email you send with `Auto-Submitted: auto-generated` and an `X-Loop` header naming the inbox.
- On receipt, skip any message whose `headers` carry `auto-submitted` set to anything other than `no`, `precedence` of `bulk`, `list`, `junk`, or `auto_reply`, or your own `x-loop` value.
- Never send a notification about a message you skipped.
- Cap notifications per sender and thread, so a loop the headers miss still stops.

The JSON webhook does not carry the raw MIME, so read these from `headers`. `GET` on the message returns `raw` only until the webhook delivers.

PostShiba also stops loops it can see. Once one sender has sent an inbox 10 messages in 10 minutes, further automated mail from that sender is stored with `drain_status` `held`. Mail counts as automated if it has one of the headers listed here, or if it was sent through PostShiba. A held message does not trigger the webhook or a forward. It still shows in the dashboard and the API until it expires. Mail from people is never held.

## Next steps

- [Managing received emails](/docs/dashboard/receiving/manage-emails)
- [Receiving attachments](/docs/dashboard/receiving/attachments)
- [Build an AI agent inbox on PostShiba](/docs/guides/build-ai-agent-inbox)
- [Inbound messages API](/docs/api-reference/messages)
