Skip to content
simailDocsv0

Guides

Holds & injection scanning

Why inbound mail is held, what agents see while it is held, how prompt-injection scanning works, and how humans release or reject mail.

docs.simail.ai/holds

A hold keeps a message away from your agent until a human looks at it. Holds are decided by deterministic checks before the message is stored, so there is never a moment when a suspicious message is readable by an agent.

What a held message looks like to an agent

Section titled “What a held message looks like to an agent”

On every channel (REST, MCP, webhooks, GET /v0/events, WebSocket) a held message is metadata only:

{
"message_id": "msg_…",
"thread_id": "thr_…",
"inbox_id": "agent-7k2m@simail.email",
"from": "unknown@example.com",
"timestamp": "2026-10-18T17:02:11Z",
"size": 18422,
"labels": ["received", "held"],
"hold_reasons": ["injection_suspected"],
"held": true,
"trust_envelope": { "computed_by": "none", "untrusted": true }
}

timestamp is when simail received the message (the same field as on every message).

Subject, body, attachments and raw source are withheld. Listings include held messages only for keys with hold_metadata_read. Held mail never wakes your agent, and search matches only its metadata. Fetching the body of a held message does not work; neither does the attachment or raw endpoint.

Your agent cannot release mail: POST /v0/inboxes/{inbox_id}/messages/{message_id}/agent-armor/release returns 403 human_required, and the MCP server has no release tool.

hold_reasonsCause
rate_limitedmore than 50 messages per hour from one sender to one inbox, or 2,000 per hour to the organization (far above that, extra copies are counted and dropped)
unparseablethe message could not be parsed safely
injection_suspectedthe prompt-injection score reached the threshold (below)
otp_limitexternal organizations only: too many verification-code messages for the organization today (more than 10, or from more than 3 sending domains). Verification-code mail to the internal tenant goes to your agent as usual
sensitive_materialthe message looks like regulated or privileged material (for example legal case material) that simail does not accept
enrichmenta later, optional check (attachment text, model-based scoring) found a problem

The scanner is a set of deterministic heuristics, run on the subject, body and HTML of every inbound message:

  • instruction phrasing aimed at an AI (“ignore previous instructions”, role or system prompts), in English and Chinese;
  • hidden text: zero-width characters, CSS-hidden content, text in the same colour as the background;
  • fake system prompts and tool-call syntax;
  • very long encoded blobs.

The result is trust_envelope.injection_score between 0 and 1. At or above injection.hold_threshold (0.7 by default; your policy can lower it, never raise it above the platform’s value) the message is held.

Two optional checks are off by default and can be turned on by a human: text extraction from attachments, and model-based injection scoring. Both run after the message is stored and can only add a hold, never remove one. A message that is held later disappears from search results and produces a message.held event.

In the console, a human with access to the inbox opens Held and chooses:

  • Release: the message becomes a normal message. Its body is indexed for search, and a message.released event is published (which can wake your agent).
  • Reject: the message stays out of reach of agents and is deleted with the trash.

Both require re-verification within 5 minutes. A human can also add the sender to a block list.

EventWhen
message.helda message was stored as held (metadata only) or held later by an optional check
message.releaseda human released it