# Transaction JSON Schema for Converted Statements

- Author: FastBankConvert (https://www.fastbankconvert.com)
- Published: 2026-08-31
- Updated: 2026-08-31
- Category: CSV, JSON and text output
- Canonical: https://www.fastbankconvert.com/blog/transaction-json-schema

> A transaction JSON schema for converted statements: required fields, date and amount types, optional balances, and validating each record.
A transaction JSON schema is a JSON file that describes what every transaction record from a converted statement must contain: a date string, a description, a numeric amount, and optional fields such as a running balance. A validator checks each record against it and names the record and field that break a rule, before bad data reaches your code.

## What belongs in a transaction JSON schema?

A transaction JSON schema has three parts: the type of the record, the list of fields it may contain with a rule for each, and the list of fields it must contain. Written in JSON Schema, a widely used vocabulary for describing JSON, those parts are the `type`, `properties` and `required` keywords.

The schema below is an example for one transaction from a converted bank statement. It is a starting point to adapt, not a standard any bank publishes.

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Statement transaction",
  "type": "object",
  "properties": {
    "date": { "type": "string", "format": "date", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
    "description": { "type": "string", "minLength": 1 },
    "amount": { "type": "number" },
    "balance": { "type": ["number", "null"] },
    "reference": { "type": "string" },
    "page": { "type": "integer", "minimum": 1 }
  },
  "required": ["date", "description", "amount"],
  "additionalProperties": false
}
```

Read it top to bottom. The record is an object. It may have six named fields, each with its own rule. Three of them must be present. And no field outside that list is allowed.

A statement as a whole is usually an array of these records, often inside a parent object that holds the account and period. You can describe that parent in the same file, with a `transactions` property whose `items` point at the record schema. Keeping the record schema separate makes it easy to reuse for card statements, savings accounts or any other ledger-like document.

### Why bother with a schema at all?

A schema turns assumptions into checks. Every script that reads converted statements assumes amounts are numbers and dates are dates. When one month's file breaks that assumption, the script fails somewhere far from the cause. A schema fails at the door, with the record number and the field name.

## Required transaction fields and the ones to leave optional

Required transaction fields are the ones every statement line has, whatever the bank: a date, a description and an amount. Make those three required. Everything else, such as the running balance, a check number, a category or the page it came from, varies between banks and layouts, so leave it optional.

The [JSON Schema object reference](https://json-schema.org/understanding-json-schema/reference/object) describes `required` as an array of unique strings, each the name of a property that must appear. A record missing any of them is invalid. Properties not listed under `properties` are ignored unless you also set `additionalProperties`.

Setting `additionalProperties` to `false`, as the example does, means no other properties are allowed. That is useful for catching typos. A converter or script that writes `ammount` instead of `amount` produces a record that fails twice: once for the missing required field and once for the unexpected one. Without that setting, the misspelled key would pass silently and the amount would simply be missing from every total.

### Fields worth adding per project

Some projects need more required fields. A bookkeeping import may need an account identifier on every record so several accounts can share one file. A categorisation tool may need a `direction` field with the values `debit` and `credit`. Add them to `required` for that project rather than to a shared base schema. For the field set a full statement export normally carries, the guide to [converting a bank statement to JSON](/blog/bank-statement-to-json) goes through header and line fields.

## Date and amount types in the transaction schema

Dates in a transaction schema should be strings in year-month-day form, and amounts should be numbers, signed so that money out is negative. JSON has no date type, so a date is always a string; the schema's job is to fix which string form is allowed.

The [JSON Schema type and format reference](https://json-schema.org/understanding-json-schema/reference/type) lists `"format": "date"`, with `2018-11-13` as its example, and says dates follow RFC 3339. The same page warns that by default `format` is only an annotation and does not affect validation, unless the validator is set to treat it as an assertion. That is why the example also has a `pattern` that requires four digits, a hyphen, two digits, a hyphen and two digits. The pattern works in every validator.

Amounts are typed as `number`. The same reference notes that JSON does not have separate integer and floating-point types, so `42` and `42.0` both pass. That suits money printed with or without cents. It does not stop a value such as `86.4` where you expected `86.40`; both are the same number.

### When amounts should be strings

Some teams store money as strings to avoid floating-point rounding in their language. In that case, type the amount as a string with a pattern such as `^-?\d+\.\d{2}$`, which allows an optional minus sign and exactly two decimals. Pick one convention and put it in the schema. Mixing the two in one file is the mistake the schema exists to catch.

## Optional running balance and reference fields

Optional fields in a transaction schema describe data that only some statements print. A running balance appears after each line on many statements but not all, and a reference or check number appears only on certain lines. List them under `properties` so their type is checked when present, and keep them out of `required`.

The example types `balance` as `["number", "null"]`. A `type` array means the value is valid if it matches any of the listed types. That lets a converter write `null` for lines where the statement printed no balance, which is clearer than leaving the key out on some lines and including it on others. A reader of the file sees at once that the balance was looked for and not found.

The `reference` field is a string, even when it looks like digits. Check numbers and transaction references can have leading zeros, and long ones can exceed the range where JSON numbers are exact. [RFC 8259, the JSON standard](https://www.rfc-editor.org/rfc/rfc8259), says implementations agree exactly only on integers up to 2 to the power 53, less one. A string keeps every digit as printed.

### Optional fields in other documents

The same split between required and optional works for other financial PDFs converted to JSON. A rent roll, for example, always has a unit and a rent, but only some units have a lease end date or a deposit. [Converting a rent roll PDF to Excel](/blog/rent-roll-pdf-to-excel) lists the fields such a document carries, and each maps to a required or optional property in the same way.

## Validating each transaction record against the schema

Validating a transaction record means running a JSON Schema validator with the schema and the record, and reading the list of errors it returns. Validators exist for most programming languages and as command-line tools. Run the check on every record of every file, right after conversion and before the data goes anywhere else.

Here are three example records, made up for illustration, checked against the schema above.

**Record 1, valid:**

```json
{ "date": "2026-09-18", "description": "CITY POWER & LIGHT", "amount": -118.72, "balance": 5988.65 }
```

Every required field is present, the date matches the pattern, the amount is a number and the balance is a number.

**Record 2, invalid:**

```json
{ "date": "09/18/2026", "description": "CITY POWER & LIGHT", "amount": "-118.72" }
```

It fails twice. The date does not match the year-month-day pattern. The amount is a string, not a number. Both errors usually come from a converter that copied printed text without parsing it.

**Record 3, invalid:**

```json
{ "date": "2026-09-18", "description": "", "amount": -118.72, "memo": "power bill" }
```

It also fails twice. The description is empty, which breaks `minLength`. And `memo` is not a listed property, so `additionalProperties: false` rejects it.

### Reading validator errors

Most validators report a path to the failing value, such as `/2/amount` for the amount in the third record of an array, plus the rule it broke. Fix the cause in the conversion step, not by editing records by hand, or the same error returns next month.

### After validation

Once records pass, other tools can rely on them. If the next stop is a spreadsheet, [opening a JSON file in Excel](/blog/json-to-excel) shows how the records become rows. If another system wants a flat file, [converting CSV to JSON and back](/blog/csv-to-json) covers the mapping between the two shapes. FastBankConvert, which is getting ready to open, is designed to download a statement's transactions as JSON alongside Excel and CSV; [Get started](/get-started) to be told when trials begin.

## Transaction schema rules that are too strict for real statements?

Transaction schema rules become too strict when they encode one bank's layout as if every statement looked the same. A schema that rejects real, correct records gets switched off, and then it protects nothing. Watch for a few rules that look sensible but fail on ordinary statements.

- **Amounts that must be non-zero.** Some statements print zero-value lines, such as a waived fee or an authorisation reversal. A `not` rule against zero will reject them.
- **Descriptions with a maximum length.** Card purchases with long merchant names and location text can run well past a guessed limit. Use `minLength` only.
- **A required balance.** Many statements omit running balances on some or all lines. Require it only for a bank you know prints it.
- **One sign convention for every account.** Credit card statements often print purchases as positive amounts. Normalise signs in the conversion step, then let the schema check the agreed sign.
- **Dates restricted to the statement month.** A transaction can post on the last day of one period and appear on the next statement. Range checks belong in a reconciliation step, not in the record schema.

A schema checks the shape of each record. It cannot check that the records add up. After validation, total the amounts and compare the result with the change between the opening and closing balance. The two checks together catch both malformed records and missing ones.
