{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/fabricatedemail-com/json-schema/fabricatedemail-com-message-schema.json",
  "title": "Message",
  "x-generated": "2026-10-30",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/fabricatedemail-com-openapi.yml#/components/schemas/Message",
  "type": "object",
  "required": [
    "id",
    "messageId",
    "from",
    "to",
    "subject",
    "text",
    "html",
    "headers",
    "attachments",
    "truncated",
    "rawSize",
    "receivedAt"
  ],
  "properties": {
    "id": {
      "type": "integer",
      "description": "Service-assigned and monotonic, but not contiguous within a\nmailbox: ids are assigned service-wide, so a mailbox may see 7, 12,\n13. Never compute the next cursor — carry over the `id` of the last\nmessage you handled. This is the `after` cursor and the\nsingle-message path parameter.\n"
    },
    "messageId": {
      "type": [
        "string",
        "null"
      ],
      "description": "The RFC 5322 `Message-ID` header, `null` when the sender set none.\nDisplay and debug data — never an API parameter.\n"
    },
    "from": {
      "type": [
        "string",
        "null"
      ],
      "description": "The parsed `From:` header address. On a headers-only row it is the\nSMTP envelope sender instead; the two can legitimately differ. A\ntest asserting origin must check this, not mere presence.\n"
    },
    "to": {
      "type": "string",
      "description": "Always the SMTP envelope recipient, the address that actually\nrouted here, so a spoofed `To:` header cannot affect it. May differ\nfrom the `to` entry in `headers`.\n"
    },
    "subject": {
      "type": [
        "string",
        "null"
      ]
    },
    "text": {
      "type": [
        "string",
        "null"
      ],
      "description": "Plain-text body, capped at 256 KiB. `null` on a headers-only row\nand when the mail had no text part.\n"
    },
    "html": {
      "type": [
        "string",
        "null"
      ],
      "description": "HTML body, capped at 256 KiB. Same nullability rule as `text`."
    },
    "headers": {
      "type": "array",
      "description": "Every header, as ordered `[name, value]` pairs with names\nlowercased, in the platform's canonical order. Repeated headers —\n`Received`, most often — are joined into one value separated by\n`\", \"`; `set-cookie` is the one exception and appears as separate\npairs. Look headers up by name; do not rely on position.\n",
      "items": {
        "type": "array",
        "minItems": 2,
        "maxItems": 2,
        "items": {
          "type": "string"
        }
      }
    },
    "attachments": {
      "type": "array",
      "description": "Metadata only — content and raw MIME are not retrievable.",
      "items": {
        "type": "object",
        "required": [
          "filename",
          "mimeType",
          "size"
        ],
        "properties": {
          "filename": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "description": "Size in bytes."
          }
        }
      }
    },
    "truncated": {
      "type": "boolean",
      "description": "`true` if anything was cut, or if the mail was stored headers-only.\nGate every body assertion on this: a large mail otherwise fails the\nassertion with a confusing `null`.\n"
    },
    "rawSize": {
      "type": "integer",
      "description": "Size in bytes of the raw message as received. Above 1 MiB the body\nis never parsed and the message is stored headers-only.\n"
    },
    "receivedAt": {
      "type": "string",
      "format": "date-time",
      "description": "When the service received it. A stored message lives 24 hours from\nhere, capped by its subscription's own expiry, and is removed by an\nhourly cleanup — so it may outlive its retention, normally by up to\nan hour, and longer if a cleanup run fails.\n"
    }
  }
}