Journal lines
Ledger lines, journal by journal, written and read back. Journal lines explain the figures the trial balance carries — they never move them, so a difference between the two is reported and never blocks a write.
Writing journal lines
The endpoints
| Method | Path | What it does |
| POST | /api/v1/organisations/{organisation}/transactions |
Writes the lines |
| POST | /api/v1/organisations/{organisation}/transactions/validate |
The same checks; nothing is written |
Needs a bearer token, a credential issued for that company, and the credential's transactions.write
scope.
The body
{
"declarations": {
"source_reference": "netsuite-gl",
"period": "2026-09",
"value_basis": "company_base",
"rate_direction": "document_to_base",
"base_currency": "SGD",
"mode": "replace_period"
},
"records": [
{
"journal_external_id": "JE-2026-0912",
"journal_number": "J-0912",
"line_external_id": "JE-2026-0912-1",
"date": "2026-09-14",
"account_code": "4000",
"side": "credit",
"amount_base": 1090.00,
"document_external_id": "INV-10241",
"document_line_id": "INV-10241-1",
"description": "Freight forwarding, September"
},
{
"journal_external_id": "JE-2026-0912",
"journal_number": "J-0912",
"line_external_id": "JE-2026-0912-2",
"date": "2026-09-14",
"account_code": "1100",
"side": "debit",
"amount_base": 1090.00,
"description": "Invoice INV-10241"
}
]
}
Declarations
| Declaration | Required | Value |
source_reference | yes | Your own id for the feed, 1–64 characters. |
period | yes | The month, YYYY-MM. |
value_basis | yes | company_base when
amount_base is already in the reporting currency, document when it is
not. |
rate_direction | yes | Which way exchange_rate converts. |
base_currency | yes | The company's reporting currency. |
scenario | no | Actual (default), Budget or Forecast. |
mode | no | replace_period (default), replace_all
or merge. |
webhook_url | no | Called once when the request finishes or fails. |
The fields
| Field | Type | Required | What it means |
journal_external_id | string | yes | The journal this line belongs
to. Half of the record's identity. |
line_external_id | string | yes | The line's own identity within the
journal. The other half. |
journal_number | string | no | The number a reader sees for the
journal. |
date | date | yes | The posting date. |
account_code | string | yes | The code in this company's accounts. An
unknown code is refused with account_not_found. |
account_name | string | no | Stated beside the code, as a reader sees
it. |
side | string | yes | debit or
credit. |
amount_base | number | yes | The amount in the reporting currency, a
positive number on its own side. |
currency_code | string | no | The document currency, when it is not
the base currency. |
exchange_rate | number | no | The rate the line was posted at, in the
direction declared. |
document_amount | number | no | The amount in the document
currency. |
tax_amount, tax_type | number, string | no | The tax the
line states and the source's tax code for it. |
reference, description | string | no | Stored exactly as
sent. |
dimensions | array | no | Named values: tracking, class, reporting
tag. |
document_external_id, document_line_id | string | no |
The document and line this posting explains, when it names one. |
What it does
- Any slice of a month, any time. Unlike a trial balance, journal lines need not be sent
whole:
merge and replace_period both accept a part.
- Each journal must balance. Debits and credits per journal across the whole payload must
agree; a difference is refused with
journal_does_not_balance and each offending line is
named. That is arithmetic, not completeness.
- A line a document already explains is suppressed. Where a document line explains the
same posting, the count comes back in
suppressed so the figures are not counted
twice.
- The comparison with the trial balance is reported, never blocking.
result.control_total answers compared: true with the per-account difference
when a trial balance exists for that month, and compared: false when none does. A
half-loaded month is expected to differ.
What it answers
{
"id": 95, "kind": "transactions", "source_reference": "netsuite-gl",
"period": "2026-09", "status": "written", "records_received": 2,
"counts": { "created": 2, "updated": 0, "unchanged": 0, "rejected": 0 },
"suppressed": 0,
"result": { "control_total": { "compared": true, "accounts": [] } },
"recalculation": null,
"written_at": "2026-10-23T06:40:18+00:00",
"links": { "self": "…/requests/95", "results": "…/requests/95/results" }
}
Refusals
| Code | When |
journal_does_not_balance | The journal's two sides differ |
account_not_found | The code is not in this company's accounts |
invalid_value | side is not debit or credit |
invalid_value | amount_base is not a positive number |
duplicate_record_identity | Two records state the same journal and line id |
Reading journal lines
The journal lines this API was sent, journal by journal, answered as they were accepted. They explain the
figures the trial balance carries rather than moving them, so a month's lines and its trial balance can
differ: the write side reports that difference as control_total and never blocks on it. A line
a document line already explains is answered here too, and its document_external_id says so -
anything that totals these lines counts it once.
The endpoint
| Method | Path | Answers |
| GET | /api/v1/organisations/{organisation}/transactions |
The lines, newest journal first |
Needs a bearer token, a credential issued for that company, and the transactions.read scope.
The filters
| Parameter | What it does |
period | One month, Mon-YYYY. |
journal_external_id | One journal. |
account_code | One account's lines. |
document_external_id | The lines a document explains. |
page, per_page | Paging, as everywhere. |
What it answers
{
"data": [
{
"journal_external_id": "JE-2026-0912", "journal_number": "J-0912",
"line_external_id": "JE-2026-0912-1",
"date": "2026-09-14",
"account_code": "4000", "account_name": "Sales Revenue",
"side": "credit", "amount_base": 1090.00,
"currency_code": "USD", "exchange_rate": 1.0, "document_amount": 1090.00,
"tax_amount": 0.00, "tax_type": null,
"reference": null, "description": "Freight forwarding, September",
"dimensions": [],
"document_external_id": "INV-10241", "document_line_id": "INV-10241-1",
"source": "API"
}
],
"page": 1, "per_page": 50, "total": 2, "has_more": false
}
| Field | What it says |
journal_external_id, line_external_id | The line's identity, as the
source stated it. |
journal_number | The number a reader sees for the journal. |
date | The posting date. |
account_code, account_name | The account the line moved. |
side, amount_base | debit or credit, and the
amount in the company's reporting currency. |
currency_code, exchange_rate, document_amount | The
document's currency, its rate, and the amount in that currency. |
dimensions | The named values the line was posted with: tracking, class, reporting
tag. |
document_external_id, document_line_id | The document and line the
posting explains, when it names one. |
source | Who wrote the line. |
An unbalanced journal cannot be stored through the API, so what is read here is arithmetic that
balances.