Amounts & dates
How the API represents money (EUR, two decimals), dates (YYYY-MM-DD), timestamps (ISO-8601) and the Europe/Madrid timezone used for quota resets.
Every monetary, date and time value in the public API follows a small set of fixed conventions. They are the same across every resource, so once you handle them in one place your client works everywhere.
Money
Amounts are always in euros (EUR) — the currency field is present on
every document and is "EUR" in v1 (ISO 4217).
There is no multi-currency support yet.
Amounts carry two decimal places (cents precision). The canonical representation is a decimal string with exactly two decimals, Stripe-style:
{ "price": "1234.56" }Some resources currently emit amounts as JSON numbers (floats) rather
than decimal strings — for example a document's total, subtotal or
unit_price come back as 1802.9, 968, 100. Write your parser to accept
both a string and a number for any money field, and normalise to a fixed
decimal type on your side (e.g. Decimal in Python, a big-decimal / minor-units
integer in JS). Never store money as a raw binary float.
Let the API compute totals
Do not pre-round and do not pre-compute. Send the raw inputs of each line
(quantity, unit_price, discount, the tax *_id) and let the API derive
the subtotal, VAT, surcharge, retention and the grand total. The server is the
single source of truth for every total — if you round line amounts yourself
before sending them, your figures can drift from what the API stores.
The total of a document follows one formula across the whole API:
total = subtotal + total_vat + total_surcharge − total_retentionIf you need to preview the breakdown before creating a document — for an
order summary, a cart, or to reconcile your own figures — call
POST /v1/taxes/calculate-totals with the lines and read back the computed
subtotal, total_vat, total_surcharge, total_retention and total
(amounts in EUR), plus a per-line breakdown in the same order:
{
"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 }
]
}The same per-line tax breakdown applies to every sales document, not just
invoices: quotes, proformas and delivery notes also accept a per-line
retention_rate and surcharge_rate (IRPF withholding and equivalence
surcharge, 0–100), and their header carries the aggregated total_vat,
total_surcharge and total_retention. The same formula holds everywhere.
Cents in tax reports. Aggregated fiscal endpoints (Modelo 303 / 347 via
/v1/tax_reports/*) return their amounts as integer cents, not EUR
decimals — e.g. an accumulated taxable base of 25000 means 250.00 €. This
is documented per field in the spec; treat tax-report figures as minor units
and divide by 100 only for display.
Dates
Calendar dates (no time component) use YYYY-MM-DD — the
ISO-8601 / RFC 3339 full-date form.
This covers fields such as issued_on, due_on, paid_on, valid_until,
delivery_date, received_on, start_on and end_on:
{
"issued_on": "2026-03-15",
"due_on": "2026-04-14",
"paid_on": "2026-03-20"
}Send dates in the same format. There is no time and no timezone on a date — it is the calendar day as recorded for the document.
Timestamps
Instant-in-time fields (audit and lifecycle metadata such as created_at,
updated_at, signed_at, last_delivery_at) use full ISO-8601 / RFC 3339
date-time strings. Most are emitted in UTC with a Z suffix:
{ "created_at": "2026-05-15T10:34:21Z" }Some timestamps carry an explicit Europe/Madrid offset instead
(+01:00 in winter, +02:00 in summer):
{ "created_at": "2026-04-15T10:31:05+02:00" }Both forms are valid ISO-8601 and denote the same kind of value: an exact
instant. Parse the offset — do not assume the string is always UTC. A
proper ISO-8601 parser (Instant.parse, datetime.fromisoformat,
new Date(...), Carbon::parse) handles Z and ±hh:mm identically and
normalises to the absolute instant.
Timezone for quotas
The monthly rate-limit quota resets on day 1 of each calendar month at
00:00 Europe/Madrid (CET/CEST), not UTC. The per-minute quota is a sliding
window and the X-RateLimit-Reset header is a UNIX timestamp (seconds since
the epoch, timezone-independent). See Rate limits for the
full window semantics.
Whenever the API needs a single civil-calendar reference for a business boundary — fiscal periods, the monthly quota reset — that reference is Europe/Madrid.
Quick reference
| Value | Format | Example |
|---|---|---|
| Money | EUR, two decimals — decimal string (some fields emit a number) | "1234.56" / 1802.9 |
| Currency | ISO 4217, always EUR in v1 | "EUR" |
| Tax-report amounts | Integer cents (minor units) | 25000 → 250.00 € |
| Date | YYYY-MM-DD (ISO-8601 full-date) | "2026-03-15" |
| Timestamp | ISO-8601 date-time, usually UTC Z, sometimes ±hh:mm | "2026-05-15T10:34:21Z" |
| Quota / fiscal calendar | Europe/Madrid civil time | day 1, 00:00 CET/CEST |