# Errors

> Every error is a JSON object with one message, written so you can show it to a person. These are the status codes and what to do about each.

When a call fails, the response has a `4xx` status and a JSON body with a single `error` message:

```json
{ "error": "Sending this pact uses 1 document (one for every 3 signers) and none are left. Buy a document pack or upgrade on the Billing page." }
```

The message says what went wrong in a sentence you can show to the person using your app. Branch on the status code, not on the wording, which may change.

## Status codes

| Status | Meaning | What to do |
|---|---|---|
| `400` Bad Request | The request was not valid: a missing field, a bad value, a document Sigil cannot read, or an action the pact's state does not allow. | Fix the request. The message says what is wrong. |
| `401` Unauthorized | The API key is missing, mistyped or revoked. | Check the `Authorization` header and the key. See [Authentication](/authentication). |
| `403` Forbidden | The key is valid, but the account's plan does not include API access. | Upgrade to Pro or Business. |
| `404` Not Found | Nothing with that id belongs to your account, or a file is not ready yet (a signed PDF before the pact is sealed). | Check the id. Ids from another account answer `404` too. |
| `409` Conflict | A plan limit: not enough documents left to send. Also deleting a pact that is not a draft. | Buy a document pack, upgrade, or void instead of deleting. |
| `413` Content Too Large | The upload is well over 10 MB and was refused before it was read. | Send a smaller file. |
| `5xx` | Something failed on Sigil's side. | Retry later (see below). |

## Common messages

| Message | Cause |
|---|---|
| `No glyphs were found. …` | The document has no glyphs. Type them in, then upload it again. See [Glyphs](/concepts/glyphs). |
| `This PDF has no selectable text, so it has no glyphs. …` | The PDF is a scan or an image. Export it from the original document instead. |
| `The document has fields for signer 2, but only 1 signer is listed. …` | The pact lists fewer signers than the document's fields need. |
| `Signer 2 has nowhere to sign: …` | A signer was listed with no field in the document for them. |
| `That document has no fields yet. …` | The template was uploaded on the website without glyphs and its fields haven't been placed. Place them in the field editor, or upload a version with glyphs. |
| `"Sam Ortiz" needs a valid email address.` | A recipient's email address is missing or invalid. |
| `avery@example.com is listed twice.` | Each address can appear once per pact. |
| `'sign.example.com' is not one of your verified domains.` | `domain_host` must be a domain verified on your account. |
| `Only a draft can be sent.` | The pact was already sent. |
| `This pact is already being sent, or has been.` | Two send calls raced; the first one won. |
| `Only a pact that is out for signature can be voided.` | It is a draft (delete it instead) or already finished. |
| `Sending this pact uses … and none are left. …` | Not enough documents. See [Documents and plans](/concepts/documents). |

## Retrying

A `4xx` will fail the same way again until the request changes, so do not retry it unchanged. For a `5xx` or a network failure:

- **Reads are safe to retry.** `GET` calls change nothing.
- **Sending is protected.** A pact can only be sent once: a repeated send answers `400` and charges nothing more. Get the pact to see whether the first attempt went through.
- **Creating is not.** A retried `POST /pacts` can create a second pact, and with `"send": true` send it twice. Before retrying, [list your pacts](/api-reference/pacts/list-pacts) to see whether the first one exists.

Wait a little longer between each retry, and give up after a few attempts.

## MCP errors

Through the MCP server, a failed tool call returns a result with `isError: true` whose text is `An error occurred invoking '<tool>': ` followed by the same message, so the assistant can read it, explain it, and often fix the call itself. An invalid key or plan answers `401` or `403` before any tool runs, with the same JSON body as the API.
