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_retentionSi 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:
| Campo | Tipo | Significado |
|---|---|---|
retention_rate | number (0–100) | Retención de IRPF aplicada a la línea. |
surcharge_rate | number | Recargo de equivalencia, mismos pares legales que en venta. |
vat_deductible | boolean | Informativo — marca el IVA como deducible. No cambia el importe pagado. |
exemption_reason | enum o null | Por 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_amountEl 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:
| Valor | Clase |
|---|---|
E1, E2, E3, E4, E5, E6 | Exenta (causa de exención de la LIVA) |
N1, N2 | No sujeta (causa de no sujeción) |
null | Heredar 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
| Valor | Formato | Ejemplo |
|---|---|---|
| Dinero | EUR, dos decimales — string decimal (algunos campos emiten un número) | "1234.56" / 1802.9 |
| Divisa | ISO 4217, siempre EUR en v1 | "EUR" |
| Importes de informes fiscales | Céntimos enteros (unidades menores) | 25000 → 250.00 € |
| Fecha | YYYY-MM-DD (ISO-8601 fecha completa) | "2026-03-15" |
| Timestamp | ISO-8601 fecha-hora, normalmente UTC Z, a veces ±hh:mm | "2026-05-15T10:34:21Z" |
| Calendario de cuotas / fiscal | Hora civil Europe/Madrid | día 1, 00:00 CET/CEST |