Post many entries as one unit, checked against the count and totals the caller asserts
const url = 'https://example.com/v1/entities/example/batches';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"reason":"example","entries":[{"legs":[{"account":"example","commodity":"example","amount":"-12.00","memo":"example","tax_code":"example"}],"dt":"example","narration":"example","reason":"example","origin":"example","code":"","comment":"","tags":[["example"]],"id":"example"}],"asserted":{"count":1,"totals":[{"commodity":"example","debits_micros":"1200000","credits_micros":"1200000"}]}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/entities/example/batches \ --header 'Content-Type: application/json' \ --data '{ "reason": "example", "entries": [ { "legs": [ { "account": "example", "commodity": "example", "amount": "-12.00", "memo": "example", "tax_code": "example" } ], "dt": "example", "narration": "example", "reason": "example", "origin": "example", "code": "", "comment": "", "tags": [ [ "example" ] ], "id": "example" } ], "asserted": { "count": 1, "totals": [ { "commodity": "example", "debits_micros": "1200000", "credits_micros": "1200000" } ] } }'Post a converted set of entries, and learn before anything commits whether it is the set you sent.
The caller asserts how many entries the batch contains and what each commodity’s debits and credits come to in micros. Both sides are asked for even though a balanced batch makes them equal. The caller computes them from its own source, so a figure transposed on one side disagrees with the other instead of cancelling out.
A refusal is whole and leaves nothing behind, whether the cause is a broken assertion, an unknown account in
the nine-hundredth entry or a date in a closed period. 422 unbalanced names every commodity whose asserted
totals disagree with what was posted, with both numbers. 422 validation covers the count and the maximum
batch size (published as limits.max_batch_entries in /capabilities). A refusal about one entry names
that entry: batch_index, its date and narration, its entry_id when the caller supplied one, and a
path such as entries[900].legs[0].account. A retry under the same Idempotency-Key replays the
original reply instead of posting the batch twice.
The whole batch is recorded as one operations row (intent: the assertion; result: the entries written),
alongside the ordinary audit action each entry keeps.
Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The entity this path is relative to
The entity this path is relative to
Request Bodyrequired
Section titled “Request Bodyrequired”N entries, posted as one unit that is applied whole or refused whole.
This is the write for a conversion: a year of entries an agent has converted from a journal, a spreadsheet or
another system’s export. A loop of single POST /entries calls posts the same rows but proves nothing
about the set. A dropped entry, a retry that posted twice, or a connection lost two thirds of the way
through each leave books that are internally consistent and simply wrong. A batch carries the caller’s own
count and per-commodity totals, and either matches them or leaves nothing behind.
object
Why this batch is being loaded; kept in the record
The entries, in the order they are to be posted
One entry of a batch, in the same shape POST /entries takes.
The only difference is that reason is optional, because the batch has one and repeating it on every entry
of a converted year would be a thousand copies of one sentence. An entry with something more specific to
say carries its own, recorded verbatim on that entry’s audit action.
object
Every leg of the entry; they net to zero per commodity
One leg of a manual journal entry: an account, a currency and a signed amount.
Unlike a line booking’s legs, these are the whole entry. No cash leg is supplied for you, because there is no statement line behind this entry. Two legs or more, netting to zero per commodity.
The entry date, YYYY-MM-DD
What this entry reads as in the books
Hledger tcode, e.g. a document number
Hledger tcomment, kept verbatim
What the caller says this batch contains: the caller’s claim, not the ledger’s derivation.
The ledger never sees the journal, spreadsheet or export the entries were converted from (decisions.md,
“Chunk F1 – the generic statement input”), so it cannot know how many entries there should be. The caller
counted them at the source and states it here, and the ledger refuses a batch that does not match.
object
How many entries this batch contains
Every commodity the batch touches, each named exactly once
What the caller says one commodity comes to across the whole batch, as two magnitudes in micros.
Both are non-negative: debits_micros is the sum of every positive posting of this commodity and
credits_micros of every negative one. Each entry balances, so the two must be equal once the batch is
built, and that is why both are asked for. The caller computes them from its own source, so a figure
transposed on one side of the conversion disagrees with the other and the batch is refused.
object
Sum of the positive postings of this commodity, in micros
Sum of the negative postings of this commodity, as a magnitude
Responses
Section titled “Responses”Successful Response
object
The one operations row this batch was recorded as
The entries that were posted, in submission order
What was actually posted; equal to the assertion, or this would have been refused
What the caller says one commodity comes to across the whole batch, as two magnitudes in micros.
Both are non-negative: debits_micros is the sum of every positive posting of this commodity and
credits_micros of every negative one. Each entry balances, so the two must be equal once the batch is
built, and that is why both are asked for. The caller computes them from its own source, so a figure
transposed on one side of the conversion disagrees with the other and the batch is refused.
object
Sum of the positive postings of this commodity, in micros
Sum of the negative postings of this commodity, as a magnitude
The batch.posted audit action’s event sequence
object
Example
{ "totals": [ { "debits_micros": "1200000", "credits_micros": "1200000" } ]}Bad Request
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Unauthorized
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Forbidden
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Not Found
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Conflict
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Precondition Failed
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Unprocessable Content
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Internal Server Error
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}Service Unavailable
object
Example generated
{ "error": { "code": "example", "message": "example", "retryable": true, "details": [ {} ], "request_id": "example", "next_action": {} }}