Docs

Search

Full text search over the messages of an inbox, with the criteria applied to the message.

View as Markdown

GET /v0/inboxes/{inbox}/messages/search searches every message of one inbox on the server. It is not a filter over a page you already loaded: a query reaches the whole inbox, including mail your client has never seen.

What comes back

The criteria apply to the message, not to the thread. A message that matches brings its thread_id with it, and the answer is a list of messages, newest first, paginated with limit (1 to 100, default 20) and cursor (from next_cursor). Group the hits by thread_id to show conversations; every hit also carries snippet, an excerpt around the match.

A negation excludes the message, never the thread: -from:noreply hides that message, and the rest of its conversation stays findable through the other messages. Criteria of different fields are ANDed. The values of one repeated parameter (from, to, subject, label) are ORed.

The query language

  • Free text matches message text, subject and sender, ignoring case and accents: fattura finds Fattura and perché is found by perche.
  • "fattura marzo" in quotes is an exact phrase: adjacent words in that order. It does not match marzo fattura.
  • A leading minus excludes: -from:noreply@example.com, -subject:newsletter, -label:archived.
  • from:, to:, subject: and label: take a value, and can be negated with a minus.
  • is: accepts unread, read or flagged. read means "no unread label", because the unread label is the only read state a message carries.
  • has_attachment:true keeps only messages with files; has_attachment:false only those without.
  • after: and before: take an RFC 3339 timestamp (or pass after= and before= as parameters); both are inclusive.
cURL
curl -s -G https://api.mueslimail.com/v0/inboxes/{inbox}/messages/search \
  -H "Authorization: Bearer $MAI_API_KEY" \
  --data-urlencode 'q="fattura marzo" -from:noreply' \
  --data-urlencode 'has_attachment=true' \
  --data-urlencode 'after=2026-01-01T00:00:00Z' \
  --data-urlencode 'limit=50'

The economical permission

message_read returns the mail, with a snippet for every hit. message_metadata is the narrower permission: a key holding only that one matches the headers (subject and sender) and receives no body, no preview and no snippet. It is what an economical sync or a routing agent should hold: it can find a message and route it without reading it. Asking for a body word returns nothing for such a key, so the permission cannot be used to probe text it may not read.

Pagination

The cursor is a keyset over (created_at, id) descending, so a message that arrives while you read page two neither shifts nor repeats a row of page one: new mail is newer than the cursor, which is the one direction the next page does not look.

Endpoints

SearchResult object

NameTypeNotes
countinteger
next_cursorstringPass as cursor= for the next page; absent on the last one
messagesMessage[]

Message object

NameTypeNotes
idstring (uuid)
message_idstringRFC 5322 Message-ID with angle brackets
thread_idstring (uuid)
inbox_idstring
directionin | out
fromstring
tostring[]
ccstring[]
subjectstring
textstring
htmlstring
extracted_textstringNew text without quoted history (set by the gatekeeper)
previewstring
snippetstringExcerpt around the match. Only in search results, and empty for a key holding message_metadata without message_read
labelsstring[]
in_reply_tostring
referencesstring[]
statusreceived | queued | sending | sent | failed | held | cancelled
delivery_error_codeunauthorized_ip | auth_failed | relay_unreachable | connection_refused | tls_failed | unknownWhy an outbound message did not leave. Set when a delivery attempt was refused, absent otherwise; with status failed, the message is not delivered and the console says so.
delivery_errorstringWhat the mail server said when it refused the message, one line, at most 500 characters, never a credential.
last_errorstringThe last reason a message did not leave, from the transport or from the platform: a send held by undo send whose window closed while sending was paused fails with the reason of the pause here, and no delivery_error, because no mail server refused it.
cancel_window_ends_atstring (date-time)End of the undo window of a send born held. Before it, POST .../cancel withdraws the message; after it, the message is on its way and cancel answers 409 invalid_state. Absent on a message that never had a window.
authentication_resultsobjectWhat the SMTP edge verified for inbound mail: dkim, spf, dmarc, dmarc_policy, antivirus, spam_score, spam_action. Mail failing DMARC is labelled dmarc-fail, and spam under p=quarantine or p=reject.
gatekeeperobject
screeningreview | allowed | releasedHold state under screen before notify; absent when the message was not held. Pending messages are never returned.
attachmentsobject[]
created_atstring (date-time)