Docs

Messages and threads

How inbound mail is parsed, threaded, labeled and screened before your agent reads it.

View as Markdown

Inbound mail is parsed into messages and grouped into threads using the In-Reply-To and References headers. Your agent reads conversations, not raw MIME.

Threads

List threads of one inbox, or across the whole organization with GET /v0/threads. Lists are newest first and paginated with limit (1 to 100, default 20) and page_token (from next_page_token). GET /v0/threads/{thread} returns the thread with all messages, oldest first.

Messages

Listing returns metadata without bodies, to keep responses small for agents. Getting one message returns:

  • text and html: the bodies as sent.
  • extracted_text: only the new text, with quoted history and mobile signatures removed. Set by the gatekeeper; prefer it when you pass a message to a model.
  • authentication_results: DKIM result for the From domain, spam score and spam action.
  • gatekeeper: prompt injection screening, see below.
  • attachments: id, filename, content type and size. See Attachments.

The original message is available as RFC 822 from GET .../messages/{message}/raw. The {message} path parameter accepts the message UUID or the RFC Message-ID (URL encoded).

Gatekeeper verdict

Every inbound message is screened for prompt injection before it is marked complete. The verdict is clear or review, with a score and the reasons that triggered it, such as instruction_override, data_exfiltration, role_hijack, fake_tool_markup, hidden_html_text, invisible_unicode or dmarc_fail.

gatekeeper field
"gatekeeper": {
  "status": "complete",
  "verdict": "review",
  "score": 0.8,
  "reasons": ["instruction_override", "data_exfiltration"],
  "model": "gatekeeper-heuristics-v1"
}

status is pending until screening finishes, unavailable if the screener could not run and timeout if no verdict arrived within 15 minutes. Messages with verdict review also get the review label, and the message.security.review event fires.

Screen before notify

Every organization has the setting screen_before_notify, on by default (see GET /v0/auth/me). Inbound mail is held until the gatekeeper verdict exists, so an agent never reads a prompt injection before screening finishes. The message field screening shows the hold state:

`screening`MeaningVisibleEvents
(pending)Waiting for the gatekeeper.Never returned.None yet.
allowedVerdict clear.Yes.message.security.completed, then message.received.
reviewVerdict review, screener unavailable or timed out: the hold fails closed.Only with include_review=true and message_review.message.security.review only, without bodies.
releasedA reviewer released it.Yes.message.received.

Held messages are left out of message and thread lists, gets, raw downloads and attachments, and cannot be replied to or relabeled. A thread started by a held message stays hidden until one of its messages is visible. The same rules apply to the MCP tools.

Review and release
# a key with message_read and message_review
curl -s "https://api.mueslimail.com/v0/inboxes/billing@agents.your-domain.eu/messages?include_review=true&label=review"   -H "Authorization: Bearer $MAI_REVIEW_KEY"

# a key with message_release: the message becomes visible and message.received fires
curl -s -X POST "https://api.mueslimail.com/v0/inboxes/billing@agents.your-domain.eu/messages/$MESSAGE_ID/release"   -H "Authorization: Bearer $MAI_REVIEW_KEY"

Labels

The platform sets system labels: received and unread on inbound mail, sent on outbound mail, spam when the spam filter flags a message, review when the gatekeeper does. Add your own labels to track workflow state, and filter any list with ?label=.

Update labels
curl -s -X PATCH "https://api.mueslimail.com/v0/inboxes/billing@agents.your-domain.eu/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $MAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"add_labels": ["invoice", "approved"], "remove_labels": ["unread"]}'

Endpoints

GET/v0/inboxes/{inbox}/threads

List threads of an inbox

Parameters

NameTypeNotes
inboxstring (email)in path, required
limitintegerin query, min 1, max 100, default 20
page_tokenstringin query
labelstringin query
include_reviewbooleanin query, Also return messages held for security review; needs message_review, default false

Responses

  • 200 OK: ThreadList

GET/v0/threads

List threads across the organization

Parameters

NameTypeNotes
limitintegerin query, min 1, max 100, default 20
page_tokenstringin query
labelstringin query
include_reviewbooleanin query, Also return messages held for security review; needs message_review, default false

Responses

  • 200 OK: ThreadList

GET/v0/threads/{thread}

Get a thread with the messages the caller may see, oldest first

Parameters

NameTypeNotes
threadstring (uuid)in path, required
include_reviewbooleanin query, Also return messages held for security review; needs message_review, default false

Responses

  • 200 OK: Thread

GET/v0/inboxes/{inbox}/messages

List message metadata (no bodies)

Parameters

NameTypeNotes
inboxstring (email)in path, required
limitintegerin query, min 1, max 100, default 20
page_tokenstringin query
labelstringin query
include_reviewbooleanin query, Also return messages held for security review; needs message_review, default false

Responses

  • 200 OK: MessageList

GET/v0/inboxes/{inbox}/messages/{message}

Get a message with bodies, extracted_text, authentication results and gatekeeper verdict

Parameters

NameTypeNotes
inboxstring (email)in path, required
messagestringin path, required, Message UUID or RFC Message-ID (URL encoded)
include_reviewbooleanin query, Also return messages held for security review; needs message_review, default false

Responses

  • 200 OK: Message
  • 403 Error envelope: Error
  • 404 Error envelope: Error

PATCH/v0/inboxes/{inbox}/messages/{message}

Add or remove custom labels

Parameters

NameTypeNotes
inboxstring (email)in path, required
messagestringin path, required, Message UUID or RFC Message-ID (URL encoded)

Body

NameTypeNotes
add_labelsstring[]
remove_labelsstring[]

Responses

  • 200 OK: Message

GET/v0/inboxes/{inbox}/messages/{message}/raw

Raw RFC 822 message

Parameters

NameTypeNotes
inboxstring (email)in path, required
messagestringin path, required, Message UUID or RFC Message-ID (URL encoded)
include_reviewbooleanin query, Also return messages held for security review; needs message_review, default false

Responses

  • 200 OK: message/rfc822

POST/v0/inboxes/{inbox}/messages/{message}/release

Release a message held for review (needs message_release, audited)

Parameters

NameTypeNotes
inboxstring (email)in path, required
messagestringin path, required, Message UUID or RFC Message-ID (URL encoded)

Responses

  • 200 Released: Message
  • 403 Error envelope: Error
  • 404 Error envelope: Error
  • 409 Error envelope: Error

Thread object

NameTypeNotes
thread_idstring (uuid)
inbox_idstring
subjectstring
previewstring
labelsstring[]
message_countinteger
timestampstring (date-time)
messagesMessage[]
retention_purged_atstring (date-time)Set when retention deleted the last message of the thread. A thread with no messages answers 200 with this and retention_notice, instead of 404, because a ticket may point at it.
retention_noticestringThe sentence in place of a body the retention window removed.

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)