Docs
Search
Full text search over the messages of an inbox, with the criteria applied to the message.
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:
fatturafindsFatturaandperchéis found byperche. "fattura marzo"in quotes is an exact phrase: adjacent words in that order. It does not matchmarzo fattura.- A leading minus excludes:
-from:noreply@example.com,-subject:newsletter,-label:archived. from:,to:,subject:andlabel:take a value, and can be negated with a minus.is:acceptsunread,readorflagged.readmeans "no unread label", because the unread label is the only read state a message carries.has_attachment:truekeeps only messages with files;has_attachment:falseonly those without.after:andbefore:take an RFC 3339 timestamp (or passafter=andbefore=as parameters); both are inclusive.
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
GET/v0/inboxes/{inbox}/messages/search
Search the messages of an inbox, full text (requires message_read or message_metadata)
Parameters
| Name | Type | Notes |
|---|---|---|
inbox | string (email) | in path, required |
q | string | in query, Free text with quoted exact phrases, -exclusions and field:value tokens |
from | string[] | in query, Sender: case and accent insensitive substring of the From header, repeatable, ORed |
to | string[] | in query, Recipient (To or Cc): repeatable, ORed |
subject | string[] | in query, Subject substring, repeatable, ORed |
has_attachment | boolean | in query, Only messages that carry an attachment, or only those that do not |
label | string[] | in query, Label, repeatable, ORed (any of them) |
is | unread | read | flagged | in query, read, unread or flagged |
after | string (date-time) | in query, Inclusive lower bound on created_at |
before | string (date-time) | in query, Inclusive upper bound on created_at |
limit | integer | in query, min 1, max 100, default 20 |
cursor | string | in query, next_cursor of the previous page |
include_review | boolean | in query, Also return messages held for security review; needs message_review, default false |
Responses
200OK: SearchResult400Error envelope: Error403Error envelope: Error404Error envelope: Error
SearchResult object
| Name | Type | Notes |
|---|---|---|
count | integer | |
next_cursor | string | Pass as cursor= for the next page; absent on the last one |
messages | Message[] |
Message object
| Name | Type | Notes |
|---|---|---|
id | string (uuid) | |
message_id | string | RFC 5322 Message-ID with angle brackets |
thread_id | string (uuid) | |
inbox_id | string | |
direction | in | out | |
from | string | |
to | string[] | |
cc | string[] | |
subject | string | |
text | string | |
html | string | |
extracted_text | string | New text without quoted history (set by the gatekeeper) |
preview | string | |
snippet | string | Excerpt around the match. Only in search results, and empty for a key holding message_metadata without message_read |
labels | string[] | |
in_reply_to | string | |
references | string[] | |
status | received | queued | sending | sent | failed | held | cancelled | |
delivery_error_code | unauthorized_ip | auth_failed | relay_unreachable | connection_refused | tls_failed | unknown | Why 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_error | string | What the mail server said when it refused the message, one line, at most 500 characters, never a credential. |
last_error | string | The 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_at | string (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_results | object | What 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. |
gatekeeper | object | |
screening | review | allowed | released | Hold state under screen before notify; absent when the message was not held. Pending messages are never returned. |
attachments | object[] | |
created_at | string (date-time) |