Output & errors

The JSON envelope, the stdout/stderr split, exit codes, and the stable error codes the Repost CLI guarantees to programs.

Agents consume JSON, not terminal tables. This page is the contract every other page in this section relies on: how to turn JSON on, the envelope shape, where output goes, and the stable codes the CLI guarantees. Read it once and branch on it. (Per-command flags live in the command reference.)

Get machine-readable output

Pass --json explicitly in scripts. The CLI also switches to JSON automatically when stdout is not a terminal, but an explicit flag keeps logs auditable.

TriggerResult
--jsonJSON on stdout.
--output json / --output tableForces JSON on, or human tables on.
Non-TTY stdout (piped or redirected)JSON automatically.

Precedence: --output wins, then --json, then the TTY check (non-terminal → JSON, terminal → human tables).

Data goes to stdout. Diagnostics, progress, and errors go to stderr. Parse stdout; never scrape stderr for results.

Read the success envelope

Almost every command returns the same two-field envelope. (The few that don't are listed under envelope exceptions.)

schemastringrequired

Stable identifier for the output shape, for example repost.events.search/v1. Branch on this. It is versioned and never changes meaning within a major version.

dataobjectrequired

The command result. Its shape is determined by schema.

repost events search 'method:POST' --bucket stripe-prod --since 15m --limit 2 --json

Guard on schema, then read data. The same result, parsed two ways. This is your consuming code, not Repost internals:

result=$(repost events search 'method:POST' --bucket stripe-prod --since 15m --json)
 
# Fail loudly if the shape isn't the one you built against
echo "$result" | jq -e '.schema == "repost.events.search/v1"' >/dev/null
 
# Read fields; take the next page only when there is one
echo "$result" | jq -r '.data.items[] | select(.response_status >= 500) | .id'
next=$(echo "$result" | jq -r 'if .data.has_more then .data.next_cursor else empty end')

Event payloads in data are redacted by default: credential and signature headers and secret-like JSON keys are masked, but PII is not. See Transcript safety for exactly what is and isn't removed.

Know the envelope exceptions

A few commands do not use the {schema, data} envelope. An agent that assumes a schema field everywhere will break on these.

CommandOutput
expectA single bare JSON object (no envelope, no schema). Always JSON, even without --json.
tailNDJSON: one bare JSON object per matching event, one per line. Always JSON.
capabilitiesTop-level { schema, version, commands }. Always JSON.
events get --body-onlyThe raw request body, no envelope.
docs schema{ schema, command, output_schema, output_mode, envelope, data_shape? }.
docs agent / docs searchMarkdown text, not JSON.

Wait commands (expect, tail) are covered in Wait for events. Discovery commands (capabilities, docs schema) are covered in Discovery.

Handle errors

When JSON is enabled, failures are structured: the error object goes to stderr and the process exits non-zero.

error.codestringrequired

Stable machine code. Branch on this, never on the message.

error.messagestringrequired

Human-readable explanation. For people, not programs.

error.exit_codeintegerrequired

Mirrors the process exit status (see the table below).

error.hintstring

Suggested next step, when one applies.

error.docsstring

Always present; defaults to repost docs agent.

error.missing_scopestring

The exact scope to request. Present only on forbidden_scope.

error.token_creation_urlstring

Where to mint a token with the missing scope. Present only on forbidden_scope.

repost replay evt_01JZ8V1 --bucket stripe-prod --forwarder prod-api --yes --json

Decide from error.code and exit_code, never from the human message:

# Capture stdout (data) and stderr (the error envelope) separately
if out=$(repost replay evt_01JZ8V1 --bucket stripe-prod --forwarder prod-api --yes --json 2>err.json); then
  echo "$out" | jq '.data'
else
  case "$(jq -r '.error.code' err.json)" in
    rate_limited|upstream_unavailable) action=retry ;;   # the only codes worth retrying
    forbidden_scope)                   action=request_scope ;;
    partial_failure)                   action=inspect ;;
    *)                                 action=fail ;;
  esac
  exit "$(jq -r '.error.exit_code' err.json)"
fi
error.codeexitWhenAgent response
validation_failed2Bad input: a client-side flag/argument check or HTTP 400/422Fix the command. Do not retry unchanged.
active_org_required2A user token can see zero or multiple licensed organizations, so a mutation has no unambiguous orgSelect one with repost config org set <slug> or --org <slug>, or use an organization token.
unauthorized3Invalid or missing token (401)Refresh or replace REPOST_TOKEN.
forbidden_scope3Token lacks a scope (403)Request missing_scope or take a read-only path.
not_found4Unknown id/bucket/forwarder (404)Verify the id and active organization.
quota_exceeded5Plan limit reached (402)Not transient. Stop or reduce scope.
rate_limited6Server limit (429)Back off, then retry.
timeout7expect / replay wait deadline, or promote setup browser pairing deadline. Not tail, which treats --max-wait as the end of the capture and exits 0.Report the unmet wait. Inspect state before retrying.
partial_failure8health --fail-on breach; replay wait with failuresInspect details before declaring success.
conflict1State changed under you (409/412)Refresh state, then retry.
upstream_unavailable1Service error / unknown (5xx)Retry with backoff, then report instability.

validation_failed, active_org_required, forbidden_scope, and quota_exceeded are terminal. Retrying the same command will fail the same way. Only rate_limited and upstream_unavailable are worth retrying.

Pin to a contract version

v1Current contract

Every command schema is repost.<area>/v1. The success envelope is {schema, data}; the failure envelope is {error}. Exit codes 0–8 are stable. When a payload changes incompatibly, the schema suffix bumps (/v2) and both are served during migration, so an agent can pin to the version it was built against.

For agents

Output contract (quick reference)

  • Enable JSON: --json, or --output json, or pipe stdout (non-TTY auto-enables JSON). --output wins over --json.
  • Success: {"schema":"repost.<area>/v1","data":{...}} on stdout.
  • Failure: {"error":{"code","message","exit_code","hint?","docs","missing_scope?","token_creation_url?"}} on stderr, non-zero exit.
  • Exceptions (no {schema,data}): expect (bare object), tail (NDJSON), capabilities ({schema,version,commands}), events get --body-only (raw body), docs schema ({schema,command,output_schema,output_mode,envelope}), docs agent/docs search (markdown).
  • Exit codes: 0 ok · 2 validation_failed/active_org_required · 3 unauthorized/forbidden_scope · 4 not_found · 5 quota_exceeded · 6 rate_limited · 7 timeout · 8 partial_failure · 1 conflict/upstream_unavailable.
  • Branch on error.code and exit_code, never on message. Retry only rate_limited and upstream_unavailable. Output redacts credentials, not PII.

Continue