Output contract
Machine-readable JSON envelopes, exit codes, and retryable semantics.
The CLI's machine-readable output is a stable contract. Scripts that
consume papermark output should pin to CONTRACT v1; we won't
break it without bumping to v2 and announcing in the
changelog.
The contract is enforced when --json is set or when stdout isn't a
TTY (i.e. when piped). Human-formatted output for interactive use is
not a contract — it can change between minor versions.
Success envelope
{
"ok": true,
"data": { … },
"meta": {
"next_cursor": "doc_…"
}
}| Field | Required | Notes |
|---|---|---|
ok | yes | true for success |
data | yes | Operation-specific payload. For list commands, an array. For single-resource commands, an object. |
meta | only for paginated lists | Currently just next_cursor; pass it back as --cursor to fetch the next page. null when there are no more pages. |
Failure envelope
{
"ok": false,
"error": {
"code": "AUTH_INVALID",
"message": "Token rejected.",
"status": 401,
"retryable": false,
"hint": "Run `papermark login` to refresh credentials.",
"doc_url": "https://papermark.com/docs/cli/output-contract#auth-invalid",
"details": {}
}
}| Field | Required | Notes |
|---|---|---|
ok | yes | false for failure |
error.code | yes | One of the canonical codes below. Safe to switch on. |
error.message | yes | Human-readable. Don't pattern-match. |
error.status | only when the error came from the API | The HTTP status the server returned. |
error.retryable | yes | true if a retry has a reasonable chance of succeeding (rate limits, transient network failures). |
error.hint | optional | One-line suggestion. Surface this to your user. |
error.doc_url | yes | Stable link to this page, anchored to the slug for code. |
error.details | optional | Code-specific structured detail. Schema varies. |
Exit codes
| Exit | When |
|---|---|
0 | Success |
1 | API error (4xx / 5xx other than auth or validation) |
2 | Auth error — no token, invalid token, or insufficient scope |
3 | Validation error — bad input or 422 from the server |
4 | Network error — DNS, connection refused, timeout |
5 | Internal CLI error — file an issue |
Scripts should check the exit code first, then parse JSON. The exit code maps reliably to "should I retry?" / "did the user mess up?" / "did I mess up?" without parsing.
Canonical error codes
Each code has a stable #slug so doc_url always resolves. The
slugs are the lowercase code with underscores replaced by hyphens.
no-token
Exit 2 · Not retryable. No token configured. Set
PAPERMARK_TOKEN, run papermark login, or paste with
papermark auth set --stdin. See
Authentication.
auth-invalid
Exit 2 · API status 401 · Not retryable. A token was provided
but the server rejected it. Either revoked, malformed, or for a
different environment than --api-url points at.
forbidden
Exit 2 · API status 403 · Not retryable. The token is valid but
lacks the scope this command needs. The error details will name
the missing scope.
not-found
Exit 1 · API status 404 · Not retryable. The resource you addressed (document, link, dataroom, visitor) doesn't exist for this team. Confirm the ID and the team.
validation
Exit 3 · API status 422 · Not retryable. Input failed validation,
either in the CLI before sending or by the server. details is a
flattened map of field → error messages. Fix the input; don't retry.
rate-limited
Exit 1 · API status 429 · Retryable. Token's per-minute budget
exceeded. The CLI itself retries with exponential backoff bounded by
X-RateLimit-Reset; you'll only see this code if the retry budget
is exhausted. See API Rate limits.
upstream-5xx
Exit 1 · API status 5xx · Retryable. The server had an internal error. The CLI retries automatically; you'll only see this code if the failure persists.
timeout
Exit 4 · Retryable. The HTTP request didn't get a response in the configured time budget. Could mean transient network trouble or that the API is overloaded. The CLI retries.
network-error
Exit 4 · Retryable. DNS failed, connection refused, TLS
handshake failed. Check connectivity to api.papermark.com.
internal
Exit 5 · Not retryable. A bug inside the CLI itself. Please file an issue with the redacted command and the JSON output.
Stability promise
Within CONTRACT v1:
- Codes are byte-stable. New codes may be added; existing codes won't be renamed.
- Exit codes are stable.
- Field names in the success / failure envelope are stable. New fields may be added.
- Field shapes are stable. A field that was a string stays a string.
If we ever need to break any of the above, we'll bump to CONTRACT v2,
ship papermark 2.0.0 with a --contract v1 opt-in for one minor
release of the new major, then drop v1.