Errores
Cómo reconocer un error de la API, qué significa cada código y qué hacer para resolverlo.
Formato del error
Toda respuesta 4xx o 5xx trae un objeto detail:
{
"detail": {
"code": "scope_missing",
"message": "La credencial no tiene permiso para esta operación.",
"scope": "invoices:issue",
"request_id": "req-ABC123"
}
}
| Campo | Qué es |
|---|---|
code | Identificador estable del error. Programa contra él, no contra message. |
message | Texto en castellano para mostrar o registrar. Algunos errores no lo traen. |
request_id | Identificador de la petición. También llega en la cabecera X-Request-Id. Guárdalo si necesitas que se revise un fallo. |
| Otros campos | Contexto de cada error: errors, missing, fields, scope, feature, reasons... Se explican en las tablas de abajo. |
La Referencia API solo declara el error 422, y con otro esquema. Guíate
por esta página.
Este formato es el de la API REST. El servidor MCP devuelve sus errores con otra forma, descrita en su guía.
Errores dentro de una respuesta correcta
Algunas operaciones responden 2xx e informan de los problemas en el cuerpo:
| Operación | Respuesta | Qué mirar |
|---|---|---|
POST /api/v1.1/drafts/validate y el resto de rutas .../validate | 200 | is_valid y errors[] |
PUT /api/v1.1/drafts y PUT /api/v1.1/received-invoices | 201 | success: false indica que la factura se ha guardado con errores |
POST /api/v1.1/issued-invoices/import | 201 | errors[]: la factura importada se guarda aunque tenga errores y success puede ser true |
POST /api/v1.1/drafts/batch y POST /api/v1.1/issued-invoices/import/batch | 200 | rejected y, en items[], cada status (accepted o rejected) con sus errors |
Cada elemento de errors[] tiene esta forma:
| Campo | Qué es |
|---|---|
source | Origen de la regla: schema, business, verifactu o ticketbai |
code | Código de la regla |
message | Descripción del problema |
path | Ruta del campo, como /invoice/totals/invoice_total |
extra | Datos adicionales de la regla |
severity | error impide emitir; warning solo informa |
Códigos HTTP
| Código | Significa | Qué hacer |
|---|---|---|
400 | Petición mal formada | Corrige la petición |
401 | Falta la API key o no es válida | Revisa la clave |
402 | El plan de la empresa no cubre la operación | Cambia de plan o espera a que se renueve el cupo |
403 | La clave no tiene permiso o la IP no está autorizada | Revisa los scopes y la lista de IP de la clave |
404 | El recurso no existe o no es de la empresa de la clave | Revisa el identificador y la ruta |
409 | El estado del recurso no permite la operación | Consulta el recurso antes de repetir |
413 | Contenido demasiado grande | Reduce el fichero |
415 | Tipo de contenido no admitido | Revisa Content-Type |
422 | Los datos no cumplen el contrato o las reglas de negocio | Corrige los datos |
429 | Límite de peticiones superado | Reintenta tras Retry-After |
500 | Error inesperado | Reintenta con espera creciente |
502 | Un servicio externo ha fallado | Reintenta con espera creciente |
503 | Servicio no disponible | Reintenta con espera creciente |
Autenticación, permisos y plan
| HTTP | code | Causa | Solución |
|---|---|---|---|
401 | unauthorized | Falta la cabecera X-API-Key (message: Missing credentials), o la clave tiene un formato incorrecto, no existe, está revocada o ha caducado (message: Invalid credentials) | Revisa la cabecera. Si la clave está revocada o caducada, crea otra (ver autenticación) |
402 | feature_outside_plan | El plan de la empresa no incluye la funcionalidad de feature: api_access (uso de la API) o products (búsqueda de productos). Trae upgrade: true | Cambia de plan desde la aplicación |
402 | quota_exceeded | Se ha agotado un cupo del plan. metric dice cuál, y limit y used, el límite y el consumo. El reconocimiento de documentos usa metric: "ocr", con cupo mensual | Espera a que se renueve el cupo o cambia de plan |
403 | scope_missing | A la clave le falta el permiso de scope | Usa una clave con ese scope. Cambiar la configuración de VeriFactu, SII o TicketBAI y emitir eventos SIF exige una clave con acceso total |
403 | ip_forbidden | La clave tiene una lista de IP autorizadas y la petición sale de otra | Añade la IP a la lista de la clave |
429 | rate_limited | Has superado el límite de peticiones de la clave | Espera los segundos de Retry-After |
Límite de peticiones
Cada API key admite 120 peticiones por ventana fija de 60 segundos. Al superarlo, la API responde:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1789470060
Retry-After: 23
{
"detail": {
"code": "rate_limited",
"scope": "api_key",
"request_id": "req-ABC123"
}
}
X-RateLimit-Reset es el instante, en segundos Unix, en que empieza la ventana siguiente.
Estas cabeceras solo llegan en la respuesta 429.
Validación del canónico
Errores al enviar una factura (PUT /api/v1.1/drafts, POST /api/v1.1/issued-invoices/import,
PUT /api/v1.1/received-invoices y los lotes) o al emitirla. Todos responden 422.
code | Causa | Solución |
|---|---|---|
request_validation_error | El cuerpo no es JSON válido o le falta schema_version, meta o invoice. errors[] trae field, message y type | Corrige la estructura raíz |
inferred_fields_not_allowed | Has enviado campos que calcula el sistema. fields lista sus rutas; en un lote, items[] trae index y fields | Quita esos campos (ver la lista debajo) |
schema_validation_failed | Al guardar un borrador, el payload no cumple el esquema json_1_1. errors[] trae cada fallo con source: "schema" | Corrige cada path. Códigos habituales: schema.required, schema.type, schema.enum, schema.additional_properties, unsupported_schema_version |
invoice_number_not_allowed | Has enviado header.invoice_number en un borrador | Quítalo: el número se asigna al emitir |
issue_date_not_allowed | Has enviado issue_data.issue_date en un borrador | Quítala: la fecha se asigna al emitir |
missing_required_fields | Faltan campos obligatorios para la operación. missing los lista | Añádelos. Al importar, el número y la fecha son obligatorios |
future_issue_date | Al importar, issue_date es posterior a hoy | Revisa la fecha |
issuer_tax_id_required / issuer_tax_id_mismatch | En una emitida, falta el NIF del emisor o no es el de la empresa de la clave (expected, got) | Pon el NIF de la empresa en supplier.tax_id |
recipient_tax_id_required / recipient_tax_id_mismatch | En una recibida, falta el NIF del receptor o no es el de la empresa de la clave | Pon el NIF de la empresa en customer.tax_id |
validation_failed | Al emitir, la factura no supera la validación. errors[] trae las reglas incumplidas | Corrige el borrador con PUT /api/v1.1/drafts?invoice_id=... y vuelve a emitir |
missing_emission_fields | Al emitir, faltan datos para numerar (missing, por ejemplo series) | Completa el borrador |
forced_number_invalid | Al emitir con invoice_number, el número no encaja con la plantilla de la serie | Envía el número completo, con la serie y el formato de su plantilla |
invoice_number_series_mismatch | Al importar una emitida, el número no empieza por su serie | Envía el número tal como figura en la factura, con la serie incluida, o la serie que le corresponde |
Campos que no se envían porque los calcula el sistema:
- En
header:is_simplifiedeis_corrective. - En
lines[]:taxable,exempt,tax_category_code,tax_output_codeytax_withheld_code. - En
taxes_outputs[]ytaxes_withheld[]:taxable,exempt,tax_category_codeytax_type_code. - En
country_subdivision:subdivision_type.
Reglas de negocio habituales
Aparecen en errors[]: al validar, al guardar un borrador con errores o dentro de
validation_failed al emitir.
code | Causa |
|---|---|
invoice_total_mismatch | totals.invoice_total no cuadra con la base, las cuotas, el recargo de equivalencia y las retenciones |
taxes_outputs_mismatch_totals_tax | totals.total_tax_outputs no coincide con la suma calculada desde las líneas. extra trae el valor calculado |
tax_rate_unknown | El tipo impositivo no está entre los tipos conocidos. message lista los admitidos |
exemption_reason_required | Una operación exenta no indica exemption_reason_code |
invalid_spanish_dni / invalid_spanish_nie / invalid_spanish_cif | NIF español con dígito de control incorrecto |
unknown_spanish_tax_id_format | El NIF no tiene formato de DNI, NIE ni CIF |
person_type_inconsistent_with_tax_id | person_type_code no encaja con el tipo de NIF |
customer_required | Falta el destinatario en una factura no simplificada |
line_price_mode_mixed | Hay líneas con unit_price_with_tax y otras con unit_price_without_tax |
corrective_reason_required / corrective_invoice_number_required | Rectificativa sin motivo o sin número de la factura rectificada |
Las reglas con source: "verifactu" o source: "ticketbai" dependen del régimen de la
empresa. Ver cumplimiento.
Estado, bloqueos y duplicados
Todos responden 409.
code | Causa | Solución |
|---|---|---|
invalid_status_transition | El estado no permite la operación, por ejemplo emitir una factura ya emitida o anular una que no lo está. from y to indican la transición | Consulta la factura antes de repetir |
company_not_verified | La empresa no está verificada y no puede emitir. status indica su situación | Completa la verificación en la aplicación |
invoice_update_not_allowed | La factura existe y su estado ya no admite cambios, por ejemplo al actualizar con PUT una factura emitida | Para corregir una emitida, rectifícala |
invoice_race_condition | Dos peticiones han intentado crear la misma factura a la vez | Consulta si ya existe antes de reintentar |
issued_invoice_fiscally_locked / received_invoice_fiscally_locked | Intentas cambiar datos fiscales de una factura ya comprometida con la Administración. reasons da el motivo y locked_fields_changed, los campos | Cambia solo datos no fiscales |
invoice_delete_not_allowed | Solo se eliminan borradores, recibidas y emitidas fuera de la plataforma, y nunca si ya están comprometidas con la Administración (reasons) | No se puede eliminar |
invoice_not_issued_for_export | Solo se exportan facturas emitidas o enviadas | Emite la factura antes de exportarla |
bulk_issue_series_active / invoice_queued_for_bulk_issue | Hay una emisión masiva en curso en esa serie, lanzada desde la aplicación | Espera a que termine |
forced_number_in_use | Al emitir con invoice_number, ese número ya lo tiene otra factura, de esa serie o de otra | Revisa la numeración de tu programa: el número no se puede repetir |
invoice_number_already_assigned | Al emitir con invoice_number, un intento de emisión anterior ya asignó otro número al borrador (number) | Emite sin invoice_number o con el número que ya tiene |
auto_number_unavailable | Al emitir sin número, la serie no ha podido asignar uno libre. retryable: true si otra emisión se quedó con el que tocaba; false si la serie tiene por delante un histórico que agota el margen de saltos | Con true, vuelve a emitir. Con false, sube el contador de la serie por encima de lo importado |
Salvo un número repetido al emitir (forced_number_in_use) o una factura que ya no admite esa escritura (invoice_update_not_allowed), la API no responde 409 a un duplicado. Cada operación lo resuelve así:
- Borrador con un
meta.external_refque ya tiene otro borrador: actualiza ese borrador. Si la referencia es de una factura que ya no es un borrador, responde409invoice_update_not_allowed. - Emitida importada con el mismo número (el número incluye la serie): actualiza la factura importada, salvo que esté bloqueada, anulada o sea de otra serie.
- Recibida con el mismo proveedor, número y ejercicio: actualiza la existente, salvo que esté bloqueada.
No encontrado
Todos responden 404.
code | Causa |
|---|---|
invoice_not_found | La factura no existe, es de otra empresa o no corresponde a la ruta: un borrador pedido en /issued-invoices, una emitida en /drafts o una recibida en /issued-invoices |
document_not_found | El documento no existe en esa factura |
company_not_found | La empresa de la clave ya no existe |
not_found | La ruta no existe. Revisa la URL base y el prefijo /api/v1.1 |
Ficheros y otras operaciones
| HTTP | code | Causa |
|---|---|---|
400 | empty_body | Importación de XML sin cuerpo |
400 | unknown_status | Valor desconocido en el filtro status |
413 | payload_too_large | XML de más de 10 MB (max_bytes) |
413 | file_too_large | Documento adjunto de más de 16 MB |
415 | unsupported_media_type | Importación de XML sin Content-Type: application/xml o text/xml (expected) |
422 | validation_error | Importación de XML con errores de lectura. Hoy el detalle llega como texto dentro de message |
422 | empty_file | Documento adjunto vacío |
422 | recognition_file_invalid | Fichero para reconocer vacío o mayor que max_bytes |
422 | no_recipient_email | Envío por email sin to y sin email del cliente en la factura |
422 | facturae_signing_error | No se ha podido firmar el Facturae. Revisa el certificado de la empresa |
422 | ubl_en16931_validation_error / cii_en16931_validation_error | La factura no supera la validación EN 16931 del formato pedido. errors trae el detalle |
422 | export_validation_error | La factura no se ha podido exportar al formato pedido |
Servicio
| HTTP | code | Causa | Qué hacer |
|---|---|---|---|
500 | internal_server_error | Error inesperado | Reintenta con espera creciente. Si persiste, guarda el request_id |
500 | invoice_creation_error | No se ha podido guardar la factura. Hoy también llega así cuando la empresa ha agotado el cupo anual de facturas de su plan, con el motivo en message | Revisa message. Si es el cupo, cambia de plan |
502 | external_service_error | Un servicio externo no responde | Reintenta más tarde |
503 | recognition_disabled | El reconocimiento de documentos no está habilitado | No reintentes |
503 | db_unavailable / service_unavailable | Servicio no disponible por un momento | Reintenta con espera creciente |
Cómo reintentar
429: espera los segundos deRetry-After.500,502y503: espera cada vez más (1 s, 2 s, 4 s...) y fija un máximo de intentos.409coninvoice_race_condition: consulta la factura antes de reintentar.- Resto de
4xx: no repitas la petición sin cambiarla. - Emitir es seguro de repetir: si el borrador ya se emitió, la API responde
409coninvalid_status_transitiony no lo emite dos veces.