> ## Documentation Index
> Fetch the complete documentation index at: https://docs.talario.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# Parsed content

> Read subject, text, HTML, headers and attachments of a message as JSON, without a MIME library.

Talario parses every received message. Your application reads the result with one API call instead of parsing MIME itself:

```bash theme={null}
curl https://talario.eu/api/v1/receiving/messages/019a0520-0000-7000-8000-000000000002/content \
  -H "Authorization: Bearer $TALARIO_API_KEY"
```

This works for every message received since parsing was introduced, whatever the payload version of your webhook endpoint and also for routes with **Store** only. The field names follow the familiar names of Mailgun's inbound routes (`sender`, `from`, `body-plain`, `message-headers` and so on). It is not a drop-in replacement for Mailgun: authentication, signatures, links and retries are Talario's own.

The API key needs the permission **Read received messages**. The endpoint has its own [rate limit](/authentication#rate-limits) of 60 requests per minute.

## Recipient

The content is shown for one envelope recipient. If the message has exactly one recipient, you can omit it. Otherwise name the recipient, URL-encoded:

```bash theme={null}
curl --get https://talario.eu/api/v1/receiving/messages/019a0520-0000-7000-8000-000000000002/content \
  --data-urlencode "recipient=support@inbound.example.com" \
  -H "Authorization: Bearer $TALARIO_API_KEY"
```

Without `recipient`, a message with several recipients returns `422 invalid_payload`. An address that is not a recipient of the message returns `404 not_found`. The comparison ignores ASCII case.

## Response

```json theme={null}
{
  "schema_version": 2,
  "id": "019a0520-0000-7000-8000-000000000002",
  "domain": "inbound.example.com",
  "received_at": "2026-10-21T16:00:00Z",
  "expires_at": "2026-10-28T16:00:00Z",
  "sender": "bounce@example.org",
  "from": "Erika Example <erika@example.org>",
  "recipient": "support@inbound.example.com",
  "to": "Support <support@inbound.example.com>",
  "cc": "",
  "reply-to": "erika@example.org",
  "subject": "Question about my booking",
  "message-id": "<external-id@example.org>",
  "in-reply-to": "",
  "references": [],
  "body-plain": "Hello, please find my booking attached.",
  "body-html": "<p>Hello, please find my booking attached.</p>",
  "message-headers": [
    ["From", "Erika Example <erika@example.org>"],
    ["Subject", "Question about my booking"]
  ],
  "attachment-count": 1,
  "attachments": [
    {
      "id": "part-2",
      "name": "booking.pdf",
      "content-type": "application/pdf",
      "size": 12345,
      "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "disposition": "attachment",
      "content-id": null,
      "url": "https://…",
      "expires_at": "2026-10-21T17:00:00Z"
    }
  ],
  "content-id-map": {},
  "parse_status": "parsed",
  "parse_warnings": [],
  "content_omitted": false,
  "download": {
    "url": "https://…",
    "expires_at": "2026-10-21T17:00:00Z",
    "size_bytes": 14557,
    "sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
  }
}
```

The response is generated for each request with fresh [download links](/receiving/downloads) and is sent with `Cache-Control: no-store`. Talario does not keep a copy of the content outside the message store.

## Fields

**schema\_version** `integer`

Version of this format. Currently `2`; it is the same format as [webhook payload version 2](/webhooks/payload-v2), without `event` and `delivery_id`.

**id** `string`

Talario's ID of the message (UUIDv7).

**sender** `string`

The SMTP envelope sender. Empty for a null sender, for example in bounces.

**from** `string`

The decoded `From` header as shown to the reader.

**recipient** `string`

The envelope recipient this content is shown for.

**to, cc, reply-to, subject, message-id, in-reply-to** `string`

Decoded headers of the message. A missing header is an empty string. These are headers written by the sender, not the envelope.

**references** `string[]`

Message IDs from the `References` header, in order. Empty if the header is missing.

**body-plain** `string`

The text part of the message, decoded to UTF-8. Empty if the message has no text part; Talario does not convert HTML to text.

**body-html** `string`

The HTML part of the message, decoded to UTF-8. Empty if the message has no HTML part. **The HTML is not sanitized**, see below.

**message-headers** `array`

All headers as `[name, value]` pairs, unfolded and decoded, in their original order and including repeated headers. The exact bytes are in the original message.

**attachment-count** `integer`

Number of entries in `attachments`.

**attachments** `object[]`

Attachments and inline parts.

**properties**

**id** `string`

Stable ID derived from the MIME position, for example `part-2` or `part-1-3`. Not derived from the file name.

**name** `string`

Decoded file name without a path. A safe replacement name if the part has none.

**content-type** `string`

MIME type as declared by the sender. Do not trust it; check the file yourself.

**size** `integer`

Size of the decoded file in bytes.

**sha256** `string`

SHA-256 of the decoded file, as hex.

**disposition** `string`

`attachment` or `inline`.

**content-id** `string | null`

Content ID without angle brackets, for inline images referenced as `cid:` in the HTML.

**url** `string`

Download link for the decoded file, valid until `expires_at`.

**expires\_at** `string`

Expiry of `url`.

**content-id-map** `object`

Maps a content ID to an attachment ID, for replacing `cid:` references in the HTML. If several parts share a content ID, the first one is used.

**parse\_status** `string`

`parsed`, or `failed` if the message could not be parsed. See below.

**parse\_warnings** `string[]`

Warning and error codes of the parser.

**content\_omitted** `boolean`

Always `false` here. Only a webhook can omit content.

**download** `object`

Link to the unchanged original message (`.eml`) with its `size_bytes` and `sha256`.

Unknown fields may be added later; ignore them.

## How Talario chooses the body

* Transfer encodings, encoded headers (RFC 2047) and file names (RFC 2231) are decoded, text is converted to UTF-8.
* In `multipart/alternative`, one part per format is used, preferring the last matching alternative.
* In `multipart/mixed`, matching parts outside attachments are joined in MIME order with a line break.
* Parts with an `attachment` disposition or a file name are attachments, never body.
* Forwarded messages (`message/rfc822`) are attachments; their text is not mixed into the body.
* Inline images with a content ID are attachments that you can download.

Talario does not remove quoted replies or signatures.

> [!WARNING]
>
> `body-html` is untrusted input from the sender and is **not sanitized**. Sanitize it before you display it, for example with [symfony/html-sanitizer](https://symfony.com/doc/current/html_sanitizer.html), and block external resources. Talario does not load external resources and does not follow links in messages.

## Parse status and warnings

If the message can be parsed, `parse_status` is `parsed`. Minor problems are reported as warnings:

| Warning | Meaning |
| - | - |
| `unknown_charset` | A part declared an unknown character set. |
| `decoding_replaced` | Invalid characters were replaced during decoding. |
| `duplicate_content_id` | Several parts share a content ID. |

If the message cannot be parsed or exceeds a [parser limit](/receiving/limits#parser-limits), `parse_status` is `failed`. The body fields, headers and attachments are then empty, and `parse_warnings` contains one of these codes:

| Code | Meaning |
| - | - |
| `mime_invalid` | The MIME structure is invalid. |
| `mime_depth_exceeded` | More than 30 nested MIME levels. |
| `mime_parts_exceeded` | More than 1000 MIME parts. |
| `attachments_exceeded` | More than 100 attachments. |
| `parsed_content_too_large` | The parsed content exceeds 8 MiB. |
| `parser_timeout` | Parsing took too long. |

Talario never truncates content silently. A message with `parse_status: failed` is still a valid, stored message: process it from the original in `download`.

## When the content is not available

| Status | Code | Meaning |
| - | - | - |
| 409 | `content_not_ready` | The message was received a moment ago and is still being parsed. Retry later. |
| 409 | `content_failed` | There is no parsed content and there will be none, for example for messages received before parsing was introduced. Use the original message. |
| 410 | `message_expired` | The retention period ended. |
| 503 | `content_unavailable` | The content cannot be read right now. Retry later. |

The metadata of a message (`GET /receiving/messages/{id}`) shows the state as `content_status`: `pending`, `ready` or `failed`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.