Developers
Show batch ingestion and synchronization guides and endpoints, core concepts, and the content hash calculator.
Explore the docs

Core concepts

Money and rounding

Understand server-side cent rounding, derived amounts, and retry identity.

On this page

The API owns monetary rounding for single and batch ingestion. No payload redesign or client-side pre-rounding is required. Extra decimals are accepted; JSON numeric strings are not.

Nearest cent, halfway away from zero

Submitted JSON numberNormalized amount
10.25410.25
10.25510.26
-10.255-10.26
1.0051.01
-0.0040.00
1.005e110.05

Amounts above are shown at two-decimal storage scale; clients send numbers, not strings. Negative examples apply only where the field permits negatives. Zero has no negative sign.

Rounding uses decimal arithmetic on the shortest decimal representation of the parsed JavaScript number, not Math.round(value * 100). Scientific notation is accepted. Precision already lost while constructing or parsing the JSON number cannot be recovered, even with a decimal library.

Fields and bounds

Rounded fields: disbursement_amount, gross_value, upfront_fee, total_expected_margin, approved_amount, operation_revenue, operation_cost, asset_value, depreciation_amount, payment_total, payment_principal, payment_fee.

  • Values must be finite JSON numbers. "10.255" is not accepted as an amount.
  • Rounded values and derived sums must fit NUMERIC(15,2): -9999999999999.99 to 9999999999999.99. Overflow returns 400; extra decimals alone do not.
  • Existing sign restrictions apply to the original submitted value even if it would round to zero. Other numeric bounds are checked after rounding.
  • Ratios such as insurer_coverage, counts, and values inside info are not rounded as money. Optional null/omission behavior remains unchanged.

Round components before deriving amounts

Rounding happens before storing the event, calculating derived amounts, hashing business content, and writing domain rows.

For example, a loan create can contain:

json
{
  "category": "disbursement",
  "data": {
    "step": "create", "disbursement_id": "DSB-ROUND-001", "kind": "loan",
    "client_id": "COMPANY-001", "disbursement_amount": 10.005, "upfront_fee": 10.005,
    "currency": "USD", "total_expected_margin": 1,
    "disbursement_date": "2026-09-14", "maturity_date": "2026-12-14"
  }
}

The client must already exist. With gross_value omitted, the two components each round to 10.01, producing 20.02, not 20.01. The default fee is zero; default gross value is amount plus fee. Explicit gross values are also rounded.

Content hashes are not request idempotency

Use the shared hash module and calculator to check canonical output. API 4.2.0 also repairs creditor inclusion in disbursement hashes under version 2; that separate repair needs an operator-run backfill. The no-backfill statements here refer only to monetary rounding.

Otherwise identical updates with 10.251, 10.254, or 10.25 normalize to the same 10.25 business value. If that content is already stored, an update can return 200 with action: unchanged and no domain side effects.

But reusing one key first with 10.251 and then with 10.254 returns 409 IDEMPOTENCY_KEY_REUSED. Request idempotency identifies the original submitted content, not its rounded equivalent. Preserve the original number and key for exact retries.

Content-hash version remains 2. Existing cent-valued records retain their canonical hashes: this rounding update alone requires no hash-version bump or backfill. See synchronization and the v2 vectors if you compute hashes locally.

Validate locally. Verify in sandbox.
The JSON validator checks structure and rounding. Credentials, existing records, and server-side state still require your test environment.

Search documentation

Start typing to find your next step.

    navigate Enter openLocal, private search