BunnyDoc
APIE-Signing API

Errors

Every error the E-Signing API can return, with a stable machine-readable code you can branch on.

Every error response has the same shape. Read error.code — it's a stable, machine-readable value. The message is for humans and may change at any time, so never branch on the message text.

Error shape
{
  "error": {
    "code": "unknown_signer_role",
    "message": "One or more recipient roles do not match this template.",
    "details": { },
    "reference": "0190f3a2-7b6c-7e21-9a3f-2c9b1d4e5f60"
  }
}
FieldAlways presentWhat it's for
codeYesThe stable value to branch on. Every possible value is listed below.
messageYesHuman-readable explanation. May change without notice.
detailsNoStructured context, when we have any — see below.
referenceNoA support id for failures we log server-side (5xx and 422).

details

Present only when there's something useful to say. Two cases are worth handling explicitly:

403 insufficient_scope
{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key is missing the required scope \"envelopes:send\".",
    "details": { "required_scope": "envelopes:send", "granted_scopes": ["templates:read"] }
  }
}
429 rate_limited
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and retry after the indicated interval.",
    "details": { "retry_after": "15 minutes" }
  }
}

Back off on the Retry-After header, not details.retry_after

details.retry_after is a human-readable string ("15 minutes", "42 seconds") meant for log lines and error messages — don't parse it. Every 429 also carries the standard Retry-After response header, which is an integer number of seconds. Use the header for your backoff.

reference

A UUID identifying the server-side log entry for this failure. It appears on the errors we persist (5xx and 422) and is absent on auth and validation errors that carry no server-side log.

Log it. When you report a problem, quoting the reference lets us find the exact failure in one lookup instead of asking you to reproduce it.

Error codes

HTTPerror.codeMeaning
401missing_api_keyNo X-API-Key header on the token request.
401invalid_api_keyThe key is unknown, revoked, expired, or not a live-mode key.
401missing_tokenNo Authorization: Bearer token was sent.
401invalid_tokenThe token is malformed or expired. Request a new one.
401key_revokedThe key behind this token was revoked — its tokens no longer work.
403insufficient_scopeThe key doesn't carry the scope this endpoint requires.
403forbiddenThe key's owner lacks the underlying permission, or the feature (API access, webhooks) isn't in your plan.
403signing_links_disabledThe envelope doesn't share signing links with the sender.
402quota_exceededNo remaining API signature requests (on send).
403quota_exceededA resource limit was reached — the contact limit, or the webhook endpoint limit.
404template_not_foundNo template with that id exists in your company.
404envelope_not_foundNo envelope with that id exists in your company.
404webhook_not_foundNo webhook subscription with that id exists in your company.
422unknown_signer_roleA recipient role doesn't match the template, or a locked role was supplied.
422sender_not_foundsender_email isn't an active user with an eSignature seat in your company.
422sender_fields_incompleteThe template's own sender ("Me") has required fields with no value, so the envelope can't go out.
400 422validation_failedThe request body is malformed, missing a required field, over a length limit, or contains an unknown placeholder or a disguised link.
429rate_limitedToo many requests — back off and retry.
5xxsend_failedThe envelope was created but couldn't be sent, so it was discarded. Nothing was delivered.
500internal_errorSomething went wrong on our side. Safe to retry.

Why some errors are deliberately vague

On the token endpoint, "no such key", "revoked", "expired", and "not live-mode" all return the same 401 body. Likewise, a template in another company and a template that doesn't exist both return 404 template_not_found. This is intentional — a more specific error would let someone probe which ids or keys exist.

Fixing sender_fields_incomplete

This one surprises people, because the request is perfectly valid. It means the template has fields assigned to its own sender role ("Me") that are required and empty — so there is no complete document to send.

The API fills the sender's fields automatically from what's stored on the template; it cannot invent values. Fix it on the template, not in your request:

  • give those sender fields a value on the template, or
  • mark them optional, or
  • remove them.

Then retry the same call.

Handling errors well

  • Branch on error.code, never on message.
  • Retry 5xx, 429, and network errors with backoff, honouring the Retry-After header on a 429. Don't retry other 4xx — the request reached us and was rejected; fix the request instead.
  • Refresh on 401 invalid_token by exchanging your key for a new token, then retry once. If you get key_revoked, the key itself was revoked — load the replacement key rather than retrying.
  • Treat quota_exceeded as a plan signal, not a transient error — it clears when the period resets or your plan is upgraded. Retrying won't help, and on send it means nothing was delivered.
  • send_failed means nothing went out. The envelope is deleted, so a retry creates a fresh one rather than duplicating a delivery.
  • Log reference whenever it's present.

On this page