GuidesConventions
Errors
Every error is an RFC 9457 problem details object (application/problem+json). Branch on code; show people detail.
The error object
| Field | Type | Description |
|---|---|---|
type | string (URI) | A link to this page for the code: https://api.skanything.io/v1/docs/errors#<code>. |
title | string | The HTTP status in words, like Forbidden. |
status | integer | The HTTP status code, repeated for convenience. |
code | string | Stable and machine-readable. Branch on this. |
detail | string | What went wrong, in plain English. It can change — don’t parse it. |
request_id | string | The id of this request, like req_7c1e5a9d3f2b4e8a6c0d9f1b. Same as the SignAnything-Request-Id header. |
required_scope | string | Only on 403 insufficient_scope: the scope the key needs. |
| Other members | varies | Some codes add what they’re about: feature (402s), pages, label, max_bytes, used_bytes and limit_bytes, received, max_days. Each is listed with its code below. |
errors | null | Reserved for field-by-field problems; not filled today. Errors from the API’s front door (keys, scopes, rate limits) send it as null; the others leave it out. |
POST /v1/envelopes
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
SignAnything-Request-Id: req_7c1e5a9d3f2b4e8a6c0d9f1b
{
"type": "https://api.skanything.io/v1/docs/errors#recipient_limit",
"title": "Unprocessable Entity",
"status": 422,
"code": "recipient_limit",
"detail": "An envelope on your plan can have up to 5 recipients.",
"request_id": "req_7c1e5a9d3f2b4e8a6c0d9f1b"
}Which error you get first
Checks run in a fixed order and stop at the first failure: key → IP allowlist → route → test mode → scope → rate limit. So a test key calling /v1/files without files:read gets test_mode_not_supported, not insufficient_scope. Calls stopped before the rate limit don’t count toward it.
Request ids
Every response — success or error — has a SignAnything-Request-Id header. Log it with your own request. If you contact support about a call, quote the id and we can find exactly what happened.
Error codes
Each code has its own anchor on this page, which is where type points.
| Status | Code | What it means · what to do |
|---|---|---|
| Keys and access | ||
| 400 | api_key_in_query | The key was sent in the URL. Send it in the Authorization header, and revoke that key — URLs end up in logs. |
| 401 | missing_api_key | No Authorization: Bearer … header. Send your key in it. |
| 401 | invalid_api_key | The key isn’t one we know: mistyped, cut short, or not a SignAnything key. Copy it again from your secret store. |
| 401 | api_key_revoked | The key was revoked (it stops working within 30 seconds). Create a new key. |
| 401 | api_key_expired | The key passed its expiry date. Create a new key. |
| 401 | account_suspended | The account is suspended, so its keys don’t work. Contact SignAnything support. |
| 403 | ip_not_allowed | The key has an IP allowlist and this address isn’t on it. Call from an allowed address, or create a key that allows it. |
| 403 | test_mode_not_supported | A test key called a KeepAnything route (files, folders, uploads, the change feed, search, grants, links, file requests). They have no test mode. Use a live key. |
| 403 | insufficient_scope | The key doesn’t have the scope this call needs; required_scope names it. Create a key with that scope. |
| 403 | forbidden | The key’s user isn’t allowed to do this to this item (for example, files need Contribute or Manage access, or your company turned links off). detail says why. |
| Requests | ||
| 404 | not_found | Nothing with this id — or it belongs to the other mode, or there’s no such endpoint. Check the id, the path and the key’s prefix. |
| 400 · 422 | invalid_request | 400: the body isn’t JSON, is empty, or wasn’t sent as application/json. 422 on POST /v1/envelopes: send: true without a template_id. |
| 400 | invalid_cursor | The cursor isn’t one we gave you. Start the list again without it. |
| 400 | invalid_idempotency_key | Idempotency-Key is longer than 64 characters. |
| 409 | idempotency_key_reused | This Idempotency-Key was already used with a different body, or by the other mode. Use a new key for a new envelope. Also when two calls with the same key race: try again. |
| 429 | rate_limited | Too many requests this minute. Wait for Retry-After seconds, then try again. |
| Envelopes, recipients and documents | ||
| 409 | not_draft | Only drafts can be changed: subject, message, expiry, documents, recipients and fields. |
| 422 | invalid_expiry | expires_at isn’t in the future (on create, update or send), or a link’s or file request’s expiry has passed. |
| 422 | too_many_labels | An envelope can have up to 10 labels. |
| 422 | label_too_long | A label is over 40 characters; label names it. |
| 422 | invalid_recipient | A recipient isn’t valid: no name, a bad email or mobile number, an unknown type, delivery, language or identity check, a PIN that isn’t 4–8 digits, or a text code and a PIN together. detail says which. |
| 422 | recipient_limit | Too many recipients for the plan: 99 on Business and Enterprise, 5 on Personal. |
| 422 | sms_recipient_limit | More recipients get their link by text than the plan allows (10 on Business and Enterprise). |
| 422 | empty_file | The uploaded document is empty. |
| 422 | unsupported_type | Upload a PDF, Word (.docx, .doc) or Excel (.xlsx, .xls) file. |
| 422 · 413 | file_too_large | 422: a document is over 50 MB. 413 on POST /v1/uploads: the file is over the plan’s per-file limit (max_bytes). A request over 60 MB is refused with a bare 413 before it reaches us. |
| 422 | too_many_documents | An envelope can hold up to 100 documents. |
| 422 | envelope_too_large | The envelope’s documents would add up to more than 200 MB. |
| 422 | too_many_pages | A document has more than 2,000 pages; pages says how many it has. |
| 422 | password_protected | The file is password-protected. Remove the password and upload it again. |
| 422 | not_a_pdf | The file couldn’t be read as a PDF. |
| 422 | conversion_failed | A Word or Excel file couldn’t be converted to PDF. Save it as a PDF and upload that. |
| 422 | invalid_field | A field doesn’t fit: an unknown type, a recipient or document not on the envelope, a page that isn’t in the document, a bad size or position, or more than 2,000 fields. |
| 409 | scan_pending | The virus check hasn’t finished. Try again in a moment. |
| 451 · 409 | document_blocked | A document was blocked by the virus check, or isn’t what its name says. 451 when you download it; 409 when you send, or use a template that has it. Replace or remove it. |
| 409 | legal_hold | The item is on legal hold, so it can’t be deleted. |
| 409 | in_trash | The envelope or folder is already in Trash. Restore it first. |
| 409 | not_deletable | Only drafts and declined envelopes can be deleted. |
| 409 | correcting | Signing is paused while the envelope is being corrected in the app. |
| Sending, signing and voiding | ||
| 409 | already_sent | The envelope isn’t a draft any more. |
| 422 | no_documents | Add a document before sending. |
| 422 | no_signers | Add at least one signer before sending. |
| 422 | fields_not_ready | A field isn’t ready to send; detail says which. |
| 409 | not_signable | A signing URL can only be made for a recipient whose turn it is on a sent envelope. |
| 422 | reason_required | Voiding needs a reason. |
| 409 | not_voidable | Only sent, in-progress and declined envelopes can be voided. |
| 409 | not_remindable | There’s no one to remind: the envelope isn’t out for signing, or no one is waiting. |
| 409 | reminded_recently | Everyone waiting was reminded within the last hour. |
| 409 | not_completed | The signed PDF and certificate are ready once everyone has signed. |
| 409 | not_combined | An envelope with several documents completed before 4 November 2026 has no single signed PDF. Download each document with document_id, and the certificate. |
| Plans and billing | ||
| 402 | plan_required | Sending with a live key needs a paid plan. |
| 402 | billing_suspended | Sending is paused until the account’s payment problem is fixed. |
| 402 | envelope_allowance_exhausted | This period’s envelopes are used up. Buy a pack in the app to keep sending. |
| 402 | plan_feature_required | The plan doesn’t include this recipient option; feature names it (sms, access_code). |
| 402 | sms_allowance_exhausted | This period’s texts are used up; feature is sms. |
| 402 | feature_not_included | The plan doesn’t include this: templates, file endpoints on the API, or a sharing option. |
| 503 | billing_unavailable | We couldn’t check the plan just now. Try again shortly. |
| 503 | settings_unavailable | The company’s settings couldn’t be read just now. Try again shortly. |
| Templates | ||
| 404 | not_published | No published template with that id. Publish it in the app first. |
| 422 | missing_data | Required data keys are missing; detail names them. |
| 422 | invalid_data | A Yes / No tag got something other than true or false. |
| 422 | unknown_role | A recipient names a role the template doesn’t have. |
| 422 | duplicate_role | The same role is given twice. |
| 422 | missing_recipient | A role the template needs has no recipient. |
| 422 | invalid_signing_order | signing_order is parallel, serial or groups. |
| 422 | invalid_document | The preview’s document number is out of range. |
| 403 | template_locked | The template locks its fields, so they can’t be added to, changed or removed on its envelopes. |
| 503 | templates_unavailable | Templates aren’t available just now. |
| Webhooks | ||
| 422 | invalid_url | The endpoint URL must be https://, up to 2,048 characters, with no user name or password, and reach a public address. |
| 422 | invalid_event_filter | event_filter names an event we don’t send. |
| 422 | too_many_webhooks | An account can have up to 20 endpoints. |
| 422 | invalid_event_type | Send test: type isn’t an event we send. |
| 422 | event_not_subscribed | Send test: the endpoint doesn’t receive that event. Send ping or one of its events. |
| Files · KeepAnything | ||
| 422 | name_not_allowed | The name is empty, too long (255), has characters files can’t have, or is reserved. |
| 409 | name_taken | Something with that name is already in the folder. |
| 409 | root_folder | The Company files root can’t be renamed, moved, deleted, copied, shared or linked. Use the folders inside it. |
| 409 | other_tree | KeepAnything and SignAnything folders are separate. Copy it instead. |
| 409 | parent_trashed | The folder is in Trash. |
| 409 | folder_same_space | Folders move only within the same space. |
| 409 | folder_no_cycle | A folder can’t go inside itself. |
| 422 | folder_required | Choose a folder. |
| 409 | too_deep | The folder is nested too deeply to copy. |
| 451 | file_blocked | The file was blocked by the virus check, or isn’t what its name says. |
| 409 | restore_folder | The item went to Trash with its folder. Restore the folder instead. |
| 423 | locked | Someone has locked the file. |
| 409 | version_unavailable | That version can’t be restored. |
| 422 | invalid_sha256 | Send the file’s SHA-256 as 64 hex characters. |
| 422 | invalid_size | Send the file’s size. |
| 507 | storage_full | The upload would go over the account’s storage; used_bytes and limit_bytes say where you are. |
| 422 | single_part | This upload is a single PUT, so it has no part URLs. |
| 409 | upload_incomplete | Some parts haven’t arrived; received lists those that have. |
| 422 | size_mismatch | The uploaded file isn’t the size you announced. |
| 410 | invalid_upload | The upload token has expired (after 7 days) or isn’t valid. Start the upload again. |
| 409 | already_shared | They already have access. Change their level instead. |
| 409 | share_folder | Files in SignAnything folders are shared through their folder. |
| 422 | invalid_level | Choose view, contribute or manage. |
| 422 | invalid_grant | Choose who to share with: a person, a group or the whole company. |
| 422 | invalid_email | Enter a valid email address. |
| 422 | invalid_audience | Choose who the link is for. |
| 422 | expiry_too_long | The link lasts longer than the company allows; max_days says how long. |
| 422 | invalid_password | A link or request password is 6 to 200 characters. |
| 422 | password_required | Your company requires a password on links. |
| 409 | link_disabled | The link is turned off. Make a new one. |
| 422 | title_required | Give the file request a title. |
| 422 | invalid_title | The title is over 200 characters. |
| 422 | invalid_instructions | The instructions are over 4,000 characters. |
| 422 | invalid_limit | A file request allows at least one file and 1 MB, and no more than the plan allows. |
| 422 | invalid_query | Search for 1 to 200 characters. |
| 503 | unavailable | Files are unavailable for a moment. Try again shortly. |
Handling errors
- 4xx means the request needs to change. Don’t retry it as it is — except
429, and the409s that say to wait (scan_pending). - 429: wait for
Retry-Afterseconds. TheRateLimit-Limit,RateLimit-RemainingandRateLimit-Resetheaders on every response tell you how close you are. - 5xx or a timeout: retry with backoff. For
POST /v1/envelopes, send the sameIdempotency-Keyso a retry can’t create a second envelope. - A few errors come from the network edge before the API sees the request, so they have no problem body:
413for a request over 60 MB, and500,502or503when something is down. Treat them by their status.