Factuarea API
Conceptos clave

Errores

Envoltorio de error normalizado, catálogo de type y code con anclas estables, y estrategia de reintentos.

Toda respuesta de error de la API pública usa un envoltorio JSON consistente. El estado HTTP indica la categoría general; el campo type desambigua y el campo code apunta a la causa específica.

Envoltorio

{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "El campo client_id es obligatorio.",
    "param": "client_id",
    "request_id": "req_01HKQS5N8VR7QXJ9K3T6BWPMZA",
    "doc_url": "https://docs.factuarea.com/guides/errors#parameter_invalid"
  }
}

Campos:

  • type — categoría general del error. Estable y enumerada (lista abajo).
  • code — causa específica. Estable y enumerada.
  • subcode — opcional. Presente en algunas respuestas 409 conflict_error / resource_already_exists para señalar la clave duplicada exacta — p. ej. subcode: "tax_id_already_exists".
  • message — texto para personas en español. No se garantiza estable entre versiones; útil para logging y visualización.
  • param — opcional, presente en errores de validación. Apunta al campo problemático.
  • doc_url — opcional. Enlace a esta guía con ancla al code específico (#{code}).
  • request_id — identificador único de la petición (req_<ULID>). Inclúyelo siempre cuando contactes con soporte. También se devuelve en el header de respuesta X-Request-Id.

El objeto error siempre lleva type, code y message; el resto de campos están presentes cuando es relevante.

Tipos de error

typeHTTPDescripción
invalid_request_error400 o 422Payload malformado, parámetros faltantes/inválidos o fallo de validación de negocio.
authentication_error401La API key falta, es inválida, está revocada, ha expirado o la IP no está en la lista de acceso.
authorization_error403La key es válida pero el scope no cubre el endpoint.
permission_error403El plan/add-on no da acceso a la funcionalidad.
not_found_error404El recurso solicitado no existe o no pertenece a la empresa de la key.
conflict_error409Conflicto de creación, lock de idempotencia o recurso duplicado (p. ej. un tax_id ya registrado).
idempotency_error409Reutilización de Idempotency-Key con un payload distinto.
rate_limit_error429Superada la cuota por minuto o mensual, o demasiados fallos de autenticación.
api_error500Error inesperado del backend. Los reintentos pueden ayudar; reporta a soporte con el request_id.
service_unavailable_error503API pública deshabilitada vía kill-switch, o caída de una dependencia (Stripe, mailer).

Las violaciones de reglas de negocio (transición de estado inválida, una acción no permitida en el estado actual del documento) responden 422 con type: invalid_request_error y code: invalid_status_transitionno 409. 409 conflict_error se reserva para creación duplicada, conflictos de idempotencia y locks de concurrencia.

Catálogo de codes

El ancla de cada encabezado H3 coincide exactamente con el valor del campo code del envoltorio. El doc_url que devuelve la API resuelve a la sección específica. La lista de abajo cubre los codes que encontrarás en la práctica; la referencia OpenAPI en vivo documenta los codes exactos por endpoint.

invalid_request_error

parameter_invalid

Un parámetro de la petición falta o es inválido. param apunta al campo problemático (p. ej. client_id, lines[0].quantity).

parameter_invalid_format

El formato de un valor es incorrecto para su semántica (regex, longitud, codificación, un UUID malformado, una fecha fuera de formato).

parameter_invalid_range

Un valor numérico o de fecha está fuera del rango permitido (p. ej. limit fuera de 1..100).

parameter_invalid_cursor

El cursor starting_after / ending_before no es un id de recurso válido. Consulta Paginación.

parameter_unknown

El body contiene un campo no documentado (en endpoints estrictos).

invalid_param_format

Falló una restricción de formato en un campo tipado — p. ej. el header Idempotency-Key o el header Factuarea-Version está malformado.

invalid_param_value

El valor no cumple una restricción (enum, formato, regla semántica).

invalid_period

El periodo de reporte solicitado es inválido (p. ej. un trimestre/año que no existe).

invalid_status_transition

La transición solicitada está prohibida por la máquina de estados del documento (p. ej. enviar una factura que no está en un estado enviable). Las violaciones de reglas de negocio como esta son 422, no 409.

invoice_already_paid

mark-paid sobre una factura ya pagada.

quote_already_accepted

Acción que entra en conflicto con un presupuesto ya aceptado.

business_rule_violation

Una invariante de dominio bloqueó la operación. El subcode identifica la regla y param el campo infractor. Lo usa el ledger de pagos (Registrar pagos):

  • payment_exceeds_pending_amount (param: "amount") — el importe del pago es mayor que el saldo pendiente de la factura. Aplica tanto a POST /v1/invoices/{id}/payments como a POST /v1/purchase_invoices/{id}/payments.
  • invalid_payment_date (param: "paid_on") — la fecha de pago cae fuera de la ventana permitida fecha_emisión … hoy (facturas de compra).
  • purchase_invoice_not_payable (param: "status") — la factura de compra está cancelada y ya no admite pagos.
{
  "error": {
    "type": "invalid_request_error",
    "code": "business_rule_violation",
    "subcode": "payment_exceeds_pending_amount",
    "message": "El importe del pago (1.500,00 €) supera el importe pendiente de la factura (710,00 €).",
    "param": "amount",
    "doc_url": "https://docs.factuarea.com/guides/errors#business_rule_violation",
    "request_id": "req_..."
  }
}

unsupported_format

El formato de exportación/reporte solicitado no está soportado.

insufficient_data_for_report

No hay datos suficientes para generar el reporte de impuestos solicitado.

signature_payload_too_large

La imagen de firma del albarán supera el tamaño máximo.

authentication_error

missing_api_key

No hay header de autenticación presente (Authorization: Bearer o X-API-Key).

invalid_api_key

La key no existe o el secreto no coincide con el hash almacenado.

api_key_revoked

La key fue revocada. Crea una nueva en el dashboard.

too_many_auth_failures

Se han limitado fallos de autenticación repetidos desde tu cliente. Espera (back off) y verifica tus credenciales.

authorization_error

insufficient_scope

La key no tiene el scope que requiere el endpoint. Consulta el catálogo en Autenticación › Scopes.

permission_error

feature_not_available_in_plan

El plan actual no incluye el módulo requerido (p. ej. recurring_invoices).

addon_not_active

El add-on developer_api está inactivo o fuera de su periodo de gracia.

not_found_error

resource_not_found

El recurso no existe o no pertenece a tu empresa.

tax_report_not_found

El reporte de impuestos solicitado no existe.

conflict_error

resource_already_exists

Intento de crear un duplicado (p. ej. un tax_id ya registrado). El subcode (p. ej. tax_id_already_exists) señala la clave duplicada.

resource_conflict

La operación entra en conflicto con el estado actual del recurso (p. ej. una modificación concurrente).

max_api_keys_exceeded

La empresa ha alcanzado su número máximo de API keys activas.

idempotency_error

idempotency_key_reused

Mismo Idempotency-Key, body de petición distinto. Usa una key nueva. Consulta Idempotencia.

rate_limit_error

rate_limit_exceeded

Superaste la cuota por minuto o mensual de tu tier. El header Retry-After indica los segundos a esperar. Consulta Límites de peticiones.

api_error

internal_error

Error inesperado. Ya está capturado por nuestra parte, pero comparte el request_id con soporte.

service_unavailable_error

service_unavailable

La API pública no está disponible temporalmente — deshabilitada globalmente vía kill-switch, en una ventana de mantenimiento, o una dependencia (base de datos, mailer, Stripe) no está sana. Reintenta tras un back-off corto.

Errores tipados con el SDK oficial

Los SDKs de TypeScript y PHP mapean este envoltorio a una jerarquía de excepciones tipada, así ramificas según una clase (y lees code, type, param, request_id) en vez de parsear JSON. Tu API key nunca se incluye en ninguna excepción.

import {
  FactuareaError,
  ValidationError,
  RateLimitError,
} from "@factuarea/sdk";

try {
  await factuarea.invoices.create(body);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(error.fields);     // { client_id: ["obligatorio"], … }
  } else if (error instanceof RateLimitError) {
    console.error(error.retryAfter); // seconds to wait
  } else if (error instanceof FactuareaError) {
    console.error(error.code, error.type, error.requestId);
  }
}

La jerarquía también exporta AuthenticationError, NotFoundError, ConflictError, ServerError y ConnectionError.

use Factuarea\Sdk\Models\Errors\ErrorThrowable;

try {
    $factuarea->invoices->publicApiV1InvoicesCreate($body);
} catch (ErrorThrowable $e) {
    $error = $e->container->error;
    echo $error->type->value;  // e.g. "invalid_request_error"
    echo $error->code;         // e.g. "parameter_invalid"
    echo $error->param;        // e.g. "client_id"
    echo $error->requestId;    // quote this to support
}

Consulta SDKs › Gestión de errores para ver la jerarquía completa. La política de reintentos de abajo la aplican automáticamente ambos SDKs.

request_id y soporte

Toda respuesta incluye un request_id. Adjúntalo a cualquier ticket o petición a support@factuarea.com:

Subject: 422 on POST /v1/invoices — request_id req_01JBVH7K9Y4N3CDQ2EHJB1AGSV

Con el request_id correlacionamos logs, métricas y trazas para investigar rápido.

Estrategia de reintentos

  • 4xx excepto 429no reintentes: el error está en la petición. Corrígelo y reenvía.
  • 429 → respeta el header Retry-After. Implementa back-off exponencial con jitter.
  • 5xx → back-off exponencial (2^n * 100ms) con jitter, máximo 5 intentos.

Stripe publica un patrón canónico que también aplica aquí: stripe.com/docs/error-handling.

En esta página