Creates a MessageSearchResult resource.
const url = 'https://api.facteur.local/messages/search';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"attachmentName":"example","body":"example","cursor":"example","hasAttachment":true,"hasOtp":true,"inbox":"example","limit":50,"match":"all","otp":"example","sentFrom":"example","sentTo":"example","since":"2026-04-15T12:00:00Z","status":"parsed","subject":"verification","type":"email","wait":0,"workspace":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.facteur.local/messages/search \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "attachmentName": "example", "body": "example", "cursor": "example", "hasAttachment": true, "hasOtp": true, "inbox": "example", "limit": 50, "match": "all", "otp": "example", "sentFrom": "example", "sentTo": "example", "since": "2026-04-15T12:00:00Z", "status": "parsed", "subject": "verification", "type": "email", "wait": 0, "workspace": "example" }'The search of searchMessages with its criteria in a JSON body instead of the query string: the same criteria, limits, page, cursor and wait. Returns the messages that match, newest first, one page at a time, bodies included. At least one criterion is required; a field that does not exist is refused.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”The new MessageSearchResult resource
A page of messages found by a search, and the cursor of the next one.
object
A substring of the name of an attachment, without regard to case, over every real attachment of the message, kept or not (an inline image is not one). A text criterion: it joins sentTo, sentFrom, subject, body and otp in all or any.
A substring of the body, in its text part or its HTML part, without regard to case.
The nextCursor of the previous page.
Whether the message carries a real attachment (an inline image, such as a signature, is not one); false looks for the absence. A bound: it narrows in both modes, and alone it is a complete search.
Whether the message carries a code (an empty one is not a code). A bound: it narrows in both modes, and alone it is a complete search.
The identifier of an inbox. Naming one is a search on its own. One the caller does not reach is a 404, never an empty list. It wins over workspace when both are given.
The size of the page, 50 by default and 200 at most; a larger one is brought down to 200, zero is refused.
How sentTo, sentFrom, subject, body, otp and attachmentName join: all of them, or any of them. The bounds (since, type, hasOtp, hasAttachment, status) narrow in both modes.
A substring of the one-time code the ingester extracted; read in that column, never in the body.
A substring of the address of the sender, without regard to case.
A substring of the address the message was sent to, without regard to case. % and _ are text.
Messages received at this instant or after, in ISO 8601. A bound: it narrows in both modes.
parsed (the default) hides a message the ingester has not finished reading; any shows every state, including the refused ones.
A substring of the subject, without regard to case.
Example
verificationThe channel. A bound, absent by default.
How long to wait, in milliseconds, for a message that matches: the search is played again every 250 ms and answers as soon as one does. Five minutes (300000) at most, a larger value is brought down to it and is not refused; zero, absent or negative is a single search. An expired wait answers 200 with an empty page. An organization holds 20 waits open at once, shared by all the instances: the next is refused with 429 TOO_MANY_WAITS and Retry-After: 1. An instance that is stopping answers a wait it cannot finish with 503 API_DRAINING and Retry-After: 1, never with an empty page. It fills no search by itself.
The address (slug) of a workspace to search in. It narrows the search and does not fill it; the inbox wins when both are given.
Responses
Section titled “Responses”MessageSearchResult resource created
A page of messages found by a search, and the cursor of the next one.
object
A message an inbox received, or the trace of one it refused.
object
The real attachments of the message, kept or not, by name then identifier; an embedded image is not one (see inlines). Absent on a refused message. Download a kept one with downloadAttachment, by its identifier: the response never says where the bytes are.
object
sha256: then the sha256 of the decoded bytes, in hexadecimal.
The type the sender declared, which nothing checked against the bytes.
As the sender wrote it: data, never a path.
Why the bytes were not kept; only on an attachment that was not.
Of the decoded bytes.
Whether the bytes were kept, so whether there is something to download. It is reason being absent.
Every code the ingester found, the best candidate first. Absent on a refused message.
What the connection knew, in the read of one message and in a search of the public API: null when the ingester knew nothing, and each fact null when it did not know it, never an empty string. A refused message keeps it.
object
Named, such as TLS 1.3, never a number.
How long the ingester took to accept it, in milliseconds.
Why the message was refused; only on a quota_exceeded message, which carries nothing derived. One of PLAN_QUOTA_EXCEEDED, INBOX_QUOTA_EXCEEDED, WORKSPACE_QUOTA_EXCEEDED, OVERAGE_CAP_REACHED, DAILY_SPIKE_CAP, SUBSCRIPTION_SUSPENDED; a code a later version of the ingester writes is said as it is.
When the retention of the plan removes the message, or null when it does not expire.
The values the extractors of the inbox found, by key. Absent on a refused message.
object
The display name of the sender, when the message gave one. Absent on a refused message.
The headers of the message, names in lower case; in the read of one message and in a search of the public API. Absent on a refused message.
object
In the read of one message and in a search of the public API; absent from the list of an inbox and from the search of app. Absent on a refused message.
The inbox that received it.
The images embedded in the body, such as a signature logo: the parts the message declared inline. They are not attachments (never counted among them, never found by a search) and carry no link. Absent on a refused message.
object
What the cid: of the HTML body names.
The links of the message, in order of appearance, the HTML part first. Absent on a refused message.
The one-time code the ingester extracted, null when there is none. Absent on a refused message.
The size of the message as received, in bytes; 0 on a message that was refused, whose content was not kept.
The state of the message. Read it before anything derived from the body.
Absent on a refused message; null when there is none.
In the read of one message and in a search of the public API; absent from the list of an inbox and from the search of app. Absent on a refused message.
The channel that carried it.
Send it back as cursor to read the next page; null on the last page.
Example
{ "messages": [ { "attachments": [ { "reason": "PERFORMANCE_MODE" } ], "status": "received", "type": "email" } ]}The request cannot be read: a criterion that is empty, unknown or invalid, a cursor that is not one, a value its rule refuses, or a body that is not JSON. The code says which, and field names the criterion.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}No key, or a key that is refused. WWW-Authenticate says where to read how to present one.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}The key does not hold the permission of the operation, or the operation refuses every key.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}There is none of this identifier in the reach of the key. The answer is the same for another organization or a workspace the key does not name.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}More calls than the key may make in a minute. Retry-After says when to try again.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}The instance that holds the call is stopping and cannot finish a wait: Retry-After: 1, send it again. It is never an empty page.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}default
Section titled “default”Any refusal: an application/problem+json body whose code says which.
A refusal, as RFC 9457 application/problem+json with the code of the register. A code adds the members its rule needs (retryAfter, permission…), so more members than these may come.
object
The stable code of the refusal. It never changes once shipped.
English prose for the person who reads the answer. It may be reworded: branch on code.
The HTTP status.
The name of the HTTP status.
The address of the problem: /errors/<code>.
On a body that fails validation, one entry per field.
object
Example
{ "code": "BAD_REQUEST", "status": 404, "title": "Not Found", "type": "/errors/INBOX_NOT_FOUND", "violations": [ { "code": "BAD_REQUEST" } ]}