Submit a reviewer decision (approve/edit/reject/escalate)
POST /v1/reviews/{id}/decision
The reviewer’s decision on a draft it holds (D5; §9). approve/edit → the PLATFORM performs the authenticated send using the COMPOSER’s inbox credentials (the reviewer holds review:act, NEVER mailbox:send on an inbox it doesn’t own; the credential boundary); send_path becomes reviewer_approved and terminal success is written post-delivery only. reject → the draft returns to the composer (needs_review) and hop_count is incremented. escalate → the draft goes to the human queue with an escalation_reason. revision + version are the optimistic CAS; a mismatch is 409 STALE with NO mutation (the human/composer moved the draft; the human always wins, D17). The two circuit breakers (hop_count ≥ max_hops, or the hard review_deadline) FORCE a reject to the human regardless of intent; forced_by_breaker names the tripped breaker. An unknown action is 400; a cross-tenant id is 404; a non-reviewer is 403. $0 LLM; the reviewer agent judged; we route, send, and enforce the breakers.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”A reviewer decision. action is approve|edit|reject|escalate. revision + version are the optimistic CAS (a mismatch is 409 STALE with NO mutation; the human always wins, D17). subject/body carry the edited content for the edit action. feedback is the reviewer’s note (reject: the rule-birth signal; escalate: the human-facing escalation reason).
object
The revision you decided against (PRIMARY CAS).
Optional row-version CAS (defense in depth).
Edited subject (edit action).
Edited body text (edit action).
Reviewer note (reject: rule-birth signal; escalate: human-facing reason).
Responses
Section titled “ Responses ”Ok (sent via the composer’s creds, or returned to the human queue)
The reviewer-decision outcome. kind=sent when the platform sent with the COMPOSER’s creds (approve/edit); kind=sent_to_human when the draft returned to the human queue (reject/escalate, or a reject forced to the human by a circuit breaker). forced_by_breaker names the tripped breaker when one overrode the intent.
object
A review request (rr_…); the server-owned pre-send record of a message under the Review Loop. Carries intent, the current proposed draft, category, state machine fields, and (once sent) the sent body + diff.
object
object
True once this review will never move again; sent, auto_sent, cancelled OR failed. It is the definitive per-review “am I done?” answer and the poll-side companion to the terminal review events; use it after a restart when your event cursor is gone. failed is included deliberately: nothing re-approves a failed review, so treating it as open means waiting forever.
Vendor-scrubbed delivery failure, present on a failed review.
How the message was released, once sent.
True iff the platform sent with the composer’s creds.
The delivered message id when sent.
The thread id when sent on the reply path.
True iff the draft returned to the human queue.
The tripped circuit breaker (max_hops_reached | review_deadline_passed) when one forced the human.
Invalid request.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeMissing or invalid credential.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeAuthenticated but lacking the required scope, or out of quota.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeResource not found.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeA Review Loop conflict, as problem+json. Branch on code, NOT on the 409 status; the four codes demand opposite behavior. stale: the revision/version you named is no longer current (a human moved the draft) and NOTHING was mutated; errors[] carries the current state, revision and version, so re-apply your change on top and resubmit with the new parent_revision (retry, bounded to ~3). born_stale: the redraft was built against an older rule high-water; re-read the rules and resubmit, or restamp_review if nothing genuinely changed (at most one retry per high-water). wrong_state: this VERB is illegal from the current state but the draft is still live; NEVER retry the same verb; errors[] repeats an allowed_action entry per verb that IS legal right now. terminal: the review is already sent/auto_sent/cancelled; nothing will ever succeed, stop retrying, and a front_run_next review event carries the outcome. send_needs_reconciliation: a prior attempt is unconfirmed and parked for recover-by-Message-ID; do NOT resend, poll the review.
RFC-9457 problem+json error body. code is a closed machine enum clients switch on; type is a dereferenceable URI under https://extrovert.dev/problems/. Served as application/problem+json.
object
The CLOSED machine code. The Review Loop members split what used to be a single opaque conflict, because an agent must take a DIFFERENT action on each: stale (the revision/version you named is no longer current; nothing was mutated; re-read, re-apply, resubmit; retryable, bounded) and born_stale (built against an older rule high-water; re-read the rules or restamp_review; at most one retry per high-water) are the ONLY retryable 409s. wrong_state means this VERB is illegal from the current state while the draft is still live; never retry the same verb, read the state and the repeated allowed_action hints in errors[] and pick a legal one. terminal means the review is already sent/auto_sent/cancelled and nothing will EVER succeed; stop, and drain your review events for the outcome. send_needs_reconciliation means a prior send is unconfirmed and parked. Do not resend. Poll instead. unavailable (503) is the retryable fail-closed answer when a dependency could not be read; it carries Retry-After and is distinct from not_configured, which is permanent for this deployment.