fabricatedemail-com Messages API

Reading stored mail, with a long poll that returns as soon as it lands.

Operations 3

GET /subscriptions/{sid}/messages Read the subscription's messages, oldest first, with an optional long poll #
GET /subscriptions/{sid}/messages/{id} Read one message #
GET /subscriptions/{sid}/messages/{id}/extract Pull the one-time codes and links out of a message #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/fabricatedemail-com:fabricatedemail-com-messages-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

fabricatedemail-com-messages-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: fabricatedemail HTTP Messages API
  version: 1.0.0
  description: '## General contract


    - Every error response has body `{"error": ""}`. The message is

    human-facing and may change; branch on the status code, never on the

    string.

    - Checks run in the order authentication (`401`) → account state (`403`) →

    rate limit (`429`) → request validation (`400`) → existence and

    ownership (`404`) → cross-key conflict (`409`) → address cap (`429`).

    A request that is wrong in two ways gets the earlier status.

    - Unknown routes and methods are `404 {"error": "Not found"}`, including

    after successful authentication.

    - Every timestamp is exactly the format of JavaScript

    `Date.prototype.toISOString()`: millisecond precision, trailing `Z`.

    - Request bodies must be JSON objects. A malformed body, a JSON array or a

    bare JSON string is `400`. Unknown fields are ignored.

    - Register first, then trigger the mail. A `201` from `POST /subscriptions`

    means the mailbox is committed and visible to the mail handler, and a

    stored message is visible to your very next poll.'
servers:
- url: https://api.fabricatedemail.com
  description: Production.
security:
- apiKey: []
tags:
- name: Messages
  description: Reading stored mail, with a long poll that returns as soon as it lands.
paths:
  /subscriptions/{sid}/messages:
    parameters:
    - $ref: '#/components/parameters/sid'
    get:
      operationId: listMessages
      tags:
      - Messages
      summary: Read the subscription's messages, oldest first, with an optional long poll
      description: 'At most 50 messages per response. A response holding exactly 50 may

        have more behind it: advance `after` to the last `id` you received and

        request again.


        With `wait`, the service re-checks about once per second and returns as

        soon as at least one message matches. On timeout it returns

        `200 {"messages": []}` — an empty list is a normal result, not an

        error.


        The key and the subscription are validated once, at request start, so a

        wait can outlive the subscription''s expiry, deletion or replacement:

        the call then times out with an empty list, and the *next* request is

        `404`.


        Parameter validation happens before existence, so a bad `after` on a

        subscription that does not exist is `400`, not `404`.'
      x-mcp-tool:
        name: wait_for_message
        summary: Wait for mail to arrive in a mailbox and return what is there.
        arguments:
        - name: subscriptionId
          required: true
          description: Sent as the `sid` path parameter.
          schema:
            type: string
            format: uuid
        - name: after
          required: false
          description: Sent as the `after` query parameter.
          schema:
            type: integer
            default: 0
        - name: waitSeconds
          required: false
          description: Sent as the `wait` query parameter. At most 30.
          schema:
            type: integer
            minimum: 0
            maximum: 30
            default: 30
      parameters:
      - name: after
        in: query
        required: false
        description: 'Return only messages whose `id` is greater than this. This is the

          integer `id` field of a message, never the RFC 5322 `messageId`.

          Ids start at 1, so an omitted `after` returns everything currently

          stored. A non-integer value — including `1.5`, `1e3`, `+1` and an

          empty value — is `400`.

          '
        schema:
          type: integer
          default: 0
      - name: wait
        in: query
        required: false
        description: 'Long-poll seconds. `0` returns immediately. A value above 30 is

          clamped to 30, not rejected. A negative or non-integer value is

          `400`.

          '
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: The matching messages, oldest first, at most 50.
          content:
            application/json:
              schema:
                type: object
                required:
                - messages
                properties:
                  messages:
                    type: array
                    maxItems: 50
                    items:
                      $ref: '#/components/schemas/Message'
              examples:
                oneMessage:
                  $ref: '#/components/examples/messageList'
                timedOut:
                  summary: A long poll that found nothing.
                  value:
                    messages: []
        '400':
          description: '`after` is not an integer, or `wait` is negative or not an integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                badAfter:
                  value:
                    error: after must be an integer message id
                badWait:
                  value:
                    error: wait must be a non-negative integer number of seconds
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/AccountSuspended'
        '404':
          $ref: '#/components/responses/SubscriptionNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /subscriptions/{sid}/messages/{id}:
    parameters:
    - $ref: '#/components/parameters/sid'
    - $ref: '#/components/parameters/messageId'
    get:
      operationId: getMessage
      tags:
      - Messages
      summary: Read one message
      description: 'Returns one message object, not wrapped in a list.


        The parent subscription''s liveness governs what is readable, not the

        message''s own retention, so a message can disappear from a live mailbox

        once its 24-hour retention has passed and the hourly cleanup has

        collected it.'
      x-mcp-tool:
        name: get_message
        summary: Read one stored message.
        arguments:
        - name: subscriptionId
          required: true
          description: Sent as the `sid` path parameter.
          schema:
            type: string
            format: uuid
        - name: messageId
          required: true
          description: Sent as the `id` path parameter — the integer `id` of the message.
          schema:
            type: integer
            minimum: 1
      responses:
        '200':
          description: The message.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
              examples:
                message:
                  $ref: '#/components/examples/message'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/AccountSuspended'
        '404':
          description: 'The message does not exist, belongs to a different subscription, or

            the path segment is not an integer; or the parent subscription is

            unknown, expired, deleted, replaced or owned by another key.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                notFound:
                  $ref: '#/components/examples/errorNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /subscriptions/{sid}/messages/{id}/extract:
    parameters:
    - $ref: '#/components/parameters/sid'
    - $ref: '#/components/parameters/messageId'
    get:
      operationId: extractFromMessage
      tags:
      - Messages
      summary: Pull the one-time codes and links out of a message
      description: 'The same authentication and `404` rules as reading the message itself.


        The rules are deliberately simple, and heuristic:


        - `codes` are runs of 4 to 8 digits in the text body — or in the HTML

        body stripped of tags, when there is no text body — that appear

        within 40 characters of one of the words `code`, `OTP`, `PIN`,

        `verification`, `verify` or `passcode`, matched case-insensitively.

        In order of appearance, deduplicated.

        - `links` are every `http://` or `https://` URL in the text and HTML

        bodies. In order of appearance, deduplicated.


        A message with neither answers empty arrays, not `404`. A headers-only

        message has no bodies to read, so it always answers empty arrays.'
      x-mcp-tool:
        name: extract
        summary: Extract one-time codes and links from a stored message.
        arguments:
        - name: subscriptionId
          required: true
          description: Sent as the `sid` path parameter.
          schema:
            type: string
            format: uuid
        - name: messageId
          required: true
          description: Sent as the `id` path parameter — the integer `id` of the message.
          schema:
            type: integer
            minimum: 1
      responses:
        '200':
          description: What the heuristics found. Either array may be empty.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Extraction'
              examples:
                codeAndLink:
                  value:
                    codes:
                    - '482913'
                    links:
                    - https://example.com/verify?t=8f14e45fceea167a
                nothingFound:
                  summary: A message with neither a code nor a link.
                  value:
                    codes: []
                    links: []
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/AccountSuspended'
        '404':
          description: Same as reading the message itself.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                notFound:
                  $ref: '#/components/examples/errorNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  responses:
    Unauthorized:
      description: 'The key or token is missing, malformed, unknown or revoked, or the key

        has no account. All of these are indistinguishable from outside.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            unauthorized:
              $ref: '#/components/examples/errorUnauthorized'
    ServerError:
      description: A server-side failure, logged by the service with the real cause. Retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            internal:
              $ref: '#/components/examples/errorInternal'
    RateLimited:
      description: 'The key passed 600 requests in a minute. `Retry-After` says when to

        come back. A well-behaved 30-second poll loop costs about two requests

        a minute and never sees this.

        '
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            rateLimited:
              $ref: '#/components/examples/errorRateLimited'
    AccountSuspended:
      description: 'The key''s account is suspended. Every key of the account sees this, on

        every key route, from the next request on; a long poll already in

        flight is not interrupted, and the request after it gets this within

        30 seconds. Mail addressed to the account''s

        addresses is dropped meanwhile. Reinstatement makes the account work

        again; mail that arrived during the suspension is gone, and addresses

        and mail that expired meanwhile were deleted as usual.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            suspended:
              $ref: '#/components/examples/errorAccountSuspended'
    SubscriptionNotFound:
      description: 'The subscription is unknown, expired, deleted, replaced or owned by

        another key. These are indistinguishable, and the handle is dead for

        good: get a new one by registering again.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            notFound:
              $ref: '#/components/examples/errorNotFound'
  schemas:
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
          description: Human-facing, and free to change. Branch on the status code.
    Extraction:
      type: object
      required:
      - codes
      - links
      properties:
        codes:
          type: array
          description: Digit runs that look like one-time codes, in order of appearance, deduplicated.
          items:
            type: string
        links:
          type: array
          description: Every URL in the bodies, in order of appearance, deduplicated.
          items:
            type: string
            format: uri
    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

            mailbox: ids are assigned service-wide, so a mailbox may see 7, 12,

            13. Never compute the next cursor — carry over the `id` of the last

            message you handled. This is the `after` cursor and the

            single-message path parameter.

            '
        messageId:
          type:
          - string
          - 'null'
          description: 'The RFC 5322 `Message-ID` header, `null` when the sender set none.

            Display and debug data — never an API parameter.

            '
        from:
          type:
          - string
          - 'null'
          description: 'The parsed `From:` header address. On a headers-only row it is the

            SMTP envelope sender instead; the two can legitimately differ. A

            test asserting origin must check this, not mere presence.

            '
        to:
          type: string
          description: 'Always the SMTP envelope recipient, the address that actually

            routed here, so a spoofed `To:` header cannot affect it. May differ

            from the `to` entry in `headers`.

            '
        subject:
          type:
          - string
          - 'null'
        text:
          type:
          - string
          - 'null'
          description: 'Plain-text body, capped at 256 KiB. `null` on a headers-only row

            and when the mail had no text part.

            '
        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

            lowercased, in the platform''s canonical order. Repeated headers —

            `Received`, most often — are joined into one value separated by

            `", "`; `set-cookie` is the one exception and appears as separate

            pairs. Look headers up by name; do not rely on position.

            '
          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.

            Gate every body assertion on this: a large mail otherwise fails the

            assertion with a confusing `null`.

            '
        rawSize:
          type: integer
          description: 'Size in bytes of the raw message as received. Above 1 MiB the body

            is never parsed and the message is stored headers-only.

            '
        receivedAt:
          type: string
          format: date-time
          description: 'When the service received it. A stored message lives 24 hours from

            here, capped by its subscription''s own expiry, and is removed by an

            hourly cleanup — so it may outlive its retention, normally by up to

            an hour, and longer if a cleanup run fails.

            '
  examples:
    errorNotFound:
      value:
        error: Not found
    errorRateLimited:
      summary: 'The rate limit — sent with `Retry-After: 60`.'
      value:
        error: rate limited
    message:
      summary: A verification mail with both bodies.
      value:
        id: 42
        messageId: <abc@mail.example.com>
        from: noreply@example.com
        to: myapp-4f2a9c81d3e6b7a0@fabricatedemail.com
        subject: Verify your account
        text: Your code is 123456
        html: <p>Your code is <b>123456</b></p>
        headers:
        - - from
          - Example <noreply@example.com>
        - - subject
          - Verify your account
        attachments:
        - filename: invoice.pdf
          mimeType: application/pdf
          size: 31337
        truncated: false
        rawSize: 4096
        receivedAt: '2026-08-10T12:00:00.000Z'
    errorAccountSuspended:
      value:
        error: account suspended
    errorInternal:
      value:
        error: internal error
    messageList:
      summary: One message in a poll response.
      value:
        messages:
        - id: 42
          messageId: <abc@mail.example.com>
          from: noreply@example.com
          to: myapp-4f2a9c81d3e6b7a0@fabricatedemail.com
          subject: Verify your account
          text: Your code is 123456
          html: <p>Your code is <b>123456</b></p>
          headers:
          - - from
            - Example <noreply@example.com>
          - - subject
            - Verify your account
          attachments: []
          truncated: false
          rawSize: 4096
          receivedAt: '2026-08-10T12:00:00.000Z'
    errorUnauthorized:
      value:
        error: unauthorized
  headers:
    RetryAfter:
      description: 'Seconds to wait before retrying. Present on the rate-limit `429` and

        absent on the address-cap `429`, which is what tells the two apart

        without reading the message.

        '
      required: false
      schema:
        type: integer
        minimum: 0
  parameters:
    sid:
      name: sid
      in: path
      required: true
      description: The subscription id from `POST /subscriptions`.
      schema:
        type: string
        format: uuid
    messageId:
      name: id
      in: path
      required: true
      description: 'The integer `id` field of the message — never the RFC 5322 `messageId`

        header. A path segment that is not an integer is `404`.

        '
      schema:
        type: integer
        minimum: 1
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'The 64-hex-character API key, created in the customer dashboard and

        shown once. The scheme is matched case-insensitively. A missing header,

        a malformed credential, an unknown key and a revoked key are all `401`

        and indistinguishable from outside.

        '
x-reserved-local-parts:
- abuse
- postmaster
- security
- support
- admin
- noreply
- no-reply
- hostmaster
- webmaster
- info
- billing
- legal
- dmarc