Loki Docs
Tools › Transactions › Log transactions in bulk

Log transactions in bulk

log_transactions_batchAdds
What it does
Adds a new record and leaves what you had alone. Safe to retry: the same call twice changes nothing more.
Permission
Transactions, change transactions:write

What your assistant is told

Record up to 500 transactions into ONE account in a single call — the way to import a bank statement the user gave you. Read the file, find or create the account (list_accounts / create_account), map each line, then send them here; page a longer statement in calls of 500. Each row is the same shape as log_transaction plus its OWN idempotencyKey — derive it deterministically from the line (e.g. account + bank id, or account + date + amount + line number) so re-sending the same statement is safe. Every row's occurredAtIso MUST carry the user's UTC offset — a statement usually prints a bare booking date, so ASK the user for their offset BEFORE you send the batch and stamp each date with it ("2026-06-17T00:00:00+03:00"); without one the row is an error, and guessing UTC would file the first of a month into the one before. Always pass the bank's transaction id as externalId when the statement has one. Rows are judged one by one: the answer lists every row in input order with status created, existing (that key already wrote it), duplicate (that externalId is already in the account; nothing written) or error (with code + message; nothing written for that row). Valid rows are saved even when others fail, so fix and resend only the error rows. Report the counts to the user. Does NOT create categories or accounts. Lines that move money between the user's own accounts (to savings, a card top-up, between their current accounts) are kind "transfer" with no category and, when known, a counterpartAccount — see log_transaction. RECONCILIATION — read this before you report anything: the user may already have logged a payment by hand or from a receipt before their bank booked it. A row that looks like one of those comes back as match_candidate: NOTHING was written, and the row carries candidates (each with its id, date, amount, description and a score of "exact", "likely" or "near") best first. A match_candidate row is A QUESTION FOR THE USER, never yours to settle: show them the line and the candidates and ask which it is. If they confirm one, call attach_statement_line with that transaction and this line. If they say it is a different payment, send the row again with newEvenIfSimilar true. The row's idempotencyKey is unspent either way, so resending it is safe. Never guess, never pick the top candidate on their behalf, and never resend with newEvenIfSimilar just to clear the question — that would duplicate the payment in their books.

Parameters

  • rowslist of object, requiredThe lines to write, 1 to 500.
  • accounttext, optionalAccount id, alias or IBAN every row goes into. Omit only when the user has a single account.