Skip to content

Errors

Every API error comes out in the same shape, on any route and on any status.

{
"error": {
"type": "authentication_error",
"code": "api_key_invalid",
"message": "Invalid, revoked or expired API key.",
"request_id": "0a8c1f2e-3b4d-4c5f-9a6b-7c8d9e0f1a2b",
"doc_url": "https://docs.fatureihoje.com/en/errors#api_key_invalid"
}
}
FieldAlways thereWhat it is
typeYesThe error family. It lets you branch by range without knowing every code
codeYesThe exact case. This is what your program decides on
messageYesA sentence for a person to read. It comes in the Accept-Language language
doc_urlYesThe address of this page, at the anchor of the code
request_idOn /public/v1The identifier of this call. It is what support asks for
paramNoReserved. The contract allows it for a single-field error, and no route emits it today
errorsNoThe list of invalid fields. Only on the 422 of validation

The code is stable and written in English. It does not change value, does not change name and does not change with the language. A code published in the catalog keeps existing even when the API stops emitting it, because an integration that handles it must not break.

The message is the opposite: it is text for a person, and it comes translated according to the Accept-Language header of the request (pt-BR is the default, and en and es are accepted).

Each code has a single HTTP status, and it never varies. validation_failed is always 422; rate_limit_exceeded is always 429. That is why the catalog below publishes both together: if you handle the code, the status follows, and a pair different from what is here will never arrive.

The seven type families:

typeRangeWhen
invalid_request_error400, 404, 413, 415The request is wrong, or what it asks for does not exist
authentication_error401The key was not accepted
permission_error403The key was accepted, but this action is not allowed
conflict_error409Conflict with the current state
validation_error422Invalid field
rate_limit_error429Request budget exhausted
api_error500, 503The problem is ours

When one or more fields are invalid, the body carries the list, field by field:

{
"error": {
"type": "validation_error",
"code": "validation_failed",
"message": "One or more fields are invalid.",
"errors": [
{ "param": "email", "code": "invalid", "message": "Invalid e-mail" },
{ "param": "phone", "code": "invalid", "message": "Invalid phone" }
],
"request_id": "6f12b0d4-8e33-4a91-b2c7-5d0a7e93f118",
"doc_url": "https://docs.fatureihoje.com/en/errors#validation_failed"
}
}

param is the field name as the API receives it. message is text for a person; what your program uses is param.

This param is the one inside errors, and it is the only one you will see today. The top-level param, next to code, is allowed by the contract but no route emits it: always read the errors list.

Every /public/v1 response, with or without an error, carries the Request-Id header. In the error body, the same value shows up in request_id.

Store that value in your log whenever a call fails. It is how support finds the call, and it is the only path when the error is internal.

A path outside /public/v1 answers 404 before the call gets an identifier, so that 404 comes without request_id. If you got an error with no request_id, check the path first.

The doc_url points to this page, already at the anchor of the code:

https://docs.fatureihoje.com/en/errors#api_key_invalid

In Portuguese the address drops the language prefix: docs.fatureihoje.com/errors#.... You can record that address in your error log: it is short, permanent, and goes straight to the explanation of the code.

The example calls GET /public/v1/me and handles both outcomes: the good response and the error body. It works for any route, because the error shape is the same everywhere.

Terminal window
curl -sS -i https://api.fatureihoje.com/public/v1/me \
-H "Authorization: Bearer $FH_API_KEY" \
-H "Accept-Language: en"

-i prints the headers before the body, so Request-Id shows up next to the error.

This is the whole list. It is generated from the API contract when the site is built, so a new code shows up here on its own and no code is left out.

Code HTTP Type
api_key_invalid 401 authentication_error
access_denied 403 permission_error
permission_missing 403 permission_error
member_permission_denied 403 permission_error
member_suspended 403 permission_error
organization_suspended 403 permission_error
subscription_inactive 403 permission_error
plan_feature_unavailable 403 permission_error
plan_limit_reached 403 permission_error
resource_not_found 404 invalid_request_error
route_not_found 404 invalid_request_error
invalid_request 400 invalid_request_error
validation_failed 422 validation_error
conflict 409 conflict_error
idempotency_key_reused 422 validation_error
idempotency_in_progress 409 conflict_error
unsupported_media_type 415 invalid_request_error
request_too_large 413 invalid_request_error
rate_limit_exceeded 429 rate_limit_error
request_failed 400 invalid_request_error
internal_error 500 api_error
service_unavailable 503 api_error
api_key_invalid 401 authentication_error

Message Invalid, revoked or expired API key.

What to do Check the Authorization: Bearer <key> header, that the key is still active in the panel, and that your server outbound IP is on the key allow list. The response is the same in every one of those cases on purpose: it does not say which one it was.

access_denied 403 permission_error

Message You do not have access to this resource.

What to do Access denied with no known cause. This code goes out when the reason is neither the key permission nor the member permission. Keep the request_id and contact support.

permission_missing 403 permission_error

Message This API key lacks the permission required by this route.

What to do See what the key has in key.permissions, in the GET /public/v1/me response, and compare it with what the route requires in the Reference. A key permission set never changes after creation, not even on rotation: to change it, create another key.

member_permission_denied 403 permission_error

Message The member this key acts as is not allowed to perform this action.

What to do The key has the permission, but the member it acts as cannot do this in the panel. Adjust that member role or permissions, or use a key of another member.

member_suspended 403 permission_error

Message The member this key acts as is suspended.

What to do Reserved. Today a suspended member falls into the 401 api_key_invalid, because the authentication response must not confirm that the key is valid. If this code shows up, reactivate the member in the panel.

organization_suspended 403 permission_error

Message The organization of this key is suspended.

What to do Reserved, for the same reason as the previous one, but for the company. If it shows up, sort out the company status in the panel.

subscription_inactive 403 permission_error

Message The organization subscription is not active.

What to do Settle the payment in the panel. Retrying before that returns the same error, so looping on it is wasted work.

plan_feature_unavailable 403 permission_error

Message The organization plan does not include this API feature.

What to do The API is available from the Start plan upwards. The same code goes out when the API was turned off for that company in particular.

plan_limit_reached 403 permission_error

Message The plan limit for this resource has been reached.

What to do The most common one is the number of active keys. Check usage in the panel, revoke what you no longer use, or move up a plan.

resource_not_found 404 invalid_request_error

Message Resource not found.

What to do The id does not exist, or it belongs to another company. The API answers 404 in both cases, never 403: a 403 would tell you the record exists somewhere else. Check the id and the key company in GET /public/v1/me.

route_not_found 404 invalid_request_error

Message Route not found.

What to do The path does not exist. Check the method, the path and the address: api.fatureihoje.com serves only /public/v1. A route of an area that is not published yet answers the same way.

invalid_request 400 invalid_request_error

Message Invalid request.

What to do The request is malformed, almost always broken JSON or an invalid query parameter. Fix it before retrying: the same request returns the same error.

validation_failed 422 validation_error

Message One or more fields are invalid.

What to do The body carries errors, with param, code and message for each field. Fix what it points at and send again: the same request returns the same error.

conflict 409 conflict_error

Message The operation conflicts with the current state of the resource.

What to do Read the resource again and decide from what it says now. Retrying without rereading returns the same conflict. It is what an appointment status change the calendar does not allow answers (cancelled does not come back, completed only goes back to confirmed), an update to a service order that is already completed or cancelled, an update to an approved or cancelled quote, deleting a quote that already became an active sale the second conversion of the same quote into a sale or a service order, starting, completing or cancelling an order already completed or cancelled, any write to a quote absorbed in a merge, an internal code (sku) that already exists in the company, a variation name repeated within the same product, deleting a category that still has products or services, and on a sale: any write to a canceled sale, a new version or cancellation with an active invoice, a new version with the seller commission already paid, receiving or rescheduling on a contract sale, reversing a payment already reversed or with an active invoice, and rescheduling with no open installment. In finance: changing a protected field of a sale or bank entry, deleting a sale or bank entry, marking as paid an entry that is canceled or on a statement, deleting an account that has entries, and a series delete that would remove more than what was asked (with param scope).

idempotency_key_reused 422 validation_error

Message This Idempotency-Key was already used with a different body.

What to do Generate a new value for every different operation. Reusing a key is only for repeating the SAME request, byte for byte.

idempotency_in_progress 409 conflict_error

Message A request with this Idempotency-Key is still in progress.

What to do Wait a few seconds and repeat the same request, with the same key. Do not generate a new key: that would run the operation twice.

unsupported_media_type 415 invalid_request_error

Message Send the body as JSON, with Content-Type: application/json.

What to do Form encoding, multipart and plain text are not accepted. Check the Content-Type your library sends by default.

request_too_large 413 invalid_request_error

Message The request body is above the 1 MB limit.

What to do Split the payload into smaller parts. The ceiling applies to every call, with or without Idempotency-Key.

rate_limit_exceeded 429 rate_limit_error

Message Per-minute request limit reached. Try again shortly.

What to do Wait what the Retry-After header says and back off progressively. Do not retry in a tight loop: the budget belongs to the company, and you delay its good calls.

request_failed 400 invalid_request_error

Message The request could not be processed.

What to do The API did not process the request and the reason has no code of its own in the catalog. Keep the request_id. If the same request keeps failing, send the request_id to support.

internal_error 500 api_error

Message Internal error. If it persists, send the request_id to support.

What to do The problem is on our side. The response never carries internal detail, on purpose. You may retry after waiting. If it persists, send the request_id to dev@fatureihoje.com.

service_unavailable 503 api_error

Message API unavailable right now. Try again shortly.

What to do The cause is edge configuration, not your request. Wait and try again. If it lasts, tell support with the request_id.

  • Rate limits: the 429 and the headers that say how much is left.
  • Idempotency: the two errors that show up when you repeat a request.
  • Authentication: the 401 checklist, the most common error at the start.