# POST /v1/inbound/search

Search received messages

The messages the server has received, newest first, one page at a time. A POST so that an address travels in the body and never in a URL. The sender is matched by keyed hash, so the search itself stores no address. Messages older than 13 months are gone.

Takes a server key (`sk_…`, or `sk_test_…` on a test server) as a bearer token. TypeScript: `sendora.inbound.search(filters)`. Python: `sendora.inbound.search(**filters)`.

## Request body

- `streamId` (string (uuid)): Messages received on this inbound stream of the server.
- `from` (string (email)): Messages from this address, in the envelope or the From header.
- `recipient` (string (email)): Messages sent to this address of yours, the envelope recipient.
- `mailboxHash` (string (at most 255 characters)): Messages whose address carried this text after the plus sign.
- `receivedFrom` (string (date-time)): Accepted at or after this time.
- `receivedTo` (string (date-time)): Accepted before this time.
- `limit` (integer (1 to 100)): Page size, 1 to 100.
- `after` (string (uuid)): The `next` value of the previous page.

Example:

```json
{
  "from": "anna@example.com",
  "receivedFrom": "2026-09-15T00:00:00+02:00",
  "limit": 20
}
```

## Responses

### 200 A page of the received messages that match, newest first.

- `messages` (array of object, required)
  - `inboundMessageId` (string (uuid), required)
  - `streamId` (string (uuid), required): The inbound stream that received it.
  - `receivedAt` (string (date-time), required): When the message was accepted from the sending server.
  - `envelopeRecipient` (string, required): The address of yours the message was sent to.
  - `mailboxHash` (string or null, required): The text after the plus sign in that address, when the sender used one.
  - `sizeBytes` (integer, required)
  - `attachmentCount` (integer, required)
  - `hasText` (boolean, required)
  - `hasHtml` (boolean, required)
  - `parseIssue` (string or null, required): What the parser met; null when the message parsed clean. A hard issue empties the parsed parts, and the raw message stays: `no_headers`, `header_block_too_large`, `too_many_headers`, `too_many_parts`, `nesting_too_deep`, `missing_boundary`, `decoded_too_large`, `parse_timeout`, `parser_error`. A soft issue keeps them and says what was changed or dropped: `truncated_multipart`, `too_many_attachments`, `unknown_transfer_encoding`, `undecodable_container`, `filename_sanitised`, `control_chars_stripped`, `multiple_from`, `duplicate_header`, `malformed_header_dropped`, `address_list_truncated`, `header_value_truncated`. A message with several names the hard one, or the first soft one in this order.
  - `authentication` (object, required): What Sendora found when it checked the message; unchecked until the checks have run.
    - `spf` ("pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror" | "unchecked", required): SPF for the address in MAIL FROM.
    - `spfHelo` ("pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror" | "unchecked", required): SPF for the name the sending server gave in HELO.
    - `dkim` ("pass" | "fail" | "none" | "temperror" | "permerror" | "unchecked", required)
    - `dmarc` ("pass" | "fail" | "none" | "temperror" | "permerror" | "unchecked", required)
    - `dmarcPolicy` ("none" | "quarantine" | "reject" or null, required): What the sender’s domain asks for; told only when DMARC failed.
    - `arc` ("none" | "pass" | "fail" | "unchecked", required)
    - `checkedAt` (string (date-time) or null, required): When the checks ran; null until they have.
  - `contentAvailable` (boolean, required): False once the stream’s content window has passed; only the reference remains.
  - `contentExpiresAt` (string (date-time), required): When the content goes.
  - `from` (object or null, required): The From header; null without it or once the content is gone.
    - `address` (string, required)
    - `name` (string or null, required)
  - `subject` (string or null, required)
  - `date` (string (date-time) or null, required): The sender’s Date header as ISO 8601, when it was a real moment.
- `next` (string (uuid) or null, required): Pass as `after` for the next page; null on the last.

Example:

```json
{
  "messages": [
    {
      "inboundMessageId": "4d1f8b2e-9c3a-4e7b-8f21-6a5d0c9e7b31",
      "streamId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "receivedAt": "2026-09-19T08:00:02.000Z",
      "envelopeRecipient": "7c9e6679742540de944be07fc1f90ae7@inbound.sendora.se",
      "mailboxHash": null,
      "sizeBytes": 48213,
      "attachmentCount": 1,
      "hasText": true,
      "hasHtml": false,
      "parseIssue": null,
      "authentication": {
        "spf": "pass",
        "spfHelo": "pass",
        "dkim": "pass",
        "dmarc": "pass",
        "dmarcPolicy": null,
        "arc": "none",
        "checkedAt": "2026-09-19T08:00:03.000Z"
      },
      "contentAvailable": true,
      "contentExpiresAt": "2026-10-19T08:00:02.000Z",
      "from": {
        "address": "anna@example.com",
        "name": "Anna Andersson"
      },
      "subject": "A question about my order",
      "date": "2026-09-19T08:00:00.000Z"
    }
  ],
  "next": null
}
```

## Errors

Every error answers `error`, the code, and `message`, a sentence for a person. A code that adds fields is shown in full below the table.

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_request` | 400 | The body, the query or a header does not match what the route takes. |
| `unauthorized` | 401 | The key is missing, malformed or revoked. |
| `wrong_token_kind` | 403 | The key is of the other kind: a server key (sk_) where an account key (ak_) is needed, or the reverse. The message names the kind the operation takes. |
| `stream_not_found` | 422 | The streamId names no stream of this server. |

### `invalid_request`

- `issues` (array of object): One entry per invalid field; absent when a header is wrong.
  - `path` (string, required): The field, dotted, such as to.0.email; empty when the whole body is wrong.
  - `message` (string, required)

Example:

```json
{
  "error": "invalid_request",
  "message": "The request is invalid: to.0: Invalid email address",
  "issues": [
    {
      "path": "to.0",
      "message": "Invalid email address"
    }
  ]
}
```
