Every error, and what to do about it.

One error shape covers every route, and every refusal links here to its own entry. Each code below says what it means, what usually causes it, and whether sending the same request again is worth your time.

What arrives in the body

Six fields, on every route, whatever went wrong. The HTTP status carries the same meaning as the code, so you can branch on either. A partial failure comes back as a refusal rather than as a 200 with the failures buried in it.

  • codeOne of the values below. Stable, and safe to branch on.
  • messageA sentence for whoever is reading the log.
  • requestIdEchoed on every response, so a support conversation can name one request.
  • docsUrlA link to this page, at the anchor for that code.
  • retryableWhether the identical request may be sent again unchanged.
  • detailsEvery problem with the request, each with a JSON pointer to the field.

A refused create, in full

{
  "code": "validation_failed",
  "message": "the agreement was refused",
  "requestId": "req_8mQ2v6Kx",
  "docsUrl": "/docs/errors#validation_failed",
  "retryable": false,
  "details": [
    {
      "path": "/parties/1/email",
      "code": "invalid",
      "message": "an email address is required"
    }
  ]
}

Following docsUrl lands on the entry for that code on this page.

The 13 codes

bad_request

HTTP 400Change the request first

The request could not be read as a request.

What causes it
A body that is not valid JSON, a query parameter of the wrong shape, or a pagination cursor that has been edited or built by hand. Cursors are opaque and are only valid exactly as they were handed to you.
What to do
Fix the request before sending it again. The message names the part that could not be read, and details carries a JSON pointer to it when the problem is inside the body.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

validation_failed

HTTP 422Change the request first

The request was understood, and its contents were refused.

What causes it
A party with no email address, a field placed on a page the document does not have, a conditional rule pointing at a field key nothing defines, or a required value left empty.
What to do
Read the whole details array rather than the first entry. Every problem with the request is listed, because somebody who made three mistakes should be told about three.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

unauthenticated

HTTP 401Change the request first

The request carried no usable key.

What causes it
A missing Authorization header, a header that is not Bearer pb_..., or a key that has been revoked or has expired. Revoking a person’s membership revokes their keys with it.
What to do
Check the header, then check the key is still live on the API keys page. A revoked key never comes back, so issue a new one.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

forbidden

HTTP 403Change the request first

The key was recognised and the action was refused.

What causes it
Three separate facts arrive under this code, and the message says which one is in play: the key is missing a scope the route requires, the account the key acts as is missing the permission, or the workspace has no active plan. Both the scope and the permission have to pass, so issuing a key never escalates whoever holds it.
What to do
Read the message. A key’s scopes are fixed when it is issued, so a missing scope means issuing another key. A missing permission is an administrator’s to grant. When the plan is the cause, details carries the reason and where to choose one.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

not_found

HTTP 404Change the request first

No such record in this workspace.

What causes it
A mistyped id, a record that has been deleted, or an id belonging to a different workspace. Every lookup is scoped to the workspace the key acts in.
What to do
Check the id against a list call. An id that exists in somebody else’s workspace still reads as missing here, which is deliberate: whether a record exists elsewhere is not something an unrelated key gets to learn.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

conflict

HTTP 409Change the request first

The record exists, and its current state does not allow what was asked.

What causes it
Sending an agreement that has already gone out, correcting one that has finished, or minting a signing session for a party your own application does not authenticate.
What to do
Read the agreement first and act on the status you find. The transition table is fixed, so the same call against the same status will keep being refused.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

locked

HTTP 423Change the request first

Somebody else holds the correction lock on this agreement.

What causes it
Two people, or a person and an integration, correcting one agreement at the same time. A lock carries an owner and an expiry, and reading an agreement never takes one.
What to do
Wait for the expiry, or have the holder release it, then send the correction again. The identical request now will meet the same lock.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

limit_exceeded

HTTP 422Change the request first

A published limit was reached.

What causes it
Documents on one agreement, pages or bytes in one document, parties, or fields. Every limit is published in the OpenAPI document under x-limits, so you can check against it before you send.
What to do
Split the work across more than one agreement, or send less. The message names the limit and the number that went past it.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

rate_limited

HTTP 429Worth retrying unchanged

This key has made more requests this hour than it is allowed.

What causes it
A backfill, a retry loop with no wait in it, or several workers sharing one key. The limit counts per key, so a busy import and your live traffic can each have their own.
What to do
Wait the number of seconds in the Retry-After header, then send the identical request again.
Retrying unchanged
Worth your time. The body carries retryable: true, so back off and send the same request again.

unsupported_media_type

HTTP 415Change the request first

The body arrived under a content type the route does not read.

What causes it
A client that defaults to text/plain or a form encoding, or a proxy that rewrites the header on the way through.
What to do
Send JSON as application/json.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

payload_too_large

HTTP 413Change the request first

The body is bigger than the route accepts.

What causes it
A document inlined into a request, or a field list far past the published maximum.
What to do
Check the byte and page limits under x-limits and send less. Document bytes go through upload rather than inside an agreement payload.
Retrying unchanged
Returns this same error. The body carries retryable: false, so change something before you send it again.

internal_error

HTTP 500Worth retrying unchanged

Something failed on our side.

What causes it
A fault in our code or in something we depend on. Your request did not cause it.
What to do
Retry with exponential backoff. If it keeps happening, quote the requestId from the body when you get in touch; it names the one request in our logs.
Retrying unchanged
Worth your time. The body carries retryable: true, so back off and send the same request again.

service_unavailable

HTTP 503Worth retrying unchanged

The service could not take the request just now.

What causes it
A dependency that is unavailable, or a deployment in progress.
What to do
Retry with exponential backoff and jitter. The identical request is expected to go through once capacity is back.
Retrying unchanged
Worth your time. The body carries retryable: true, so back off and send the same request again.

Reporting one

Every response carries a request id, whether it succeeded or not. Quote it and we can find the one request you mean rather than asking you to describe it.

Error codes