Currencies, amounts & dates
Three conventions show up on every endpoint that handles money or time: the Money envelope, ISO 4217 currency codes with per-currency scale, and UTC datetimes in RFC 3339 form.
Money on the wire
Every monetary amount in the API is a JSON object - never a bare number, never a minor-unit integer:
Code
| Field | Notes |
|---|---|
value | Decimal string, scaled to the currency's precision. JSON numbers are avoided to keep arithmetic exact across languages and to preserve trailing zeros ("100.00" ≠ "100"). |
currency | ISO 4217 code, three uppercase letters. |
The wire scale is fixed per currency:
| Currencies | Scale | Example value for one whole unit |
|---|---|---|
| USD, EUR, GBP, CHF, AUD, CAD, … (most) | 2 | "1.00" |
| JPY, KRW, ISK, BIF, CLP, … (zero-decimal) | 0 | "1" |
| BHD, IQD, JOD, KWD, OMR, TND, LYD | 3 | "1.000" |
| CLF, UYW | 4 | "1.0000" |
Any other 3-letter ISO 4217 code defaults to scale 2. Inputs you send with extra precision are rounded down to the registered scale at the boundary.
Use a decimal library on the client side (
bigdecimal.js, PythonDecimal, Goshopspring/decimal, JavaBigDecimal). Parsingvalueas a JavaScriptNumberwill silently lose precision once amounts exceedNumber.MAX_SAFE_INTEGERminor units (~$90 trillion at scale 2 - fine for invoices, not fine for ledgers that aggregate without rounding).
Currency codes
Codes are validated structurally as exactly three uppercase A–Z letters. The API is permissive - any well-formed code is accepted with default scale 2. Per-currency scale (above) is the only currency-specific behaviour the platform encodes; everything else is opaque to the billing engine.
If your downstream needs a stricter allowlist (typical for payment-gateway integrations that only support a fixed set), enforce it on your side; the API won't.
Multi-currency
Customers carry a preferred_currency:
Code
A product can have multiple prices in different currencies attached to the same plan:
Code
When the customer's preferred currency matches a price, the invoice charges that price directly. When it doesn't, the catalog price is converted via the active FX rate at billing time and the invoice line records the rate used (exchange_rate_id) plus which FX policy resolved it (fx_policy_applied). The full FX selection model - the three candidate rates pinned per invoice (period_start_exchange_rate_id, issue_exchange_rate_id, segment_exchange_rate_id) and the four policies that pick between them - is documented in Exchange Rates.
Dates and timestamps
| Type | Wire format | Example |
|---|---|---|
| Datetime | RFC 3339 (ISO 8601 subset), always UTC | "2025-03-15T14:30:00Z" |
| Date-only | ISO 8601 calendar date | "2025-03-15" |
All datetimes the API emits end in Z (UTC). Timestamps you send should also be UTC; the API does not interpret offsets - convert before sending.
Billing anchor day
Subscriptions accept billing_anchor_day (1–28) to align cycles to a fixed day-of-month. Values above 28 are rejected so the anchor never falls on a day that doesn't exist in February. The current_period_start and current_period_end fields on the subscription tell you exactly when the next charge will be cut.
Filtering by date
Date filters use the standard filter operator grammar:
Code
gt / gte / lt / lte are exclusive / inclusive in the obvious way. There's no separate created_after / created_before query convention - every endpoint follows the same field.operator=value pattern.
Related
- Exchange Rates - FX rate model and the four FX policies (issue / period_start / segment / daily snapshot)
- Pagination - the
field.operator=valuefilter grammar - API reference - per-endpoint money fields and currency-aware DTOs