This page is the global error-code dictionary for the API. Every error response carries a stable, machine-readable code your client must use to branch logic. The detail is human-readable and translated; the code never changes and is never translated.
For a gentle intro to the error envelope, see Errors. For related flows, see Idempotency, Rate limits, and Webhooks architecture.
Response shape
Every error response follows the standard JSON:API envelope — an errors array and a meta object. A single response can carry several errors when applicable (typically for field validation).
{
"errors": [
{
"status": "422",
"code": "phone_not_verified",
"detail": "Phone is not verified.",
"source": { "pointer": "/data/attributes/phone" },
"meta": { "masked_phone": "+52 55 ••••1234" }
}
],
"meta": {
"version": "1.47.0",
"api_version": "v1",
"request_id": "a1b2c3d4e5f6",
"datetime": { "timezone": "UTC", "format": "ISO 8601" }
}
}Global rules
codeis stable, snake_case ASCII, and never translated. Branch client logic on this field.detailis human-readable and translated perAccept-Language(es/en).statusis the HTTP status code as a string (matches the response status).source.pointer(optional) is a JSON Pointer (RFC 6901) to the request-body field that failed.- Per-error
metais optional — some codes attach context (e.g.masked_phone,retry_after_seconds). - A 422 response may include several errors — one per invalid field.
meta.request_idcorrelates with server logs — always include it when reporting a bug.
Common HTTP statuses
| Status | Meaning in this API |
|---|---|
| 400 Bad Request | Request is malformed — invalid JSON, missing query param, wrong format. Don't retry without fixing. |
| 401 Unauthorized | Authentication missing, token expired, or invalid credentials. Re-auth before retrying. |
| 403 Forbidden | Authenticated but lacking permission for this resource or action. Don't retry — the caller lacks the role or auth method. |
| 404 Not Found | The resource doesn't exist or isn't visible to the caller. |
| 405 Method Not Allowed | Correct path, wrong HTTP method. |
| 409 Conflict | State conflict — typically in-flight idempotency, resource already terminal, or race condition. May be worth retrying after Retry-After. |
| 410 Gone | Resource existed but was deleted. |
| 413 Payload Too Large | Body or upload exceeds the endpoint cap. |
| 422 Unprocessable Entity | Request parsed correctly but failed business validation. A single response may list multiple errors. |
| 429 Too Many Requests | Rate limit exceeded. Wait Retry-After seconds before retrying. |
| 500 Internal Server Error | Unexpected server failure. Retry with exponential backoff. |
| 502 Bad Gateway | Upstream service (Banxico, Stripe) is down or returned malformed data. Retry with backoff. |
| 503 Service Unavailable | Subsystem temporarily disabled or overloaded. Retry after Retry-After. |
Taxonomy by category
Authentication & session
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
unauthorized | 401 | Missing, invalid, or expired credentials. | All protected | Re-authenticate (relogin or rotate API key) |
invalid_credentials | 401 | Wrong email or password. | POST /v1/auth/login, POST /v1/auth/request-verification-otp | Show a generic message — don't reveal which failed |
auth_header_empty | 401 | The Authorization header is empty. | All protected | Send Authorization: Bearer ... |
unrecognized_auth_format | 401 | Authorization doesn't parse as Bearer <token>. | All protected | Use Bearer mxcep_... (API key) or cookie JWT |
invalid_api_key | 401 | API key prefix or format is invalid. | M2M endpoints | Regenerate from /uso or POST /v1/users/me/api-key/regenerate |
invalid_encrypted_api_key | 401 | API key doesn't decrypt against the server secret. | M2M endpoints | Regenerate |
api_key_decryption_failed | 401 | Cryptographic failure decrypting the API key. | M2M endpoints | Regenerate |
auth_method_denied | 403 | Endpoint requires cookie (UI) but request used API key, or vice versa. | Audience-restricted | Use the correct auth method — see Authentication |
dashboard_token_misuse | 401 | A dashboard token was used against an M2M endpoint. | /v1/* M2M | Use API key, not cookie |
token_expired | 401 | JWT has expired. | Cookie endpoints | Renew (re-login) or clear the cookie |
token_lifetime_exceeded | 401 | JWT exceeds the server's absolute max lifetime. | Cookie endpoints | Re-login |
invalid_token_format | 401 | JWT doesn't have 3 segments. | Cookie endpoints | Re-login |
invalid_token_encoding | 401 | JWT doesn't decode as base64url. | Cookie endpoints | Re-login |
invalid_token_signature | 401 | JWT signature doesn't verify. | Cookie endpoints | Re-login |
invalid_token_payload | 401 | JWT payload doesn't parse as JSON. | Cookie endpoints | Re-login |
invalid_token_issuer | 401 | JWT iss is unexpected. | Cookie endpoints | Re-login |
invalid_token_audience | 401 | JWT aud is unexpected. | Cookie endpoints | Re-login |
unsupported_token_algorithm | 401 | JWT uses an alg outside the whitelist. | Cookie endpoints | Re-login |
session_invalidated | 401 | Session was manually invalidated (force-logout, 2FA reset). | Cookie endpoints | Re-login |
csrf_missing | 401 | X-CSRF-Token header missing on a cookie-only mutation. | UI mutations | Refresh the cookie and retry |
csrf_mismatch | 401 | CSRF token doesn't match the cookie. | UI mutations | Refresh the cookie and retry |
forbidden | 403 | RBAC permission denied. | Any | Ask your account admin for the permission |
permission_denied | 403 | Variant of forbidden for admin endpoints. | /v1/admin/* | Check the user's role |
account_suspended | 403 | Caller's account is suspended. | All authenticated | Contact support |
account_deleted | 403 | Account was soft-deleted. | All authenticated | Contact support |
registration_disabled | 403 | Global flag registration.enabled is OFF. | POST /v1/auth/register | Wait until re-enabled |
OTP, 2FA, and verification
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
phone_not_verified | 403 | Phone hasn't been verified via OTP. | POST /v1/auth/login, mutations | Trigger POST /v1/auth/request-verification-otp |
phone_already_verified | 409 | Phone is already verified. | POST /v1/auth/request-verification-otp | Continue with the normal flow |
otp_not_found | 404 | No active OTP for that user + purpose. | POST /v1/auth/verify-phone, GET /v1/auth/otp-status | Re-request OTP |
otp_invalid_code | 422 | OTP code is wrong. | POST /v1/auth/verify-phone, password reset | Re-enter (capped at N attempts) |
otp_expired | 422 | OTP expired before use. | OTP confirmation | Re-request OTP |
otp_max_attempts | 422 | Attempts exhausted for this OTP. | OTP confirmation | Request a brand-new OTP |
otp_already_used | 422 | OTP was already consumed. | OTP confirmation | Request another |
otp_cooldown | 429 | Too many OTPs in a short window. | POST /v1/auth/resend-otp | Wait the cooldown |
invalid_otp_id | 422 | The otp_id isn't a UUID. | OTP confirmation | Re-request OTP and use the new otp_id |
invalid_otp_parameters | 422 | OTP payload incomplete. | OTP confirmation | Check required fields |
missing_otp_id | 422 | otp_id missing from body. | OTP confirmation | Add otp_id |
invalid_verification_token | 422 | Verification token invalid or malformed. | POST /v1/auth/reset-password | Restart the verification flow |
invalid_reset_context | 422 | Reset context (purpose, audience, expiry) doesn't match. | POST /v1/auth/reset-password | Restart the flow |
purpose_mismatch | 422 | OTP was issued for a different purpose. | password-reset endpoints | Re-request OTP with the correct purpose |
confirmation_token_missing | 422 | confirmation_token missing in a step-up call. | step-up endpoints | Add confirmation_token |
confirmation_token_invalid | 422 | Malformed step-up token. | step-up | Re-request the token |
confirmation_token_malformed | 422 | Token doesn't have the expected structure. | step-up | Re-request |
confirmation_token_mismatch | 422 | Token corresponds to a different action. | step-up | Re-request for the correct action |
confirmation_token_already_used | 409 | Token already consumed (single-use). | step-up | Request a new one |
confirmation_token_slug_mismatch | 422 | Token was issued for a different plan/slug. | step-up billing/plans | Re-issue for the correct slug |
twofa_required | 401 | The user has 2FA enabled — solve the challenge. | POST /v1/auth/login | Trigger POST /v1/auth/2fa/verify-challenge |
twofa_invalid_challenge | 422 | Wrong 2FA challenge code. | POST /v1/auth/2fa/verify-challenge | Re-enter |
twofa_invalid_context | 422 | 2FA context (purpose / audience) doesn't match. | 2FA endpoints | Restart the flow |
twofa_not_enabled | 422 | Tried to verify 2FA but the user doesn't have it on. | 2FA endpoints | Enable 2FA first |
twofa_already_disabled | 422 | 2FA disable requested but already off. | POST /v1/auth/2fa/confirm-disable | Ignore — state is correct |
twofa_already_enabled | 422 | 2FA enable requested but already on. | POST /v1/auth/2fa/confirm-enable | Ignore — state is correct |
twofa_method_same | 422 | Method change requested but new method equals current. | POST /v1/auth/2fa/confirm-method-change | Pick a different method |
twofa_method_unsupported | 422 | Requested 2FA method isn't supported. | 2FA endpoints | Use sms or email |
twofa_action_mismatch | 422 | Resolved challenge doesn't match the requested action. | 2FA step-up | Restart the step-up |
Registration & account
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
email_already_registered | 422 | Email already has a verified account. | POST /v1/auth/register | Show a generic anti-enum message |
email_already_taken | 422 | Variant: email isn't available. | Email change | Pick another |
email_already_verified | 409 | Email is already verified. | POST /v1/auth/email/request-verify | Continue normally |
email_same_as_current | 422 | New email matches the current one. | POST /v1/auth/email/request-change | Use a different email |
email_delivery_failed | 502 | Email provider rejected the send. | Email change/verify | Retry; contact support if persistent |
phone_taken | 422 | Phone is on another verified account. | Registration / phone change | Use a different phone |
phone_required_for_email_change | 422 | Verified phone required before changing email. | POST /v1/auth/email/request-change | Verify phone first |
password_mismatch | 422 | password and password_confirmation don't match. | Registration / reset | Re-enter matching values |
password_too_short | 422 | Password below min length. | Registration / reset / change | Use a longer password |
password_too_weak | 422 | Password fails complexity rules. | Registration / reset / change | Strengthen the password |
new_password_too_short | 422 | Variant: in password change, the new value is too short. | Password change | Use more characters |
current_password_required | 422 | current_password missing in a password change. | Password change | Add the field |
current_password_incorrect | 422 | current_password is wrong. | Password change | Re-enter correctly |
invalid_email_change_context | 422 | Email-change context invalid (purpose mismatch). | Email change apply | Restart the flow |
sms_delivery_failed | 502 | SMS provider rejected the OTP send. | Re-OTP / register | Retry; contact support if persistent |
unsupported_locale | 422 | Submitted locale isn't whitelisted. | PUT /v1/users/me | Use es or en |
language_unsupported | 422 | Variant of unsupported_locale. | PUT /v1/users/me | Use es or en |
Generic field validation
These codes are emitted by Support\Validator and may appear on any endpoint with a body.
code | Status | When fired | Client should |
|---|---|---|---|
required | 422 | Required field missing. | Include the field |
missing_field | 422 | Variant of required for specific fields. | Include the field |
invalid_format | 422 | Field doesn't match expected format. | Fix the format |
invalid_value | 422 | Value outside allowed set. | Use a value from the enum |
invalid_type | 422 | PHP/JSON type doesn't match. | Adjust the type |
invalid_email | 422 | Email doesn't parse. | Fix it |
invalid_phone | 422 | Phone doesn't parse as E.164. | Use +52... format |
invalid_uuid | 422 | Not a UUID v4. | Pass a valid UUID |
invalid_url | 422 | URL is malformed. | Fix it |
invalid_url_scheme | 422 | Only https accepted. | Use https:// |
url_ssrf_blocked | 422 | URL points to a private IP / DNS rebind. | Use a public host |
url_dns_failed | 422 | DNS couldn't resolve the host. | Verify the host |
max_length_exceeded | 422 | Exceeds field max length. | Shorten |
invalid_amount | 422 | Amount doesn't parse as positive decimal. | Fix it |
invalid_date | 422 | Date malformed or out of range. | Use ISO 8601 |
invalid_account_format | 422 | Account isn't CLABE 18 / card 16 / phone E.164. | Fix per account_type |
invalid_clabe_checksum | 422 | CLABE check digit fails. | Verify the CLABE |
invalid_card_luhn | 422 | Card Luhn check fails. | Verify the PAN |
invalid_account_length | 422 | Wrong length for the type (18 CLABE / 16 card / 10 phone). | Adjust length |
invalid_bank_code | 422 | Bank code not in the catalog. | Use GET /v1/public/banks |
invalid_image | 422 | Image doesn't decode. | Re-send |
invalid_image_format | 422 | Format outside JPEG/PNG/WEBP. | Convert to JPEG/PNG |
image_too_large | 422 | Image over the cap. | Reduce size |
validation_error | 422 | Generic validation error. | Check source.pointer and meta |
validation_failed | 422 | Variant for combined failures. | Check the detail |
invalid_request_body | 400 | JSON doesn't parse. | Send valid JSON |
invalid_payload | 400 | Payload shape doesn't match the contract. | Check endpoint spec |
payload_too_large | 413 | Whole body over the cap. | Shrink |
payload_corrupt | 422 | Payload arrived truncated or malformed. | Re-send |
message_build_failed | 422 | Server couldn't build the message to dispatch. | Retry |
Idempotency
See Idempotency for the full contract.
code | Status | When fired | Client should |
|---|---|---|---|
invalid_idempotency_key | 400 | Idempotency-Key header fails [A-Za-z0-9_-]{1,255}. | Generate a new key with valid characters |
idempotency_key_in_progress | 409 | Another in-flight request shares this key. | Wait Retry-After seconds and retry |
idempotency_key_reused | 422 | Same key used previously with a different body. | Generate a new key or replay the exact body |
SPEI transfer validation
Applies to POST /v1/validate, POST /v1/validate-ocr, and the bulk validation pipeline.
code | Status | When fired | Client should |
|---|---|---|---|
clabe_prefix_not_recognized | 422 | First 3 digits of the CLABE don't map to any bank. | Verify the CLABE — may not be SPEI |
bank_code_required_for_phone | 422 | account_type=phone requires an explicit bank_code. | Add bank_code |
bank_code_unresolvable_for_phone | terminal | Async: bank couldn't be resolved from the phone via DiMo. | Wait — terminal state is error |
intra_bank_no_cep | terminal | Async: intra-bank transfer; Banxico doesn't issue CEP. | Look for the bank's internal receipt |
clave_or_ref_required | 422 | Need clave_rastreo or referencia_numerica. | Include one |
image_or_image_url_required | 422 | OCR: neither image nor image_url. | Send one |
image_not_readable | 422 | OCR couldn't read the image. | Send a clearer image |
image_not_readable_receipt | 422 | OCR read it but it doesn't look like an SPEI receipt. | Send the right receipt |
image_not_spei_receipt | 422 | The receipt isn't SPEI (another transfer type). | Send an SPEI receipt |
Async-validation terminal codes
These codes appear in validations.error_code when an async validation lands in terminal state error or failed. See Async validations and Retry policy.
code | When fired | Triggers validation.error webhook |
|---|---|---|
network | Banxico unreachable after network retries exhausted. | Yes |
captcha | Banxico returned an active captcha. | Yes |
max_retries_exceeded | Plan's retry cap was hit. | Yes |
ttl_expired | Job TTL elapsed before reaching terminal state. | No |
preflight_failed | Pre-dispatch validation failed. | No |
rate_limit_exhausted | Proxy/rate slots exhausted. | No |
dispatch_failed | Handler couldn't dispatch the job. | No |
bank_code_unresolvable_for_phone | Phone account_type with no resolvable bank. | No |
intra_bank_no_cep | Intra-bank — Banxico doesn't issue CEP. | No |
banxico_rate_limit | Banxico returned a punctual rate-limit. | (internal, retries) |
banxico_rate_limit_exhausted | Rate-limit retries exhausted. | (internal) |
banxico_captcha_active | Banxico has a global captcha active. | (internal, retries) |
rate_limit_batch | Batch lot hit rate-limit. | No |
ocr_error | OCR upstream (Gemini) failed. | No |
banxico_error | Generic Banxico error. | No |
Beneficiaries
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
query_param_account_required | 400 | Missing ?account= in the query. | GET /v1/beneficiaries/lookup, /validate-account | Add the param |
query_param_last_digits_required | 400 | Missing ?last_digits= in the query. | GET /v1/account-directory/lookup | Add the param |
invalid_last_digits_length | 422 | last_digits isn't 4 digits. | GET /v1/account-directory/lookup | Pass 4 digits |
account_number_required | 422 | Missing account_number in body. | POST /v1/beneficiaries | Add the field |
beneficiary_already_registered | 422 | Account is already in your beneficiaries. | POST /v1/beneficiaries | Reuse the existing one |
beneficiary_not_found_for_account | 404 | No beneficiary linked to this account. | GET /v1/beneficiaries/lookup | Create it |
account_not_in_catalog | 404 | Account doesn't appear in the cross-tenant directory. | GET /v1/account-directory/lookup | Treat as "no info" |
Bulk imports (validations + beneficiaries)
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
file_required | 422 | No file in the upload. | POST /v1/{validations,beneficiaries}/imports | Attach the file |
multi_upload_not_allowed_for_text | 422 | Multi-select but the files are plain text. | POST /v1/validations/imports | Upload one by one, or use images/ZIP |
parse_failed | 422 | Parser couldn't extract rows. | Imports (preview) | Check the file format |
commit_failed | 422 | Import commit failed mid-way. | POST /v1/{validations,beneficiaries}/imports/{id}/commit | Check detail; retry if idempotent |
job_not_editable | 422 | Edit attempted on a job in terminal state. | PATCH /v1/validations/imports/{id}/rows/{row_id} | The job is already committed |
job_not_cancellable | 409 | Cancel requested on a non-cancellable job. | Imports admin | Job already terminal |
no_valid_fields | 422 | Edit payload has no valid fields. | PATCH import row | Include at least one field |
zip_pack_failed | 422 | Failed to repack a ZIP of images. | POST /v1/validations/imports with ZIP | Retry with a standard ZIP |
no_valid_test_files | 422 | No processable file in the upload. | Admin tests | Check the set |
duplicate_clave_rastreo | (row) | Same clave_rastreo across rows of the same import. | preview rows | Edit or discard the duplicate |
duplicate_tuple | (row) | Duplicate tuple (sender, receiver, date, amount). | preview rows | Edit or discard |
account_duplicate | (row) | Beneficiary import: repeated account_number. | preview rows | Edit/discard |
alias_duplicate | (row) | Beneficiary import: repeated alias. | preview rows | Edit/discard |
alias_missing | (row) | Beneficiary import: alias missing. | preview rows | Add it |
clabe_checksum_failed | (row) | Row's CLABE fails checksum. | preview rows | Fix |
card_luhn_failed | (row) | Row's card fails Luhn. | preview rows | Fix |
bin_unknown | (row) | Card BIN doesn't map to a bank. | preview rows | Verify the PAN |
masked_account_unresolved | (row) | Account arrived masked and didn't resolve. | preview rows | Edit with the full account |
no_match | (row) | Free-form parser didn't detect fields. | preview rows | Edit manually |
no_usable_data | (row) | Row has insufficient data. | preview rows | Edit or discard |
tracking_missing | (row) | clave_rastreo missing. | preview rows | Add |
clabe_missing | (row) | CLABE/account missing. | preview rows | Add |
fecha_missing | (row) | Date missing. | preview rows | Add |
fecha_invalid | (row) | Date doesn't parse. | preview rows | Fix |
monto_missing | (row) | Amount missing. | preview rows | Add |
monto_invalid | (row) | Amount doesn't parse. | preview rows | Fix |
Plan & billing
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
plan_not_found | 404 | Referenced plan doesn't exist. | /v1/billing/*, /v1/plans/{slug} | List plans via GET /v1/plans/public |
billing_plan_not_found | 422 | Variant: slug doesn't resolve to an active plan. | POST /v1/billing/checkout-session | Verify the slug |
billing_plan_is_free | 422 | Attempted to subscribe to a free plan via Stripe checkout. | Checkout | Free plans are assigned without Stripe |
billing_plan_not_self_service | 422 | Plan isn't assignable via self-service. | Checkout | Contact support |
billing_plan_slug_required | 422 | Missing plan_slug in body. | Checkout | Add the field |
billing_plan_slug_too_long | 422 | Slug exceeds max length. | Checkout / admin | Shorten |
billing_plan_slug_invalid | 422 | Slug doesn't match ^[a-z0-9-]+$. | Checkout / admin | Fix it |
billing_intent_invalid_shape | 422 | app_checkout_intent cookie is corrupt. | Checkout | Clear the cookie and restart |
billing_invalid_interval | 422 | interval isn't month or year. | Checkout / admin prices | Use an allowed value |
billing_invalid_kind | 422 | Price kind isn't base or metered. | Admin prices | Use an allowed value |
billing_disabled | 503 | Billing module is disabled in this deploy. | /v1/billing/* | Wait until enabled |
no_active_subscription | 404 | User has no active subscription. | /v1/billing/subscription/* | Start checkout |
subscription_race_retry | 409 | Race condition mutating the subscription — retry. | /v1/billing/subscription/* | Retry after 1-2s |
subscription_not_found | 404 | Subscription doesn't exist. | Admin subs | Verify user_id |
stripe_cancel_failed | 502 | Stripe rejected the cancel. | /v1/billing/subscription/cancel | Retry |
stripe_checkout_failed | 502 | Stripe rejected the session creation. | Checkout | Retry |
stripe_unreachable | 502 | Stripe API timeout / connection error. | Any billing | Retry |
stripe_invoice_malformed | 422 | Stripe invoice arrived incomplete in the webhook. | (internal) | (logged & resync) |
stripe_subscription_malformed | 422 | Stripe subscription arrived incomplete. | (internal) | (logged & resync) |
stripe_meter_lookup_failed | 502 | Stripe meter lookup failed. | Metered billing | Retry |
stripe_meter_missing | 422 | Expected meter not in Stripe. | Metered billing | Create the meter or disable metered |
stripe_product_create_failed | 502 | Stripe Product creation failed. | Admin plans | Retry |
stripe_price_not_found | 404 | stripe_price_id doesn't exist in Stripe. | Admin prices | Verify the ID |
stripe_price_archive_failed | 502 | Stripe rejected price archive. | Admin prices | Retry |
stripe_price_id_required | 422 | Missing stripe_price_id. | Admin prices | Add |
stripe_price_id_format_invalid | 422 | stripe_price_id doesn't match price_*. | Admin prices | Fix |
price_not_found | 404 | Local price doesn't exist. | Admin prices | Verify the ID |
price_local_id_required | 422 | Missing price_local_id. | Admin prices | Add |
price_not_active | 422 | Price is archived. | Checkout / admin | Activate first |
price_already_in_catalog | 422 | stripe_price_id already used by another price. | Admin prices | Reuse the existing |
price_combo_already_exists | 422 | Duplicate (plan, interval, currency, kind) combo. | Admin prices | Edit the existing one |
price_plan_mismatch | 422 | Referenced price doesn't belong to this plan. | Admin prices | Use the correct plan |
source_price_id_required | 422 | Missing source_price_id in migrate. | Admin price migrate | Add |
unit_amount_required | 422 | Missing unit_amount. | Admin prices | Add |
overage_update_failed | 422 | Overage toggle failed. | /v1/billing/overage/{enable,disable} | Retry |
overage_not_applicable | 422 | Current plan doesn't support overage. | Overage toggle | Upgrade plan |
refund_amount_invalid | 422 | Invalid refund amount (negative or > invoice). | Admin refund | Fix |
refund_invoice_not_found | 404 | Referenced invoice doesn't exist in Stripe. | Admin refund | Verify invoice_id |
admin_grant_failed | 422 | Admin-side plan grant failed. | Admin grant-plan | Check the detail |
comp_amount_invalid | 422 | Invalid comp amount. | Admin comp | Fix |
comp_user_missing | 422 | Target user missing in comp. | Admin comp | Add |
comp_user_not_found | 404 | Comp target user doesn't exist. | Admin comp | Verify |
Finance
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
invalid_month | 400 | month param missing or not YYYY-MM. | /v1/finance/*, /v1/api-usage/* | Pass YYYY-MM |
invalid_period | 400 | period doesn't match the convention. | Finance / usage | Fix |
invalid_range | 400 | from/to range incoherent. | Finance / usage | Fix |
range_required | 400 | range or from/to missing. | Finance / usage | Pass one |
pdf_render_failed | 500 | dompdf failed to render. | Finance PDF | Retry; report if persistent |
xlsx_render_failed | 500 | PhpSpreadsheet failed. | Finance XLSX | Retry |
csv_stream_failed | 500 | CSV stream failed. | Finance / exports CSV | Retry |
invalid_export_format | 422 | Format outside json/csv/xlsx/pdf. | Exports | Pick a supported one |
no_ceps | 404 | No CEPs in the range. | GET /v1/finance/ceps | Adjust the range |
Webhooks & notifications
code | Status | When fired | Endpoints | Client should |
|---|---|---|---|---|
webhook_url_required | 422 | url missing. | POST /v1/webhooks | Add |
webhook_url_empty | 422 | url sent but empty. | PATCH /v1/webhooks/{id} | Add a real URL |
webhook_url_too_long | 422 | url over 2048 chars. | Webhooks CRUD | Shorten |
webhook_url_invalid_format | 422 | url doesn't parse. | Webhooks CRUD | Fix |
webhook_url_not_https | 422 | Scheme isn't https. | Webhooks CRUD | Use HTTPS |
webhook_events_required | 422 | events array missing. | Webhooks CRUD | Add at least one |
webhook_events_too_many | 422 | More events than the cap. | Webhooks CRUD | Reduce |
webhook_event_invalid | 422 | Event outside the registry. | Webhooks CRUD | See events in docs |
webhook_status_invalid | 422 | status isn't active/disabled. | Webhooks CRUD | Fix |
webhook_limit_reached | 422 | Plan webhook cap hit. | POST /v1/webhooks | Upgrade plan or delete one |
signature_invalid | 401 | Incoming webhook signature mismatch — Stripe ingress. | POST /v1/webhooks/stripe | Verify the secret and timestamp |
push_subscription_invalid_shape | 422 | PushSubscription object is invalid. | POST /v1/notifications/push/subscribe | Re-subscribe from the SW |
push_subscription_endpoint_required | 422 | endpoint missing. | POST /v1/notifications/push/test | Add |
push_subscription_endpoint_not_https | 422 | Endpoint isn't HTTPS. | Push subscribe | Check the provider |
push_subscription_endpoint_too_long | 422 | Endpoint exceeds the cap. | Push subscribe | (provider issue) |
push_subscription_none_active | 422 | No active push subscriptions. | POST /v1/notifications/push/test | Subscribe first |
push_not_configured | 503 | Server has no VAPID configured. | Push endpoints | Contact support |
notifications_event_unknown | 422 | Event outside the registry. | /v1/notifications/preferences | Check valid events |
notifications_channel_unknown | 422 | Channel outside the registry. | Preferences | Use email/sms/push/in_app/telegram |
notifications_preference_invalid | 422 | Preference shape invalid. | Preferences | Check the body |
notifications_preference_locked | 422 | Preference is forced by the server (broadcast). | Preferences | Not user-editable |
notifications_preferences_empty | 422 | Body has no preferences. | Preferences | Add at least one |
notification_not_found | 404 | Referenced notification doesn't exist. | GET /v1/notifications/{id} | Verify the id |
invalid_notification_id | 422 | id isn't a UUID. | Notifications | Fix |
telegram_bot_not_configured | 503 | Telegram bot isn't configured server-side. | /v1/notifications/telegram/link | Contact support |
quiet_hours_invalid_timezone | 422 | Timezone doesn't exist. | PUT /v1/notifications/quiet-hours | Use an IANA TZ |
quiet_hours_minutes_out_of_range | 422 | Minutes outside [0, 1440]. | Quiet hours | Fix |
Rate limit
See Rate limits for the full headers + buckets convention.
code | Status | When fired | Client should |
|---|---|---|---|
rate_limited | 429 | Generic rate-limit bucket exceeded. | Wait Retry-After seconds |
rate_limit_exceeded | 429 | Variant for internal buckets (e.g. login). | Wait and retry |
too_many_requests | 429 | Semantic variant of the standard 429. | Wait |
Generic resources
code | Status | When fired | Client should |
|---|---|---|---|
not_found | 404 | Requested resource doesn't exist or isn't visible. | Verify the id |
user_not_found | 404 | Referenced user doesn't exist. | Verify the id |
key_not_available | 404 | User's API key isn't displayable. | Regenerate |
cep_not_available | 404 | CEP for the validation isn't available yet. | Retry later |
image_not_available | 404 | Original image isn't accessible. | Re-upload (if applicable) |
changelog_not_found | 404 | Requested version doesn't exist. | List via GET /v1/public/changelog |
changelog_empty | 404 | No changelog published. | Wait for publication |
method_not_allowed | 405 | HTTP method not supported for this path. | Use an allowed one |
internal_error | 500 | Unexpected failure. | Retry with backoff |
dispatch_failed | 500/503 | Job couldn't be dispatched to the worker. | Retry |
Admin (internal operation)
These codes come from /v1/admin/* endpoints and require a permissioned role. The complete list covers queues, scheduled tasks, services, migrations, seeders, tests, plans, security, and i18n panels. The most recurring ones are listed below; the rest follow the convention <domain>_<action>_failed for 5xx and <domain>_<action>_invalid for 4xx.
code | Status | Domain |
|---|---|---|
permission_denied | 403 | Any admin |
admin_self_target_forbidden | 403 | Actions on the caller's own user (force-logout, reset-2fa) |
cannot_delete_self / cannot_suspend_self / cannot_change_own_role / cannot_change_own_status / cannot_force_logout_self / cannot_reset_own_2fa | 403 | Foot-gun guardrails |
cannot_suspend_owner / cannot_suspend_admin / only_owner_can_* | 403 | RBAC role hierarchy |
cannot_deactivate_default_plan / cannot_set_inactive_as_default / plan_cannot_delete_default | 422 | Default plan invariants |
queues_*_failed | 500 | /admin/queues family — overview, states, stats, pause, resume, start, stop, restart, cancel, retry, discard, dlq, batches, jobs, workers, config. Each has its _failed and, where applicable, its _prepare_failed (step-up phase 1). |
scheduled_task_*_failed | 500 | /admin/scheduled-tasks family — create, update, delete, detail, run_now, executions, drift, list. |
migration_* | 422 / 404 | migration_not_found, migration_file_missing, migration_filename_required, migration_filename_format, migration_target_version_format, migration_target_invalid_type, migration_mark_bulk_limit. |
seeder_* | 422 / 404 | seeder_not_found, seeder_invalid, seeder_bulk_limit, seeder_filename_format, seeder_filename_required. |
test_* | 422 / 404 | test_file_not_found, test_filename_format, test_files_limit, test_files_must_be_array, test_name_invalid, test_suite_empty, test_suite_invalid, compare_runs_must_differ. |
worker_not_found / instance_not_found / scheduled_task_not_found / dlq_entry_not_found / execution_not_found / run_not_found | 404 | Queues subresources |
worker_not_running / job_not_cancellable / run_not_cancellable | 409 | Conflict — terminal state |
dlq_entry_unreadable | 422 | DLQ entry malformed |
payload_corrupt / payload_too_large / invalid_payload | 400 / 413 | Admin step-up payloads |
invalid_action / invalid_instance / invalid_service / invalid_stream / invalid_user_id / invalid_audit_id / invalid_event_id / invalid_delivery_id / invalid_otp_id / invalid_run_id / invalid_id / invalid_param / invalid_status_filter / invalid_source_filter / invalid_target_user_id / invalid_bucket_target / invalid_version / missing_version | 400 | Param validation across admin panels |
bulk_action_invalid / bulk_slugs_required / bulk_slugs_too_many | 422 | Admin bulk endpoints |
name_already_exists | 422 | Naming collision (broadcasts, plans) |
broadcast_not_found / broadcast_empty_audience | 404 / 422 | /admin/notifications/broadcasts |
logs_read_failed / heartbeat_persist_failed / execution_failed | 503 | /admin/services |
openapi_spec_missing / openapi_spec_invalid_yaml | 500 | /admin/i18n bundle generator |
target_required / probe_label_required | 422 | /admin/banxico probes |
retry_policy_invalid | 422 | Per-plan retry caps |
permissions_must_be_object | 422 | /admin/roles editor |
plan_* | 422 / 404 | Plan CRUD: plan_name_required, plan_name_too_long, plan_slug_required, plan_slug_format_invalid, plan_duplicate_same_slug, plan_currency_invalid, plan_features_invalid_shape, plan_included_validations_negative, plan_limit_negative, plan_sort_order_negative, plan_trial_days_out_of_range, plan_tax_behavior_invalid, plan_free_cannot_have_initial_prices, plan_stripe_product_missing, plan_price_* (migrate, archive, range). |
initial_price_base_required / initial_price_metered_required | 422 | Plan create with initial prices |
out_of_range / invalid_type | 422 | /admin/queues worker config validator |
worker_failed / run (queues) | 500 | Generic |
recent_executions_failed / scheduled_tasks_drift_failed / scheduled_tasks_list_failed | 500 | Scheduler |
i18n — placeholder_parity_drift, icu_malformed, html_unsafe, brand_removed | 422 | /admin/i18n lint |
Reporting an error
When something fails unexpectedly:
- Capture
meta.request_idfrom the response. - Note the
codeand thestatus. - If it's a repeatable 5xx, open a support ticket including both.
The request_id correlates with server-side logs and lets the team trace the exact cause without you having to reproduce it.