Errors
Error envelope shape and canonical error codes.
When something goes wrong, the API responds with a 4xx or 5xx HTTP status and a structured JSON body.
Envelope
{
"error": {
"code": "unauthorized",
"message": "Bearer token missing or invalid.",
"doc_url": "https://papermark.com/docs/api/errors#unauthorized",
"details": null
}
}| Field | Type | Notes |
|---|---|---|
code | string | A stable, lowercase identifier from the table below. Safe to switch on. |
message | string | A human-readable sentence. Tone, casing, and wording can change between releases — don't pattern-match on this. |
doc_url | string | A URL pointing at this page, anchored to the slug for code. The slug equals the code, character for character. |
details | object | null | Present on unprocessable_entity (Zod validation errors) and on a few other codes where context helps. Schema varies by code. |
The CLI translates this envelope into its own canonical envelope —
see the CLI output contract. Codes
differ (server uses unauthorized; CLI uses AUTH_INVALID) because
the CLI normalizes across multiple sources (network errors, internal
bugs) that the server can't see.
Canonical codes
Each code below has a stable #slug so the doc_url field always
resolves. Don't change the casing or hyphenation when comparing —
codes are byte-equal across responses.
unauthorized
HTTP 401. The Authorization header is missing, malformed, or
the token is unknown / revoked.
Fix: confirm the header shape (Authorization: Bearer <token>) and
that the token still exists in Settings → API Tokens. If you're
mid-OAuth and your refresh token is also expired, restart the device
flow.
forbidden
HTTP 403. The token is valid, but doesn't have the scope the endpoint requires or isn't authorized to act on the team you're addressing.
Fix: check the endpoint's required scope on its reference page. If the scope is wrong, mint a new token with the right scopes (existing tokens cannot be amended).
bad_request
HTTP 400. The request was syntactically wrong — bad JSON, an unparseable parameter, a missing required field that didn't make it through Zod parsing.
Fix: re-read the endpoint reference. Check JSON syntax. The
message will tell you what specifically is wrong.
not_found
HTTP 404. The resource you addressed (document, link, dataroom, visitor) doesn't exist for this team. Could mean: typo in the ID, the resource was deleted, or the token belongs to a different team than the one that owns the resource.
Fix: confirm the ID with a list call. If you have multiple teams, confirm the token is for the right one.
conflict
HTTP 409. The request would put the resource into an inconsistent state (duplicate name where uniqueness is required, write-after-delete races, etc.).
Fix: read the message for specifics. Often safe to retry after
fetching the current state.
unprocessable_entity
HTTP 422. The request was syntactically fine but semantically
invalid — typically a Zod schema violation. details carries a
flattened map of field → array of error messages.
{
"error": {
"code": "unprocessable_entity",
"message": "Validation failed.",
"doc_url": "https://papermark.com/docs/api/errors#unprocessable-entity",
"details": {
"name": ["Required"],
"expiresAt": ["Invalid ISO 8601 datetime"]
}
}
}Fix: surface the field-level errors to your user. Don't retry until the input changes.
rate_limit_exceeded
HTTP 429. Your token has exceeded its per-minute budget. See Rate limits for the budget and headers.
Fix: back off until X-RateLimit-Reset. The CLI marks this error
retryable and handles backoff automatically.
internal_server_error
HTTP 500. Something on our side went wrong. This is a bug.
Fix: retry once. If it persists, please file an issue with the request shape (token redacted) and approximate timestamp.