Glossary

Approval gate

An approval gate is a policy layer that turns an agent's send into a pending request a named human must decide, returning a held result instead of a message ID until somebody approves the exact content reviewed.

Definition checked against the implementation on .

A review step is a promise: what the reviewer read is what leaves the building. An approval gate is the machinery that keeps the promise, and most implementations labelled “human in the loop” keep a weaker version of it. They show a preview, record a click, and then send whatever the draft holds when the job runs, which is not necessarily what was on the screen.

Where the gate sits

Two independent things decide whether a send transmits, and they are not alternatives. The first is the credential. A principal holding drafts:submit and not messages:send is always held, with reason code draft_only_principal, even on an inbox configured for direct sending. The second is inbox policy. An approval_mode of always on any policy layer holds everything, however powerful the credential is.

Policy is evaluated at workspace scope, then project, then inbox, and matched reasons accumulate rather than short-circuit, so one held message can carry ["always", "attachments"]. Two further reasons exist that the console cannot set, new_recipient_domain and attachments. They are operator-configured and returned in unmanaged_approval_rules rather than hidden, so a rule you cannot edit is at least a rule you can see.

The held response is HTTP 202, the same status as a successful queue. The body carries "result": "approval_required" where a direct send carries "result": "queued" and a message_id. Code that reads the status and assumes transmission drops every message that needed review, silently. This is the single most expensive detail on this page.

What the gate binds to

An approval binds to content, not to an agent and not to an intention.

When a send is held, the canonical payload, meaning the inbox, action, reply target, normalized To, Cc and Bcc, subject, text, HTML and attachment IDs, is hashed with SHA-256 and the digest is written onto both the draft version row and the approval row. At approval time the two are compared and a mismatch is refused with version_conflict. That is what turns “the reviewer saw it” from an assumption into an invariant.

Approval does not freeze authority either. When a human approves, the system re-reads the current stored grant of the principal that asked, re-checks its project, inbox and drafts:submit scope, re-evaluates the whole policy chain, and separately checks that the approver is a human principal holding approvals:decide there. Revoke the agent’s key while its draft waits and approving fails. Block the recipient while its draft waits and approving fails with policy_denied. The reasoning behind each of these is laid out in how a human approval step should work.

Approving is itself idempotent. The endpoint requires its own Idempotency-Key, namespaced separately from the original send, and the approval is marked consumed in the same transaction, so a double-clicked approve button cannot produce two emails. Approval records expire seven days after they are created.

The events, stated exactly

The event type list declares four: approval.requested, approval.approved, approval.rejected and approval.invalidated. The current build emits approval.requested and approval.rejected. The other two are declared and not yet produced, which is worth knowing before you write a handler that waits on approval.approved and blocks forever. Read the approval record rather than waiting for a callback.

What the gate does not do

It does not review content for quality. The gate decides whether a human sees a message before it leaves, not whether the message is any good, and a reviewer who approves everything has bought nothing but latency.

It does not protect the reviewer’s own browser. The approval record carries both review_html, the server’s sanitized projection, and payload.html, the canonical content the hash was taken over. Only the first is safe to render. A review UI that renders the second has turned its own approval queue into an attack surface.

And it is not a substitute for a narrow grant. An approval queue that fills with requests nobody wanted is a symptom of a credential that was too wide to begin with, which is what approvals and permissions treats as the first control rather than the last.

Read further

approvalscontrolspermissions

All terms · Documentation · Guides