The API is designed for agents and workers that stop, restart, retry, and resume. Reliability comes from preserving the identity and ordering boundaries in the protocol—not from assuming every request runs once.
Keep credentials out of agent context#
Prefer the supported Agent Outbox CLI's secure local credential storage. For a raw HTTP integration, inject the bearer credential at the network boundary. Never place it in source control, request JSON, logs, tracing attributes, or model prompts.
Each credential is scoped to one caller and account. Use separate callers when services need independent identity, revocation, or audit history.
Make send retries deterministic#
Build caller_item_id from the logical work item and reuse it across transport
retries. A repeated send with equivalent normalized content is a success. A
different request under the same pending id returns a conflict instead of
silently changing the review.
If the pending content truly changed, call input/replace with the complete new
request.
Poll with restraint#
output/check is non-mutating and cursor-paginated. Poll when the caller can
actually resume work, use backoff and jitter for recurring workers, and obey
Retry-After when present. input/list uses the same page default and maximum
and is also non-mutating metadata. input/list and input/read share the
output_check_read per-minute limit and consume monthly API request quota.
Full output/read-all pages include every matching canonical input. Choose a
smaller limit when submissions are large so workers do not need to buffer an
unnecessarily large response.
Do not assume the first page represents the entire queue. Continue with the
opaque next_cursor while has_more is true. Never inspect or construct a
cursor yourself.
Drain ready work in batches#
Use output/read-all when a worker is ready to process a page of complete
decisions. It uses the same opaque cursor model as check and marks only returned
items read.
{ "limit": 25, "cursor": null }
Apply the durable-handling sequence below to each output_result_id, then
acknowledge each result individually. If a page reports unavailable_outputs,
retry those ids later; they were not marked read.
Separate read from acknowledgement#
Reading crosses a meaningful boundary: the first successful read disables the person’s undo. It does not remove the result.
Use this order:
- Read the result.
- Check whether
output_result_idhas already been handled. - Perform or durably record the downstream work.
- Commit that work.
- Acknowledge the result.
If the worker stops before step 5, the result can be delivered again and the idempotency check prevents duplicate effects.
Download files before acknowledgement#
Output JSON contains file metadata, never file bytes. Download and durably store or process required bytes before acknowledgement. Verify size and digest where your workflow depends on file integrity, and treat MIME type and filename as untrusted display metadata.
Handle errors by category#
Branch on the stable error.code, not on the message or HTTP status alone.
Several lifecycle conditions intentionally share a status:
{
"ok": false,
"request_id": "req_126",
"correlation_id": "corr_126",
"error": {
"code": "pending_content_conflict",
"message": "A pending item with this caller_item_id has different content."
}
}
400and422mean the request must change. Field errors identify invalid paths without echoing sensitive content.401means the caller credential is absent or unusable. Do not branch on a more specific secret lifecycle state.402can meanupgrade_requiredorbilling_grace_expired; use the code and attached metadata to choose the recovery.404means the live resource is not available to this caller.409 pending_content_conflictmeans useinput/replacefor a real content change.answered_unacknowledgedmeans read and acknowledge the output first.input_not_pendingmeans stop the pending-item operation and reconcile state.429means a rate, quota, storage, or retention limit blocked the operation. HonorRetry-Afterwhen present. Storage and retention limits may have no reset time; delete pending work or read and acknowledge completed work as the structured limit metadata directs instead of waiting blindly.5xxmeans retry only operations that are safe for the caller’s current state, using bounded exponential backoff and jitter.
The CLI distinguishes valid API errors from transport and response-contract
failures. For a mutating data-plane command, inspect write_outcome before
retrying: not_accepted is a definitive rejection, accepted means the server
returned success even though the CLI could not decode its data, and unknown
requires reconciliation. Valid internal_error and temporary_unavailable
envelopes report unknown because a server-side failure can follow a committed
write. A multi-page output read --all reports unknown when an earlier page
was accepted and a later page was rejected or ambiguous, so assume returned
results may already have been marked read and fetch them again by stable result
id or by repeating the read. In particular, retry input send with the same
caller_item_id and content only after checking the caller-scoped input list;
never invent a new id to work around an ambiguous response.
Every JSON response includes request_id and correlation_id. Preserve them in
content-safe operational logs and support reports. The generated
error-code reference lists every public
code and its caller recovery.
Plan for retention#
Unacknowledged results are retained for 14 days. Acknowledged results and their live inputs/files are removed immediately. Persist anything your application needs long term in its own system of record.
Production checklist#
- Stable
caller_item_idvalues survive retries and restarts. output_result_idguards downstream idempotency.- Workers follow every pagination cursor.
- Polling uses backoff, jitter, and
Retry-After. - File bytes are handled before acknowledgement.
- Credentials never enter prompts or logs.
- Request and correlation ids are retained without review content.
- Integration tests exercise replay between durable handling and ack.