---
title: "Get a message"
method: GET
path: "/v1/messages/{id}"
tags: ["messages"]
---

# Get a message

`GET /v1/messages/{id}`

## Request

- Base URL: `https://api.mailfixture.com`
- URL: `https://api.mailfixture.com/v1/messages/{id}`
- Auth: HTTP bearer

Full detail: bodies, unfolded headers, extraction results, and attachment metadata. Raw MIME bytes are not included; use the attachment download endpoint for decoded parts.

## Path parameters

- `id` string, uuid, required

## Response `200`

The message.

- MessageDetail
  - `attachments` AttachmentMeta[], required
    - `content_type` string, required: Declared MIME type; defaults to application/octet-stream.
    - `filename` string, nullable, required
    - `inline` boolean, required: True for cid-referenced inline parts (e.g. embedded images).
    - `size` integer, required: Decoded byte length.
  - `expires_at` string, date-time, required: Retention expiry (plan retention from receipt).
  - `extracted` Extracted, required: Extraction results computed at receipt. `text`, `auth`, and `spam` are conditionally present keys — see their descriptions.
    - `auth` ExtractedAuth: Receive-side SPF/DKIM/DMARC verdicts (RFC 8601 vocabulary). The whole key is absent when verification is disabled on the deployment — absence means "not verified", never "failed". Observation only: verdicts never affect acceptance.
      - `dkim` DkimAuth[], required: One entry per DKIM signature; unsigned mail is an empty array.
        - `domain` string, required
        - `error` string: Present only for non-pass results — e.g. a body-hash mismatch reports `neutral` with the reason here (RFC 6376 §6.1).
        - `result` 'pass' | 'fail' | 'softfail' | 'neutral' | 'none' | 'temperror' | 'permerror', required: RFC 8601 result vocabulary. DNS trouble degrades to `temperror`.
        - `selector` string, required
      - `dmarc` DmarcAuth, required
        - `domain` string: RFC5322.From domain; present only when non-empty.
        - `policy` 'none' | 'quarantine' | 'reject': Present only when a DMARC record exists.
        - `result` 'pass' | 'fail' | 'softfail' | 'neutral' | 'none' | 'temperror' | 'permerror', required: RFC 8601 result vocabulary. DNS trouble degrades to `temperror`.
      - `spf` SpfAuth, required
        - `domain` string, required: The identity checked (MAIL FROM, falling back to HELO); may be empty.
        - `result` 'pass' | 'fail' | 'softfail' | 'neutral' | 'none' | 'temperror' | 'permerror', required: RFC 8601 result vocabulary. DNS trouble degrades to `temperror`.
    - `links` ExtractedLink[], required
      - `class` 'verify' | 'reset' | 'unsubscribe' | 'other', required: Class guess from URL path + anchor keywords; magic-link/login URLs class as `verify`.
      - `text` string, nullable, required: Anchor text; null for bare-text URLs.
      - `url` string, required
    - `otp` ExtractedOtp, required
      - `best` string, nullable, required: Top-ranked code, or null when no candidate scores above the confidence threshold.
      - `candidates` OtpCandidate[], required: At most 5, sorted by descending score.
        - `score` number, required: Keyword-proximity score, one decimal.
        - `value` string, required: The digit sequence.
    - `spam` ExtractedSpam: SpamAssassin content analysis (local rules only — no network/RBL tests, so scores are deterministic). The whole key is absent when scoring is disabled, the daemon was unreachable, or the message exceeded the scoring size cap — absence means "not scored", never "failed". Observation only: the verdict never affects acceptance.
      - `rules` SpamRule[], required: The rules that fired, with their individual score contributions; empty when nothing matched.
        - `description` string, nullable: Human-readable rule description, when spamd supplied one.
        - `name` string, required: SpamAssassin rule symbol, e.g. `MISSING_HEADERS`.
        - `score` number, required: This rule's contribution to the total; may be negative.
      - `score` number, required: Total SpamAssassin score; higher is spammier. May be negative.
      - `threshold` number, required: The score at or above which the message is classed spam (default 5.0).
      - `verdict` 'spam' | 'ham', required: SpamAssassin's own decision (its `Spam: True/False`), not a client-side threshold recomputation.
    - `text` string: Plain-text rendering of the HTML part; present only when the message had no text/plain part.
  - `from_addr` string, required
  - `headers` MessageHeader[], required: All headers in source order, folding unfolded.
    - `name` string, required
    - `value` string, required
  - `html_body` string, nullable, required
  - `id` string, uuid, required
  - `inbox_id` string, uuid, required
  - `raw_size` integer, required: Size of the raw MIME in bytes.
  - `received_at` string, date-time, required
  - `subject` string, nullable, required
  - `text_body` string, nullable, required
  - `tls` boolean, required
  - `to_addrs` string[], required

## Other responses

- `401`: Missing bearer token, malformed key, or unknown/revoked/invalid API key.
- `404`: No such message on this account.
- `429`: Per-key rate limit exceeded. `Retry-After` says when the next request will be admitted; the RateLimit headers name the bucket that refused (`requests` or `creates`) with `r=0`.
- `500`: Unexpected server error. `detail` is null — internals are never leaked.

---

[API](https://skmtc.dev/mailfixture/apis/mailfixture-api.md) · [All operations](https://skmtc.dev/mailfixture/apis/mailfixture-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mailfixture/apis/mailfixture-api/revisions/4ddefe29f6f6?raw)
