Skip to content

Errors

Every refusal has the same shape, application/problem+json, on every route:

{
"type": "/errors/UNAUTHENTICATED",
"title": "Unauthorized",
"status": 401,
"detail": "Authentication is required to access this resource.",
"code": "UNAUTHENTICATED"
}

You can see it by calling the API without a key:

no-key.sh
curl --silent --show-error --include https://api.facteur.eu/inboxes

code is the member your client tests against. detail is a sentence in English for the person who reads a red pipeline, and its wording can change. Do not parse it and do not assert on it. To show a refusal in another language, translate the code.

A refusal that needs a value to be useful carries it as a member of its own, next to detail. The 403 of a missing permission has permission, and the operation that can answer it documents it in the reference. A validation failure (VALIDATION_FAILED) lists each field in violations, with its propertyPath, its message and its own code.

Status Code What you do
401 UNAUTHENTICATED Add the Authorization header.
401 API_KEY_UNKNOWN Check what your pipeline sends. This key never existed.
401 API_KEY_REVOKED Create a new key. A person revoked this one.
401 API_KEY_EXPIRED Create a new key, and do it before the date next time.
403 API_KEY_PERMISSION_MISSING Give the key the permission named in permission, or call an operation it already reaches.
403 API_KEY_CANNOT_DELETE_WORKSPACE Delete the workspace from the Facteur app. No key does it.

Authentication explains the permissions and the three reasons for a 401.

Code What you do
INBOX_NOT_FOUND Check the identifier, then check that the key reaches the workspace of the inbox.
MESSAGE_NOT_FOUND The same, for a message.
WORKSPACE_NOT_FOUND The same, for a workspace address.
MEMBER_NOT_FOUND The person is not a member of this workspace.
EXTRACTOR_NOT_FOUND Check the identifier of the extractor and the inbox it belongs to.
ATTACHMENT_NOT_FOUND Check the identifier of the attachment.
ATTACHMENT_NOT_STORED Facteur never kept the bytes of this attachment. Its reason in the message says why.
SMS_REQUEST_NOT_FOUND This inbox has never asked for an SMS number. Ask for one with a POST on the same path.

All of them answer 404. A resource that belongs to another organization, or to a workspace the key does not reach, gets the same 404 as one that does not exist.

A search on an inbox the key does not reach answers 404 too, and never an empty list. Tell the two apart in your test: an empty list means the mail has not arrived, a 404 means you are looking in the wrong place.

inbox-not-found.sh
curl --silent --show-error --include \
-H "Authorization: Bearer $FACTEUR_API_KEY" \
https://api.facteur.eu/inboxes/zzzzzzzz/messages
Status Code What you do
400 CRITERIA_EMPTY Name a set: an inbox, a recipient, a subject, a code or a date.
400 CRITERIA_UNKNOWN Fix the field the detail names. It is not a criterion of the search.
400 CRITERIA_INVALID Fix the value of the field the detail names: a text over its longest length (Limits lists them), a limit that is not a whole number, a date that is not one, a boolean that is not true or false.
400 INVALID_CURSOR Send back the nextCursor of the previous page, unchanged, or leave cursor out.

The two forms of the search, GET/messages and POST/messages/search, answer the same codes. A POST whose body is not JSON answers 400 BAD_REQUEST.

CRITERIA_UNKNOWN is the one that surprises people. If subjet were ignored in place of subject, the search would match every message of the inbox, your test would assert on the first one, and it would pass until a second message arrived.

unknown-criterion.sh
curl --silent --show-error --include --get \
-H "Authorization: Bearer $FACTEUR_API_KEY" \
--data-urlencode "inbox=j3k9x2mq" \
--data-urlencode "subjet=code" \
https://api.facteur.eu/messages

The other refusal of a search is a value too long. A subject is at most 255 characters, and a longer one is refused instead of being cut, because a cut text would match other messages:

too-long-criterion.sh
curl --silent --show-error --include --get \
-H "Authorization: Bearer $FACTEUR_API_KEY" \
--data-urlencode "subject=$(printf 'a%.0s' {1..300})" \
https://api.facteur.eu/messages
Status Code What you do
429 RATE_LIMITED Wait the number of seconds in Retry-After, then send fewer requests. A key may make 600 a minute.
429 TOO_MANY_WAITS Await each search before you send the next, lower your worker count, or shorten wait.
503 API_DRAINING Send the same request again after the Retry-After delay. The instance that received it was stopping.
503 ATTACHMENT_STORE_UNAVAILABLE Try the download again a little later.

The two 429 count different things, so the code tells you what to change. Limits gives the numbers.

These answer 402. They come from the plan of the organization, not from the key:

Code What you do
INBOX_LIMIT_REACHED Delete an inbox, or change the plan.
WORKSPACE_LIMIT_REACHED Delete a workspace, or change the plan.
INBOX_QUOTAS_UNAVAILABLE The plan does not include per-inbox ceilings. Removing a ceiling works on every plan.
WORKSPACE_QUOTAS_UNAVAILABLE The plan does not include per-workspace ceilings. Removing a ceiling works on every plan.
SMS_NUMBER_LIMIT The plan holds no more SMS numbers. limit and occupied say how many it holds and how many are taken, requiredPlan names the plan that holds one more. Take back a request, or change the plan.

A request for an SMS number is read by a person of the Facteur team, who gives the number. These are the refusals of the request itself:

Status Code What you do
400 SMS_COUNTRY_NOT_OPEN Ask for a country listed in countries, or leave country out for the United Kingdom.
400 SMS_NOTE_TOO_LONG Shorten note to max characters.
409 SMS_REQUEST_ALREADY_OPEN The inbox already has a request waiting. Read it with a GET, or take it back before you ask again.
409 SMS_REQUEST_NOT_WITHDRAWABLE The request was already declined or taken back: there is nothing left to take back.

Sometimes a message arrives while a limit is reached. Facteur stores it with the status quota_exceeded and reads nothing from it. It carries the reason in errorCode, and none of the members read from the body (subject, otp, links, extract). Read status before you read any of them.

errorCode What you do
PLAN_QUOTA_EXCEEDED The capacity of the plan is spent for this period.
INBOX_QUOTA_EXCEEDED Raise or remove the ceiling you set on this inbox.
WORKSPACE_QUOTA_EXCEEDED Raise or remove the ceiling you set on this workspace.
OVERAGE_CAP_REACHED Raise the spending cap you chose, or buy more capacity.
DAILY_SPIKE_CAP Wait for the next day.
SUBSCRIPTION_SUSPENDED Settle the outstanding invoice.

These are a person’s decisions in the Facteur app. An API key cannot change any of them.

The routes of this API are listed in the reference, each with the refusals it can answer. For example, GET/messages can answer the codes of the search table above.