# Errors

Errors are data, never a stack trace. Each has a stable code to branch on and a plain message an agent can act on.

## The shape of an error

```json
{"error": {"code": "draft_changed", "message": "This draft changed since that hash. Review it and use the new draft_hash.", "doc_url": "https://docuinked.com/docs/errors#draft_changed"}, "request_id": "req_..."}
```

Quote the `request_id` if you write to support. It is also in the `request-id` response header. Over MCP, the same code and message come back as a tool error.

## Codes

Every error reply carries a `doc_url` that links to its entry here.

### invalid_request

400. A field is missing, unknown or wrong. Fix the input. The message names the field.

### invalid_json

400. The body isn't JSON. Fix the body.

### pdf_fetch_failed

400. We couldn't download `pdf_url`. Check that the link is public https.

### draft_hash_required

400. An agent key left out `draft_hash`. Pass the hash from `create_document`.

### unauthorized

401. The key is missing, wrong or revoked. Send `Authorization: Bearer ...`.

### send_not_allowed

403. This key may only prepare drafts. A person sends from the review link.

### agent_cannot_sign

403. Agents can't get live signing links. The signer uses the link in their email.

### test_only

403. That tool needs a test key. Use a test key.

### not_found

404. No such object in this account and mode. Check the ID and key mode.

### draft_changed

409. The draft no longer matches your hash. Review it and use the new hash.

### not_completed

409. The document has no signed PDF yet. Wait for `document.completed`.

### not_sent

409. The document hasn't been sent. Send it first.

### not_waiting

409. Only a waiting document can be reminded. Check the status.

### request_in_progress

409. That `Idempotency-Key` is still running. Wait, then retry with the same key.

### pdf_too_large

413. The PDF is over 25 MB. Send a smaller file.

### anchor_not_found

422. No text matched your field anchor. Check the wording, or use page, x and y.

### rate_limited

429. Over 120 requests a minute. Wait for `Retry-After`, then retry.

### internal

500. Something failed on our side. Retry with the same `Idempotency-Key`.

## Safe retries

Writes take an `Idempotency-Key`. A repeat with the same key returns the first result and does no new work. If the first try failed, the key is released, so a retry runs again. That makes any write safe to retry, as long as you reuse the key. Build the key from something stable, such as a row ID or an order number, not a random value made on each attempt.

## Common questions

### Should my agent retry a 403?

No. A 403 means a rule refused the action, and a retry will get the same answer. Show it to a person.

### Does a retry with an Idempotency-Key send the document twice?

No. The second call gets the stored result of the first and nothing new happens. Two calls at the same moment are safe too: the second gets `request_in_progress`.

### Why do I get not_found for a document I can see in the app?

Test and live are separate. A test key can't see live documents, and a live key can't see test ones. Check which key you are using.
