Factuarea API

Importes y fechas

Cómo representa la API el dinero (EUR, dos decimales), las fechas (YYYY-MM-DD), los timestamps (ISO-8601) y la zona horaria Europe/Madrid usada para el reinicio de cuotas.

Cada valor monetario, de fecha y de hora de la API pública sigue un pequeño conjunto de convenciones fijas. Son las mismas en todos los recursos, así que en cuanto las gestionas en un sitio tu cliente funciona en todas partes.

Dinero

Los importes siempre van en euros (EUR) — el campo currency está presente en todos los documentos y es "EUR" en v1 (ISO 4217). Todavía no hay soporte multidivisa.

Los importes llevan dos decimales (precisión de céntimos). La representación canónica es un string decimal con exactamente dos decimales, al estilo Stripe:

{ "price": "1234.56" }

Algunos recursos emiten actualmente los importes como números JSON (floats) en lugar de strings decimales — por ejemplo el total, el subtotal o el unit_price de un documento vuelven como 1802.9, 968, 100. Escribe tu parser para que acepte tanto un string como un número en cualquier campo de dinero, y normalízalo a un tipo decimal fijo en tu lado (p. ej. Decimal en Python, un big-decimal o un entero de unidades menores en JS). Nunca guardes el dinero como un float binario en crudo.

Deja que la API calcule los totales

No redondees ni calcules por adelantado. Envía los datos en crudo de cada línea (quantity, unit_price, discount, el *_id del impuesto) y deja que la API derive el subtotal, el IVA, el recargo, la retención y el total general. El servidor es la única fuente de verdad para cada total — si redondeas los importes de línea por tu cuenta antes de enviarlos, tus cifras pueden divergir de lo que almacena la API.

El total de un documento sigue una sola fórmula en toda la API:

total = subtotal + total_vat + total_surcharge − total_retention

Si necesitas previsualizar el desglose antes de crear un documento — para un resumen de pedido, un carrito o para conciliar tus propias cifras — llama a POST /v1/taxes/calculate-totals con las líneas y lee el subtotal, el total_vat, el total_surcharge, el total_retention y el total calculados (importes en EUR), más un desglose por línea en el mismo orden:

{
  "subtotal": 250,
  "total_vat": 52.5,
  "total_surcharge": 0,
  "total_retention": 15,
  "total": 287.5,
  "lines": [
    { "subtotal": 100, "vat_amount": 21, "surcharge_amount": 0, "retention_amount": 0, "total": 121 }
  ]
}

El mismo desglose de impuestos por línea aplica a todos los documentos de venta: facturas, presupuestos, proformas y albaranes aceptan todos un retention_rate y un surcharge_rate por línea (retención de IRPF y recargo de equivalencia, 0–100), y su cabecera lleva los total_vat, total_surcharge y total_retention agregados. La misma fórmula se cumple en todas partes. Cada línea además devuelve retention_rate y surcharge_rate en la respuesta, para que puedas reconciliar el desglose línea a línea.

El recargo de equivalencia sigue pares legales fijos. Cuando una línea declara un surcharge_rate, debe coincidir con el tipo de IVA de esa línea según el régimen español: 21% → 5.2%, 10% → 1.4%, 4% → 0.5%, 0% → 0%. Un par ilegal (p. ej. tax_rate: 21 con surcharge_rate: 1.4) se rechaza con 422 y los pares permitidos se devuelven en error.allowed_values. Envía solo el recargo que admite el tipo de IVA de la línea.

Facturas de compra — impuesto por línea

Una factura de compra registra lo que te cobró un proveedor, así que cada una de sus líneas acepta unos calificadores fiscales extra que el lado de venta cubre a su manera. En CreatePurchaseInvoiceRequest.lines[] puedes fijar:

CampoTipoSignificado
retention_ratenumber (0–100)Retención de IRPF aplicada a la línea.
surcharge_ratenumberRecargo de equivalencia, mismos pares legales que en venta.
vat_deductiblebooleanInformativo — marca el IVA como deducible. No cambia el importe pagado.
exemption_reasonenum o nullPor qué la línea está exenta o no sujeta (ver abajo).

El total por línea sigue la misma forma que en el lado de venta, restando la retención y sumando el recargo:

total de línea = subtotal + impuestos − retention_amount + surcharge_amount

El surcharge_rate de una línea de compra sigue los mismos pares legales IVA↔recargo que una línea de venta, validados en el servidor: 21 → 5.2, 10 → 1.4, 4 → 0.5, 0 → 0. El par 0 → 0 también es válido en el lado de compra — una línea exenta o a tipo cero lleva un recargo cero. Un par ilegal se rechaza con 422.

exemption_reason califica por qué una línea queda fuera del IVA ordinario. Es un enum o null (o ausente), lo que significa que la línea hereda la calificación de la cabecera de la factura, o no declara ninguna:

ValorClase
E1, E2, E3, E4, E5, E6Exenta (causa de exención de la LIVA)
N1, N2No sujeta (causa de no sujeción)
nullHeredar de la cabecera / ninguna declarada

Una línea de factura de compra que lleva a la vez un recargo y un motivo de exención:

{
  "description": "Wholesale goods",
  "quantity": 10,
  "unit_price": "50.00",
  "tax_rate": 21,
  "surcharge_rate": 5.2,
  "retention_rate": 0,
  "vat_deductible": true,
  "exemption_reason": null
}
curl -s -X POST https://api.factuarea.com/v1/purchase_invoices \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "supplier_id": "01931b3e-...s01",
    "received_on": "2026-03-15",
    "lines": [
      { "description": "Exempt service", "quantity": 1, "unit_price": "200.00", "tax_rate": 0, "surcharge_rate": 0, "exemption_reason": "E1" }
    ]
  }' | jq '.data | {subtotal, total_vat, total_surcharge, total_retention, total}'

Céntimos en los informes fiscales. Los endpoints fiscales agregados (Modelo 303 / 347 vía /v1/tax_reports/*) devuelven sus importes como céntimos enteros, no como decimales de EUR — p. ej. una base imponible acumulada de 25000 significa 250.00 €. Esto está documentado campo a campo en la spec; trata las cifras de los informes fiscales como unidades menores y divide por 100 solo para mostrarlas.

Fechas

Las fechas de calendario (sin componente horario) usan YYYY-MM-DD — la forma de fecha completa ISO-8601 / RFC 3339. Esto cubre campos como issued_on, due_on, paid_on, valid_until, delivery_date, received_on, start_on y end_on:

{
  "issued_on": "2026-03-15",
  "due_on": "2026-04-14",
  "paid_on": "2026-03-20"
}

Envía las fechas en el mismo formato. Una fecha no tiene hora ni zona horaria — es el día de calendario tal como queda registrado para el documento.

Timestamps

Los campos de instante temporal (metadatos de auditoría y ciclo de vida como created_at, updated_at, signed_at, last_delivery_at) usan strings de fecha-hora ISO-8601 / RFC 3339 completos. La mayoría se emiten en UTC con un sufijo Z:

{ "created_at": "2026-05-15T10:34:21Z" }

Algunos timestamps llevan en su lugar un offset Europe/Madrid explícito (+01:00 en invierno, +02:00 en verano):

{ "created_at": "2026-04-15T10:31:05+02:00" }

Ambas formas son ISO-8601 válidas y denotan el mismo tipo de valor: un instante exacto. Parsea el offset — no asumas que el string siempre está en UTC. Un parser ISO-8601 en condiciones (Instant.parse, datetime.fromisoformat, new Date(...), Carbon::parse) gestiona Z y ±hh:mm de forma idéntica y normaliza al instante absoluto.

Zona horaria para las cuotas

La cuota mensual del rate limit se reinicia el día 1 de cada mes natural a las 00:00 Europe/Madrid (CET/CEST), no en UTC. La cuota por minuto es una ventana deslizante y la cabecera X-RateLimit-Reset es un UNIX timestamp (segundos desde el epoch, independiente de la zona horaria). Consulta Rate limits para la semántica completa de las ventanas.

Siempre que la API necesita una única referencia de calendario civil para un límite de negocio — periodos fiscales, el reinicio de la cuota mensual — esa referencia es Europe/Madrid.

Referencia rápida

ValorFormatoEjemplo
DineroEUR, dos decimales — string decimal (algunos campos emiten un número)"1234.56" / 1802.9
DivisaISO 4217, siempre EUR en v1"EUR"
Importes de informes fiscalesCéntimos enteros (unidades menores)25000 → 250.00 €
FechaYYYY-MM-DD (ISO-8601 fecha completa)"2026-03-15"
TimestampISO-8601 fecha-hora, normalmente UTC Z, a veces ±hh:mm"2026-05-15T10:34:21Z"
Calendario de cuotas / fiscalHora civil Europe/Madriddía 1, 00:00 CET/CEST

En esta página