Skip to main content

Timestamps and money

Timestamps and money are two primitives you want to get right. Bugs in either one can be surprisingly hard to spot.

Timestamps​

Every timestamp uses RFC 3339, is in UTC, and ends with Z:

{
"created_at": "2026-07-29T14:32:05Z",
"expires_at": "2029-04-30T23:59:59Z"
}
  • Always UTC. Never local time or an offset other than Z.
  • Second precision. Sub-second precision isn't returned.
  • Timestamp fields end in _at.

Dates that don't include a time, such as a date of birth, use YYYY-MM-DD and don't use the _at suffix:

{ "date_of_birth": "1990-04-12" }

Send timestamps in the same format. Timestamps with an offset, such as +08:00, are accepted and normalized to UTC in the response.

Timestamps without a timezone are rejected with 400 invalid_parameter. We don't guess the timezone, because that's how you end up with dates and times being eight hours off.

Money​

Money is always represented as an object, never as a bare number:

{
"amount": 1050,
"currency": "SGD"
}

amount is an integer in minor units. For example, 1050 means S$10.50. currency is an uppercase ISO 4217 code.

Never use a float​

This isn't just a style preference. Binary floating-point numbers can't represent most decimal fractions exactly:

0.1 + 0.2 === 0.3; // false
(0.1 + 0.2).toFixed(20); // "0.30000000000000004441"

That kind of error might look harmless for one transaction, but accumulate it over a month and your reconciliation can end up off by an amount nobody can explain. Integers in minor units don't have this problem.

Keep the integer representation all the way through your stack: database columns, application types, and JSON parsing. Be careful with parsers that convert large integers to floats, since amounts above 2⁵³ minor units can be corrupted.

Minor units are not always two decimals​

CurrencyMinor unitsamount: 1000 means
SGD, USD, EUR, GBP210.00
JPY, KRW01,000
BHD, KWD, JOD31.000

Don't assume every currency has two decimal places. Use the currency's actual exponent. ISO 4217 publishes it, and established money libraries generally provide it for you.

Rounding​

We don't round amounts you send. They're used exactly as provided.

When the platform has to produce a fractional result, such as during a currency conversion or percentage fee calculation, the result is rounded half up to the currency's minor unit. The response includes both the value before rounding and the final rounded value.

Negative amounts​

amount can be negative when the direction matters, such as for a refund line or ledger entry. If a field represents a magnitude rather than a signed value, negative amounts are rejected with 400 invalid_parameter.

Comparing money​

Two money objects can only be compared when their currency values match. The API never converts currencies implicitly.

If an operation mixes currencies, it returns 422 with currency_mismatch.