Forward a message to new recipients
POST /v1/inboxes/{inbox_id}/messages/{id}/forward
Forward an existing message to new recipients. The subject and the quoted parent are materialized at SUBMIT time, so a queued forward shows the human the exact forwarded content and an approved forward delivers the reviewer’s edit rather than a body re-derived from the live parent. A forward carries no In-Reply-To: its recipients were never part of the parent conversation.
The resolved account/inbox review policy is authoritative on every send. Omitting fields does not bypass it. Read effective_review_policy on GET /v1/inboxes/{inbox_id} to know which branch you are on:
require_review(the default for every account): a request WITHOUT anintentis rejected with422 intent_required. Nothing is sent or queued. A request WITH an intent returns202 queued_for_review. Then monitor the review until you receive asentorsend_failedreview event.allow_direct: a bare request (no mode/intent/category_id) still sends immediately and returns the legacy202body, now plusreview_id. Supplying an intent ormode: reviewqueues it for a human.auto_send_graduated: a categorized message that clears the graduation gates auto-sends; everything else is queued.
Contact lists, list-unsubscribe suppression and the billing quota are all enforced at SUBMIT time, so a rejected recipient fails fast rather than after a human has already approved the draft. Forward uses the same review policy as send. Any other behavior would create a bypass, especially because it exfiltrates a received thread.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Opaque inbox id (the canonical path key). The inbox’s email address is also accepted here as a within-project alias. Treat the id as opaque.
Opaque message id (msg_...). Encodes its owning inbox.
Request Body required
Section titled “Request Body required ”object
Additional recipients on the forward. Screened by the same pre-flight as to.
Blind recipients on the forward. Never rendered as a header.
Optional note prepended to the forwarded content.
Accepted and IGNORED. The forwarded content is a plain-text quote of the parent message; emitting an HTML alternative would show HTML-capable clients the note WITHOUT the forwarded thread.
Review Loop per-send assertion (see SendRequest.mode). A forward is an outbound message to arbitrary NEW recipients that quotes an inbound thread, so it is governed by the same review policy as send.
The agent’s “for the human reviewer” summary. summary is REQUIRED when the resolved mode is review (else the submit is 422).
object
Free-text intent summary (who/what/why).
Structured intent payload.
object
Opaque category id (cat_…) matched from the registry.
Agent-supplied confidence (0..1) in the category match (see SendRequest.category_confidence).
Opaque token from a fresh, unfiltered GET /v1/rules for this agent, project, and category.
DEPRECATED body-level alias for the Idempotency-Key header (see SendRequest.idempotency_key). Send the header instead.
Responses
Section titled “ Responses ”Sent immediately (policy-permitted direct or graduated path).
A Review Loop submit that was sent immediately (policy-permitted direct or graduated path). kind is always “sent”.
object
object
The review row that governed this send (ADDITIVE).
object
Opaque review id (rr_…).
Terminal state (sent | auto_sent).
Accepted; either queued for review or accepted for delivery.
A Review Loop submit that was parked for human review.
object
object
Opaque review id (rr_…).
Current review state (e.g. needs_review).
The mode after the policy resolved the agent’s assertion.
The outcome of a reply or forward.
object
Opaque review id (rr_…) for the review row that governed this send. ADDITIVE. Every agent-plane send now creates one, so an agent that crashes after issuing the request can still call GET /v1/reviews/{id} and read closed / sent_message_id instead of guessing whether the message went out.
The send/reply/forward is malformed or fails validation. Decode and validation failures are problem+json (code: bad_request) and NAME the offending field; an unknown key lists the accepted set, and supplying both text and the deprecated body with DIFFERENT content is errors[0].code = conflicting_alias (there is no safe guess, so the request is refused rather than relayed with the wrong bytes). A few pre-existing domain rejections reached through this path (e.g. domain_not_allowed) still carry the legacy {error, message} envelope.
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.
object
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 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.
object
The send/reply/forward is well-formed but cannot be processed. Always problem+json; branch on code. intent_required (D3): the resolved review mode is review and no intent summary was supplied; nothing was sent and nothing was queued, and detail names the field to add. recipient_suppressed: one or more recipients have a list-unsubscribe opt-out, and errors[] names ONLY the suppressed addresses; never the suppression scope or origin; so the agent can retry without them.
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.
object
A dependency could not be read, so the request was failed CLOSED rather than served on a guess (Problem code = unavailable). Retryable; see Retry-After. Distinct from not_configured, which is permanent for this deployment.
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.
object
Headers
Section titled “Headers ”Seconds to wait before retrying.