Errors and Refusal codes
Every error the Essentio Public API answers has one shape, whatever went wrong and whatever the request asked to be answered in. A program branches on the error’s type, and on a refused Action’s Refusal code; the message is for a person.
The error shape
An error is a JSON object with one field, error:
- Name
type- Type
- string
- Description
What kind of error it is — one of the error types. Branch on this.
- Name
message- Type
- string
- Description
What went wrong, in words for a person, in the words Essentio’s own screens use.
- Name
errors- Type
- object
- Description
With a
422invalid_requestwhose fields the rules refused: each field, parameter or header at fault, with a list of what is wrong with it.
- Name
code- Type
- string
- Description
With a
refusal: the Refusal code that says why.
- Name
remedy- Type
- string
- Description
With some
refusals: what the Business does in Essentio to open the Action — one of the remedies.
- Name
document_number- Type
- string
- Description
When a Send issued its Draft and then did not go out (
not_delivered, or the refusalmail_account_lost): the Document number the Draft took. It stays issued.
- Name
invoice- Type
- object
- Description
With the remedy
record_on_its_invoice:idanddocument_numberof the Invoice that takes the Payment instead.
A Client with no email: 422
{
"error": {
"type": "invalid_request",
"message": "The email field is required.",
"errors": {
"email": ["The email field is required."]
}
}
}
A 4xx answer means the request changed nothing in the Business, with one exception: a Send that issued its Draft and then found the mail account lost (mail_account_lost) keeps the Draft issued, and says so in document_number — as a 502 not_delivered after an issue does.
Error types
The types an answer carries today. Within v1 another may be added (see Versioning): treat a type you do not know by its HTTP status.
- Name
authentication- HTTP status
- 401
- Description
The request carries no API key or access token, an unknown one, or one that no longer acts: a revoked key, an expired token, or a Connected app that ended. See Authentication.
- Name
permission- HTTP status
- 403
- Description
The credential’s Role may not do this: an API key’s, or the Role a Connected app acts with now — or the Business’s plan does not let it, and then
codesays why. It is asked before the record the request names, so a Role that may not make a change is answeredpermissionwhatever id it sends, one that names nothing included.
- Name
not_found- HTTP status
- 404
- Description
No such endpoint, or the Business has no record with this id. A record of another Business is not found either.
- Name
invalid_request- HTTP status
- 4xx
- Description
Any other request the API cannot answer as sent. A
422names each field, parameter or header at fault inerrors, in the words Essentio’s own forms use — a decimal sent as a JSON number, alimitout of range, acursorthe API did not give.
- Name
idempotency- HTTP status
- 422
- Description
An
Idempotency-Keysent again with another request than the first one under it. Nothing was done. See Idempotency.
- Name
idempotency_in_flight- HTTP status
- 409
- Description
The first request under this
Idempotency-Keyis still being performed. Nothing was done; send the same request again in a few seconds to read its answer.
- Name
rate_limit- HTTP status
- 429
- Description
The API key or Connected app made more requests this minute than its limit allows. Nothing was done; wait
Retry-Afterseconds. See Rate limits.
- Name
refusal- HTTP status
- 422
- Description
The Action is refused for the document or Payment as it stands.
codesays why; nothing was written.
- Name
not_delivered- HTTP status
- 502
- Description
The mail provider did not accept a Send’s email, or could not be reached in time. The request was sound.
- Name
server_error- HTTP status
- 5xx
- Description
A fault on Essentio’s side.
Refused, not permitted, not delivered
Three errors answer a request that was well formed, and each asks something different of your program:
refusal(422): the Business’s rules refuse the Action for the record as it stands — an issued document is not issued again, a Draft is not cancelled, a Client with no email address is not sent a document. The same request stays refused until the record changes. Readcode, andremedywhen there is one.permission(403): the credential’s Role may not take the Action at all — a Viewer’s key sending a document. No change to the document opens it; a credential with another Role does. A Role that may not take an Action is never answered as a refusal. The Public API itself needs a paid plan: while the Business is on the Free plan or Paused, every request of its API keys and Connected apps is this failure with the codepaid_plan_requiredand the remedychoose_plan, a read included, and the same credential works again once the Business is paid. The MCP server works on every plan; there, when the Business’s plan is why a change is refused — it is Paused, or on the Free plan — the failure carries itscode(business_paused,plan_role_viewing) and the remedychoose_plan. A plan chosen in Essentio opens either.not_delivered(502): the Send was allowed and tried, and the mail provider did not take it. The same request may succeed later. When the connection to the provider failed, the email may have gone out before it dropped, so check before you send again.
A document says beforehand what each Action would answer: its actions hold, for issue, send, cancel, convert, record_payment and delete, whether the Action is open and, when it is not, the Refusal code it would answer — or role_forbidden when your credential’s Role may not take it. Read them to draw only the buttons that would work.
Refusal codes
Why an Action is refused. The same refusal always has the same code, and within v1 codes are only added — never renamed or removed. Read a code you do not know as a refusal you do not handle yet, and show its message. The list holds every code Essentio has: first the ones the API’s operations answer, then the ones Essentio answers only in its own screens, on the MCP server, or to a caller that met none of the API’s rules — listed so that one never surprises you.
- Name
already_issued- Refused by
- Issue
- Description
Only a Draft is issued; this document already has its number.
- Name
number_taken- Refused by
- Issue, or a Send that issues a Draft
- Description
The Document number the Draft asks for (
requested_document_number) is already taken by another document.
- Name
number_skips_ahead- Refused by
- Issue, or a Send that issues a Draft
- Description
The requested number skips ahead of the numbering;
messagenames the next one.
- Name
number_beyond_the_counter- Refused by
- Issue, or a Send that issues a Draft
- Description
The requested number ends in a number larger than the numbering can count to.
- Name
number_malformed- Refused by
- Issue, or a Send that issues a Draft
- Description
The requested number is not a valid Document number.
- Name
numbering_at_its_end- Refused by
- Issue, a Send that issues a Draft, or Record a Payment
- Description
The next number — a document's, or a Receipt's — is the largest the numbering holds, which it never takes, because it cannot count past it;
messagenames that number. Nothing is written. A Draft can still be issued with an unused earlier number sent as itsdocument_number; a Receipt has no such way out.
- Name
cancelled_not_sent- Refused by
- Send
- Description
A Cancelled document is not sent.
- Name
no_client_address- Refused by
- Send
- Description
The Client has no email address. Nothing is issued.
- Name
no_mail_provider- Refused by
- Send
- Description
The Business has nothing to send through: it sends through Essentio's own mail server unless it connected a mailbox, and it has no email address of its own for replies (remedy
connect_mail_provider). Nothing is issued.
- Name
mail_account_lost- Refused by
- Send
- Description
The Business’s connected mail account refused to renew its access (remedy
connect_mail_provider).
- Name
sending_limit_reached- Refused by
- Send
- Description
The Business sends through Essentio's own mail server, which emails a limited number of recipients for it a day — To, Cc and Bcc each count: 20 on the Free plan, a Test business and a Paused Business, 100 on Pro, 200 on Pro Plus — and this email does not fit what is left;
messagesays how many are (remedyconnect_mail_provider: its own mailbox has no such limit). Once none is left a Draft is not issued; a Send refused after it issued its Draft names the number inmessage.
- Name
draft_not_cancelled- Refused by
- Cancel
- Description
A Draft is not a document yet; it is deleted instead (
DELETE /v1/documents/{id}).
- Name
paid_not_cancelled- Refused by
- Cancel
- Description
A Paid document is corrected with a Credit Note.
- Name
already_cancelled- Refused by
- Cancel
- Description
The document is already Cancelled.
- Name
holds_money- Refused by
- Cancel
- Description
The document holds Payments (or its Proforma’s); it is corrected with a Credit Note.
- Name
not_a_proforma- Refused by
- Convert
- Description
Only a Proforma Invoice converts into an Invoice.
- Name
draft_not_converted- Refused by
- Convert
- Description
A Draft Proforma Invoice is issued first.
- Name
cancelled_not_converted- Refused by
- Convert, or Update a document
- Description
A Cancelled Proforma Invoice converts into nothing, so neither is it converted nor is a Draft still linked to it saved, which would issue it.
- Name
already_converted- Refused by
- Convert
- Description
An Invoice was already made from this Proforma Invoice.
- Name
issued_not_deleted- Refused by
- Delete
- Description
Only a Draft is deleted; an issued document is cancelled instead.
- Name
type_takes_no_payments- Refused by
- Record or amend a Payment
- Description
A Credit Note or a Quote takes no Payments;
messagesays why.
- Name
draft_takes_no_payments- Refused by
- Record or amend a Payment
- Description
A Draft is issued first.
- Name
cancelled_takes_no_payments- Refused by
- Record or amend a Payment
- Description
A Cancelled document takes none.
- Name
nothing_due- Refused by
- Record a Payment
- Description
The document’s Amount due is zero.
- Name
payment_goes_to_its_invoice- Refused by
- Record a Payment
- Description
The Proforma Invoice was converted into an Invoice, which takes the Payment (remedy
record_on_its_invoicewitherror.invoice, while that Invoice still takes one).
- Name
payment_currency_mismatch- Refused by
- Record or amend a Payment
- Description
currencyis not the document’s.
- Name
payment_exceeds_amount_due- Refused by
- Record or amend a Payment
- Description
The amount is above what is due.
- Name
payment_reversed- Refused by
- Amend or reverse a Payment
- Description
It was reversed already.
- Name
issued_not_changed- Refused by
- Update a document
- Description
Only a Draft is changed through the API; an issued document is changed in Essentio.
- Name
draft_deleted- Refused by
- Issue, Send, or Update a document
- Description
The Draft was deleted meanwhile, by another request made while this one waited for it. Nothing is written and no number is taken.
- Name
proforma_deleted- Refused by
- Update a document
- Description
The Draft is linked to a Proforma Invoice that was deleted, so nothing is converted from it and its save, which would issue it, is refused.
- Name
only_into_an_invoice- Refused by
- Update a document
- Description
The Draft is linked to a Proforma Invoice, and only an Invoice is made from one.
- Name
proforma_other_currency- Refused by
- Update a document
- Description
The Draft is linked to a Proforma Invoice in another currency;
messagenames it.
- Name
proforma_other_client- Refused by
- Update a document
- Description
The Draft is linked to a Proforma Invoice of another Client.
- Name
not_exportable- Refused by
- Download a document’s XML
- Description
The document is not exported as EN16931 XML (a Draft, a Proforma Invoice, a Quote, or one the EN16931 check does not pass);
messagesays why.
- Name
exports_not_in_a_test_business- Refused by
- Download a document’s XML
- Description
The Business is a Test business (
is_test), which keeps no real books, so it exports nothing an accountant books from. Its PDFs stay, marked SAMPLE.
- Name
role_forbidden- Where
- in a document’s actions only
- Description
The credential’s Role may not take the Action. Taken anyway, it is a
permissionfailure (403), not arefusal.
- Name
paid_plan_required- Refused by
- Any request
- Description
The Business is on the Free plan or Paused, and the Public API needs a paid plan, so every API key and Connected app of the Public API is refused whatever it asks, a read included. It is a
permissionfailure (403) carrying this code and the remedychoose_plan;messagesays whether the Business is on the Free plan or paused. Nothing is revoked: the same credential works again once the Business is on Pro or Pro Plus. It comes before any other answer, so the Public API never answersbusiness_pausedorplan_role_viewing.
- Name
business_paused- Refused by
- Any change the plan does not keep, on the MCP server and in Essentio
- Description
The Business is Paused, because its Owner’s plan does not cover it; it still reads everything and records Payments. Taken, it is a
permissionfailure (403) carrying this code and the remedychoose_plan; a document’sactionsanswer it too. The Public API refuses a Paused Business’s credentialspaid_plan_requiredfirst.
- Name
plan_role_viewing- Refused by
- Any change, on the MCP server and in Essentio
- Description
The Business is on the Free plan, where everyone but its Owner only reads, and so does every Connected app they authorised; the Owner’s own Connected app is cut by nothing but the Role it was given. Taken, it is a
permissionfailure (403) carrying this code and the remedychoose_plan; a document’sactionsanswer it too. The Public API refuses a Free Business’s credentialspaid_plan_requiredfirst.
- Name
not_a_member- Refused by
- Any Action, in Essentio
- Description
The document is another Business’s. On the API another Business’s record is
not_found(404).
- Name
issuing_limit_reached- Refused by
- Issue, or a Send that issues a Draft
- Description
The Business is on the Free plan, or is a Test business, and has issued its Invoices for this year;
messagenames the day it issues again (remedychoose_plan). On the MCP server only a Free Business’s Owner’s own Connected app issues, so it meets this; anyone else’s meetsplan_role_viewingfirst, and the Public API answers a Free Businesspaid_plan_requiredfirst. A Test business keeps the Public API on every plan and meets it there too, in its own words and with no remedy: a reset lets it issue again.
- Name
no_mail_provider_to_remind- Refused by
- Remind, in Essentio
- Description
The Business has nothing to send through — no mailbox connected and no email address of its own for Essentio's mail (remedy
connect_mail_provider).
- Name
not_remindable- Refused by
- Remind, in Essentio
- Description
Reminders go only for issued documents still unsettled.
- Name
never_reminded- Refused by
- Remind, in Essentio
- Description
A Credit Note or a Quote is never reminded about;
messagesays why.
- Name
converted_proforma- Refused by
- Remind, in Essentio
- Description
The Proforma Invoice was converted into an Invoice, which is reminded about instead.
- Name
receipt_voided- Refused by
- Email a Receipt, in Essentio
- Description
The Payment was reversed, so its Receipt is void.
- Name
no_mail_provider_for_receipt- Refused by
- Email a Receipt, in Essentio
- Description
The Business has nothing to send through — no mailbox connected and no email address of its own for Essentio's mail (remedy
connect_mail_provider).
- Name
no_receipt- Refused by
- Email a Receipt, in Essentio
- Description
The Payment has no Receipt.
- Name
client_deleted- Refused by
- Duplicate, in Essentio
- Description
The document’s Client was deleted.
- Name
payment_not_positive- Refused by
- Record or amend a Payment, in Essentio
- Description
The amount is not above zero. The API refuses that amount as
invalid_requestfirst.
- Name
payment_not_in_minor_units- Refused by
- Record or amend a Payment, in Essentio
- Description
The amount is finer than its currency’s minor unit. The API refuses that amount as
invalid_requestfirst.
- Name
cancelled_not_edited- Refused by
- Edit, in Essentio
- Description
A Cancelled document is not edited.
- Name
number_not_correctable- Refused by
- Correct a Document number, in Essentio
- Description
The document takes no correction of its number as it stands.
- Name
proforma_not_found- Refused by
- Save a document linked to a Proforma Invoice
- Description
The Proforma Invoice it names is not found.
- Name
draft_not_delivered- Refused by
- Mark sent, in Essentio
- Description
A Draft is issued first.
- Name
cancelled_not_marked- Refused by
- Mark sent, in Essentio
- Description
A Cancelled document is not marked as sent.
- Name
cancelled_keeps_mark- Refused by
- Unmark sent, in Essentio
- Description
A Cancelled document keeps its delivery mark.
- Name
not_marked_sent- Refused by
- Unmark sent, in Essentio
- Description
The document is not marked as sent.
- Name
webhook_paused- Refused by
- Resend a Webhook delivery, in Essentio
- Description
The Webhook is paused.
- Name
app_not_connected- Refused by
- Resend a Webhook delivery, in Essentio
- Description
The app no longer hears the Business the message is about.
- Name
webhooks_need_a_paid_plan- Refused by
- Resend a Webhook delivery, in Essentio
- Description
The Business the message is about is on the Free plan or Paused, and Webhooks need a paid plan.
- Name
support_read_only- Refused by
- Any change, in Essentio
- Description
The request is Essentio support’s, which reads a Business with its Owner’s Support access and changes nothing. No credential of the API or the MCP server is support’s.
A number the Draft asks for that is taken, skips ahead or is malformed leaves the Draft a Draft: change or clear its document_number and try again. Each resource’s page says which codes its Actions answer: documents and Payments. These words are the contract’s own: RefusalCode in the OpenAPI file.
Remedies
What the Business does in Essentio to open a refused Action. It is nothing an API call can do: show it to a person who can act in Essentio. Remedies, too, are only added within v1. A document’s actions carry remedy_url, the page in Essentio, for connect_mail_provider and choose_plan.
- Name
connect_mail_provider- Description
In Settings → Email, connect a mailbox of the Business's own (or connect the lost account again); for
no_mail_provider, adding the Business's email address in Settings → General lets Essentio send again, and forsending_limit_reachedthe next day does.
- Name
record_on_its_invoice- Description
The Proforma Invoice was converted into an Invoice, which carries the debt: record the Payment on that Invoice (
error.invoice) instead.
- Name
choose_plan- Description
The Business’s plan does not let it (
business_paused,plan_role_viewing,paid_plan_required,webhooks_need_a_paid_plan,issuing_limit_reached): its Owner chooses a plan in Settings → Billing.
Handling an error
Branch on the HTTP status and type first, and on code for a refusal. This cancels a document and, when it turns out to be a Draft — which is deleted, not cancelled — deletes it instead:
Cancel a document, or delete it if it is a Draft
answer=$(curl -s -X POST https://api.essentio.pro/v1/documents/118/cancel \
-H "Authorization: Bearer $ESSENTIO_API_KEY")
case "$(echo "$answer" | jq -r '.error.code // empty')" in
"") echo "Cancelled: $(echo "$answer" | jq -r .status)" ;;
draft_not_cancelled)
curl -s -X DELETE https://api.essentio.pro/v1/documents/118 \
-H "Authorization: Bearer $ESSENTIO_API_KEY"
echo "It was a Draft: deleted" ;;
*) echo "Refused: $(echo "$answer" | jq -r .error.message)" ;;
esac
Cancelling a Draft: 422
{
"error": {
"type": "refusal",
"code": "draft_not_cancelled",
"message": "A draft is deleted, not cancelled — it is not a document yet."
}
}