Export value conventions

One set of value rules for every export payload. Decimals are strings; integers are numbers.

Every SpreadSpace export payload (tax return spreads, financial statement spreads, bank statement analytics) uses one set of value conventions, declared by schema_version at the payload root. Parse against these rules and you never need to special-case a field.

The wire rule, inside these payloads: Decimals are strings; integers are numbers.

Numbers

KindYou receiveRules
Money"1250000.00"String, exactly 2 decimal places, . decimal point, no thousands separators, no currency symbols. Base units: never “in thousands”, even where the source form prints that way.
Ratios (dscr, fccr, leverage_ratio)"1.4167"String, at most 4 decimal places. Round for display yourself.
Percentages (*_percent)"5.25"String, at most 4 decimal places. "5.25" means 5.25%, never 0.0525. Every percentage field name ends in _percent.
Basis points (*_bps)275JSON integer. 275 means 2.75%. Every basis-point field name ends in _bps.
Counts, fiscal_year, months_covered, sort_order12JSON integers.

Rounding is banker’s rounding (round half to even), applied once at serialization. There is no field named rate: a field is either *_percent or *_bps, so its scale is always explicit.

Signs

Negative means outflow or contra: "-8200.55". There are no parentheses-negatives and no separate sign flags. Where a classification exists (for example flow_direction: "INFLOW" | "OUTFLOW" on bank lines), it is carried alongside the signed amount, so you never have to infer meaning from the sign alone.

Null versus zero

null means the value is absent from the source document. "0.00" means the document affirmatively shows zero. These are different facts and both are always emitted: keys are never dropped.

Dates and timestamps

  • Business dates are ISO 8601 date-only strings: "2023-12-31". No time, no timezone. Do not parse them with new Date("2023-12-31") in JavaScript (that yields UTC midnight and renders off by one day in US timezones); split the string or use a date-only parser.
  • Timestamps (generated_at, audit fields) are RFC 3339 UTC with a Z suffix, seconds precision: "2026-07-20T13:30:05Z".

Fiscal periods

Never a bare label like "FY23". Always a struct:

1{
2 "period_start": "2023-04-01",
3 "period_end": "2023-11-30",
4 "period_type": "ANNUAL",
5 "fiscal_year": 2023,
6 "months_covered": 8
7}

period_type is ANNUAL, INTERIM, or TTM. A short-year return stays ANNUAL with months_covered under 12. Non-calendar fiscal year ends are just what the dates say.

Identifiers

EINs, account numbers, and routing numbers are always strings, and leading zeros are preserved: "041234567". Where a display context needs a masked variant, it is a separate field (for example account_last4).

Record identifiers (borrower_id, loan_id, job_id, document ids) are opaque strings. Store and compare them byte-for-byte; never parse meaning from an id or validate its shape, which can change. Where an identifier carries a stable prefix (evt_ events, whsec_ webhook secrets, API keys’ ss_live_ / ss_test_ / ss_embed_), the prefix is part of the value.

Key casing

Envelope fields are snake_case (borrower_id, extracted_document_id, report_data). Keys inside a document’s stored report_data body keep the casing they were extracted and stored with: bank statement bodies, for example, carry camelCase keys such as accountNumberLast4 and dailyBalances. Match keys exactly as each endpoint’s example shows them.

Enums

Enums are closed, versioned sets serialized as string tokens (numeric tokens are rejected). Wherever a value is mapped from an open source set, the enum includes an OTHER escape and a sibling *_raw string carries the source value verbatim, so nothing is silently coerced (statement_type + statement_type_raw). Two enums have no OTHER by design: period_type is a fixed set (ANNUAL | INTERIM | TTM) defined by the fiscal period contract, and flow_direction is derived from the amount with the source rail carried verbatim in transaction_type_raw.

Provenance

Values may carry an optional provenance object pointing back at the source document: { "source_doc_id": "...", "page": 3, "field_ref": "line_7_officer_compensation" }. page is 1-based; field_ref is the stable field handle: the form-line key for scalar tax lines, a dotted path for columnar schedule values (schedule_l.line_1_cash.ending, where the line’s period object distinguishes the beginning and ending balance columns). Map by field_ref, never by row position: line sets can grow additively.

Currency

currency is declared once at the envelope root as an ISO 4217 code (currently always "USD"). There are no per-value currency objects.

Versioning

schema_version (semver) sits at the payload root. Within a major version, changes are additive only: new optional fields and new enum values may appear; existing fields never change name, type, scale, or meaning. Build your parser to ignore unknown fields.