Draft Journal Entries (v2)
Create draft journal entries in batch (v2)
Atomically creates 1–100 draft journal entries in a single transaction. Each entry follows the same restrictions as POST /api/v2/draft-journal-entries: auto-append of the AR/AP leg for locked types, tenant-scoped customerId / vendorId / journalCategoryId / line accountId validation.
If any entry fails validation or persistence the entire batch is rolled back — no draft rows are created. journalCategoryId / customerId / vendorId / accountId from another tenant rejects the whole batch with the matching error code.
Attachments: each entry may include up to 10 base64-encoded attachments[]. A failure on any
entry rolls back the whole batch and deletes every already-uploaded blob — no orphan documents
survive.
Required Scope
journal-entries-write
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Create draft journal entries in batch (v2) › Request Body
Array of 1–100 draft journal entries, carrying at most 10000 lines in total across the batch. Wrapped in a single transaction; one bad entry rolls back the whole batch.
Create draft journal entries in batch (v2) › Responses
Batch created — every draft persisted
totalCreatedNumber of drafts created (= entries.length on success).
Created draft journal entries, ordered to match the request entries[].
List draft journal entries (v2)
Returns a paginated list of draft journal entries owned by the API key's app company. Tenant-scoped: drafts created by other companies are never visible here. Identical response shape to GET /api/v1/draft-journal-entries.
Required Scope
journal-entries-read
query Parameters
pagePage number (1-indexed)
limitMaximum items per page
fromDateOnly include drafts on or after this date (YYYY-MM-DD)
toDateOnly include drafts on or before this date (YYYY-MM-DD)
accountIdFilter by account ID (matches any draft line on this account)
customerIdFilter by customer ID
supplierIdFilter by supplier ID
vendorIdDeprecated — use supplierId. Legacy alias for the supplier filter.
journalTypeFilter by journal type
minAmountOnly include drafts with an amount greater than or equal to this value (inclusive).
maxAmountOnly include drafts with an amount less than or equal to this value (inclusive).
externalSystemIdExact-match filter on your own system's id. Not unique — several drafts may share one id, so expect zero or more results.
searchFree-text search in description and reference fields
List draft journal entries (v2) › Responses
Drafts retrieved
List of draft journal entries
totalTotal number of matching drafts
pageCurrent page number
limitItems per page
totalPagesTotal number of pages
Create a draft journal entry (v2)
Creates a draft journal entry with the V2 restriction matrix enforced at create-time (unlike v1, which only validates on book). For CUSTOMER_INVOICE / CUSTOMER_PAYMENT / SUPPLIER_INVOICE / SUPPLIER_PAYMENT submit only the counter-side lines — the server auto-appends the AR/AP leg on the partner group default account.
Tenant scoping: every customerId / vendorId / journalCategoryId / line accountId is validated against the API key's app company. Cross-tenant or unknown ids reject with 400 (JOURNAL_CATEGORY_NOT_FOUND, ACCOUNT_NOT_FOUND, CUSTOMER_HAS_NO_GROUP, VENDOR_HAS_NO_GROUP, INVOICE_AR_LEG_NOT_ON_GROUP_DEFAULT, PAYMENT_AR_LEG_NOT_ON_GROUP_DEFAULT, INVOICE_AP_LEG_NOT_ON_GROUP_DEFAULT, PAYMENT_AP_LEG_NOT_ON_GROUP_DEFAULT).
Attachments: include up to 10 base64-encoded files in the optional attachments[] field —
same shape and limits as POST /api/v2/journal-entries. Each becomes a Document linked to the
draft; when the draft is booked, the documents migrate to the posted JE and surface via
GET /api/v1/journal-entries/:id/attachments. Rejection paths: an invalid MIME type rejects at
DTO validation with 400 SHARED_VALIDATION_FAILED; malformed base64 reaches the service and
rejects with 400 JOURNAL_ENTRY_INVALID_ATTACHMENT_TYPE.
Required Scope
journal-entries-write
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Create a draft journal entry (v2) › Request Body
entryTypeJournal type. For CUSTOMER_INVOICE / CUSTOMER_PAYMENT / SUPPLIER_INVOICE / SUPPLIER_PAYMENT the AR/AP leg is appended automatically; do not include it in lines.
datePosting date (YYYY-MM-DD).
originalAmountFace value of the document — what the counterparty is invoiced or pays. NOT the sum of lines. On CUSTOMER_INVOICE / CUSTOMER_PAYMENT / SUPPLIER_INVOICE / SUPPLIER_PAYMENT this value IS the appended AR/AP leg (its absolute value), so an account that appears on both sides of the entry — a clearing or pass-through account — must not be counted into it. Sign carries direction: positive = invoice / payment, negative = credit-note / refund.
Counter-side lines (revenue / expense / bank / outlay). For locked types do NOT include the AR/AP leg. The cap is per ENTRY, not per request or per journal category — nothing accumulates across entries, so a company with many unposted drafts is never refused for that reason.
customerIdCustomer reference. Required for CUSTOMER_* types.
supplierIdSupplier reference. Required for SUPPLIER_* types.
descriptionOptional human-readable description.
referenceInvoice number / external reference.
externalSystemIdYour own system's id for this draft. Carried onto the booked journal entry. Not unique — several drafts/entries may share one id. Filter the list endpoint by externalSystemId.
currency^[A-Za-z]{3}$ISO 4217 alpha-3 currency code.
exchangeRateExchange rate from currency to the company base currency, e.g. 7.4612 for EUR to DKK. Must be greater than zero; a non-positive rate is rejected whatever the entry currency, so the same body is accepted or refused identically for every tenant. Omit the field to have the rate resolved from the rate feed at the posting date — supplying one lets an integrator that already converted the amount itself post both legs of a settlement against the same rate. Not applied when the entry is already denominated in the company base currency, where the conversion is the identity.
tagIdsTag IDs applied at the entry level.
journalCategoryIdJournal category ID (UUID). Must belong to the API key's app company; cross-tenant or unknown IDs are rejected with 400 JOURNAL_CATEGORY_NOT_FOUND. When omitted the entry uses the company's default category.
Inline file attachments (base64-encoded). Same shape and limits as v1: maximum 10 attachments per entry, a 10MB ceiling on each file measured AFTER base64 decoding (over it is rejected with DOCUMENT_UPLOAD_FILE_TOO_LARGE), and a 15MB ceiling on the total request body (JSON + base64 payload). Either can be the binding one: base64 inflates a payload by about a third, so two 6MB files each clear the per-file bound and exceed the request one. Each attachment becomes a Document linked to the created journal entry — equivalent to what GET /api/v1/journal-entries/:id/attachments returns. An invalid MIME type rejects at DTO validation with 400 SHARED_VALIDATION_FAILED; malformed base64 reaches the service and rejects with 400 JOURNAL_ENTRY_INVALID_ATTACHMENT_TYPE. Either rejects the whole entry, and in a batch the whole batch rolls back.
vendorIdDeprecated — use supplierId. Legacy alias; sending both with different values returns 400.
Create a draft journal entry (v2) › Responses
Draft created
idDraft journal entry ID
dateDate in YYYY-MM-DD format
amountHow large this draft is, in your company base currency — the one your company reports in. A draft entered as EUR 1,000 by a company reporting in DKK is 7450 here.
Do not display this number together with currency. That field names the currency the draft was entered in (EUR in the example), while this number is in DKK, so the pair reads as EUR 7,450 — about seven times what the draft is actually for. originalAmount is the figure that belongs beside currency.
Reads 0 on a draft that holds no figure in this currency. The minAmount and maxAmount filters on the list endpoint run on this field, so a draft reading 0 here is excluded whenever either filter is applied.
The deprecated v1 create takes a request field of the same name carrying the figure the draft is entered in, so a EUR draft sent as amount: 1000 is returned with amount: 7450 — the request field and this one are in different currencies under one name.
originalAmountHow large this draft is, in the currency it was entered in — the currency currency names, and the one lines[].amount uses. A draft entered as EUR 1,000 is 1000 here, whatever your company reports in.
This is the figure to display beside currency. amount is the same draft in your company base currency: on that EUR draft this field reads 1000 and amount reads 7450. Reads 0 on a draft that carries no amount yet.
currencyThe currency this draft was entered in — EUR for a supplier invoice received in EUR. This is not the currency amount is in; originalAmount is the figure in this currency.
journalTypeJournal type
Every line of the draft — the field to read. Always present, an empty array included, so a client never has to distinguish absent from empty. The name matches the lines the v2 draft endpoints take, so a v2 draft reads back through the field it was written with. The deprecated v1 create still takes splitEntries, so a v1 client sends these lines back under that name.
statusAlways "DRAFT" for draft journal entries
createdAtCreation timestamp
updatedAtLast update timestamp
documentDateDocument date (date on the invoice/voucher). Null when not set.
descriptionDescription
referenceReference number
externalSystemIdYour own system's id for this draft, when set. Not unique. null when unset or cleared.
typeTransaction type (debit or credit)
accountIdPrimary account ID
contraAccountIdContra account ID
customerIdCustomer ID
supplierIdSupplier ID
vatCodeIdVAT/Tax code ID
tagIdsTag/Dimension IDs
documentRefsDocument IDs attached to the draft
exchangeRateWhat one unit of currency is worth in your company base currency. 7.45 on a EUR draft of a company reporting in DKK means 1 EUR buys 7.45 DKK.
This is the rate the draft is converted at, not a factor between the two amounts on this response. amount is built from the draft lines rather than from originalAmount, so multiplying originalAmount by this rate does not reliably reproduce it — on a draft whose sides disagree the two figures differ with no rate involved at all. A draft already entered in your own currency carries 1. Null only while a foreign-currency draft has no rate yet — booking gives it one.
exchangeRateDateDate the exchangeRate was captured for. Null whenever exchangeRate is.
vendorIdDeprecated — use supplierId. Legacy alias, emitted with the same value.
Deprecated — use lines. Present only on drafts that need it: a plain one-debit-one-credit draft with no per-line memo or dimensions reports its accounts through accountId / contraAccountId and omits this field entirely. It also reports a placeholder line's account as "", which the write side rejects. lines is populated either way and round-trips.
Get a draft journal entry (v2)
Returns the draft if it belongs to the API key's app company; 404 otherwise. Identical response shape to GET /api/v1/draft-journal-entries/:id.
Required Scope
journal-entries-read
path Parameters
idDraft journal entry ID (UUID)
Get a draft journal entry (v2) › Responses
Draft retrieved
idDraft journal entry ID
dateDate in YYYY-MM-DD format
amountHow large this draft is, in your company base currency — the one your company reports in. A draft entered as EUR 1,000 by a company reporting in DKK is 7450 here.
Do not display this number together with currency. That field names the currency the draft was entered in (EUR in the example), while this number is in DKK, so the pair reads as EUR 7,450 — about seven times what the draft is actually for. originalAmount is the figure that belongs beside currency.
Reads 0 on a draft that holds no figure in this currency. The minAmount and maxAmount filters on the list endpoint run on this field, so a draft reading 0 here is excluded whenever either filter is applied.
The deprecated v1 create takes a request field of the same name carrying the figure the draft is entered in, so a EUR draft sent as amount: 1000 is returned with amount: 7450 — the request field and this one are in different currencies under one name.
originalAmountHow large this draft is, in the currency it was entered in — the currency currency names, and the one lines[].amount uses. A draft entered as EUR 1,000 is 1000 here, whatever your company reports in.
This is the figure to display beside currency. amount is the same draft in your company base currency: on that EUR draft this field reads 1000 and amount reads 7450. Reads 0 on a draft that carries no amount yet.
currencyThe currency this draft was entered in — EUR for a supplier invoice received in EUR. This is not the currency amount is in; originalAmount is the figure in this currency.
journalTypeJournal type
Every line of the draft — the field to read. Always present, an empty array included, so a client never has to distinguish absent from empty. The name matches the lines the v2 draft endpoints take, so a v2 draft reads back through the field it was written with. The deprecated v1 create still takes splitEntries, so a v1 client sends these lines back under that name.
statusAlways "DRAFT" for draft journal entries
createdAtCreation timestamp
updatedAtLast update timestamp
documentDateDocument date (date on the invoice/voucher). Null when not set.
descriptionDescription
referenceReference number
externalSystemIdYour own system's id for this draft, when set. Not unique. null when unset or cleared.
typeTransaction type (debit or credit)
accountIdPrimary account ID
contraAccountIdContra account ID
customerIdCustomer ID
supplierIdSupplier ID
vatCodeIdVAT/Tax code ID
tagIdsTag/Dimension IDs
documentRefsDocument IDs attached to the draft
exchangeRateWhat one unit of currency is worth in your company base currency. 7.45 on a EUR draft of a company reporting in DKK means 1 EUR buys 7.45 DKK.
This is the rate the draft is converted at, not a factor between the two amounts on this response. amount is built from the draft lines rather than from originalAmount, so multiplying originalAmount by this rate does not reliably reproduce it — on a draft whose sides disagree the two figures differ with no rate involved at all. A draft already entered in your own currency carries 1. Null only while a foreign-currency draft has no rate yet — booking gives it one.
exchangeRateDateDate the exchangeRate was captured for. Null whenever exchangeRate is.
vendorIdDeprecated — use supplierId. Legacy alias, emitted with the same value.
Deprecated — use lines. Present only on drafts that need it: a plain one-debit-one-credit draft with no per-line memo or dimensions reports its accounts through accountId / contraAccountId and omits this field entirely. It also reports a placeholder line's account as "", which the write side rejects. lines is populated either way and round-trips.
Delete a draft journal entry (v2)
Deletes a draft journal entry. Any attached documents are released back to the inbox (status reset to NEW). Returns 404 if the draft id does not belong to the API key's app company.
When a bank payment still references the draft, the delete is refused with 409 DRAFT_LINKED_TO_BANK_PAYMENT
(the linked payment ids are in the error details); re-send with ?detachBankPayments=true to confirm —
the payments survive, only their draft link is cleared.
Required Scope
journal-entries-write
path Parameters
idDraft journal entry ID (UUID)
query Parameters
detachBankPaymentsConfirm detaching any bank payments still linked to this draft.
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Delete a draft journal entry (v2) › Responses
Draft deleted successfully
Update a draft journal entry (v2)
Partial update: every create-time field is accepted except entryType (the journal type pins
constraints decided at creation and is immutable) and attachments (own lifecycle). Omitted fields
keep their current values; splitEntries, when provided, replaces the full line set.
externalSystemId is carried onto the booked journal entry when the draft is booked, and is not
unique: several drafts or entries may share one id. Filter
GET /draft-journal-entries?externalSystemId=... to list carriers.
Reviewed (locked) drafts reject updates with 400 DRAFT_REVIEWED_LOCKED — unmark the review first. Concurrent updates are last-write-wins.
Required Scope
journal-entries-write
path Parameters
idDraft journal entry ID (UUID)
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Update a draft journal entry (v2) › Request Body
datePosting date (YYYY-MM-DD).
originalAmountFace value of the document — what the counterparty is invoiced or pays. NOT the sum of lines. On CUSTOMER_INVOICE / CUSTOMER_PAYMENT / SUPPLIER_INVOICE / SUPPLIER_PAYMENT this value IS the appended AR/AP leg (its absolute value), so an account that appears on both sides of the entry — a clearing or pass-through account — must not be counted into it. Sign carries direction: positive = invoice / payment, negative = credit-note / refund.
customerIdCustomer reference. null clears it; omitting keeps it. Changing it reshapes the entry — requires lines and re-runs the per-entryType validation (CUSTOMER_* types cannot clear their counterparty).
supplierIdSupplier reference. null clears it; omitting keeps it. Changing it reshapes the entry — requires lines and re-runs the per-entryType validation (SUPPLIER_* types cannot clear their counterparty).
descriptionOptional human-readable description.
referenceInvoice number / external reference.
externalSystemIdYour own system's id. null clears the value; omitting the field keeps it.
currency^[A-Za-z]{3}$ISO 4217 alpha-3 currency code.
exchangeRateExchange rate from currency to the company base currency, e.g. 7.4612 for EUR to DKK. Must be greater than zero; a non-positive rate is rejected whatever the entry currency, so the same body is accepted or refused identically for every tenant. Omit the field to have the rate resolved from the rate feed at the posting date — supplying one lets an integrator that already converted the amount itself post both legs of a settlement against the same rate. Not applied when the entry is already denominated in the company base currency, where the conversion is the identity.
Counter-side lines (revenue / expense / bank / outlay). For locked types do NOT include the AR/AP leg. The cap is per ENTRY, not per request or per journal category — nothing accumulates across entries, so a company with many unposted drafts is never refused for that reason.
tagIdsTag IDs applied at the entry level.
journalCategoryIdJournal category ID (UUID). Must belong to the API key's app company; cross-tenant or unknown IDs are rejected with 400 JOURNAL_CATEGORY_NOT_FOUND. When omitted the entry uses the company's default category.
vendorIdDeprecated — use supplierId. Legacy alias for the supplier reference (null clears it); sending both supplierId and vendorId with different values returns 400.
Update a draft journal entry (v2) › Responses
Draft updated
idDraft journal entry ID
dateDate in YYYY-MM-DD format
amountHow large this draft is, in your company base currency — the one your company reports in. A draft entered as EUR 1,000 by a company reporting in DKK is 7450 here.
Do not display this number together with currency. That field names the currency the draft was entered in (EUR in the example), while this number is in DKK, so the pair reads as EUR 7,450 — about seven times what the draft is actually for. originalAmount is the figure that belongs beside currency.
Reads 0 on a draft that holds no figure in this currency. The minAmount and maxAmount filters on the list endpoint run on this field, so a draft reading 0 here is excluded whenever either filter is applied.
The deprecated v1 create takes a request field of the same name carrying the figure the draft is entered in, so a EUR draft sent as amount: 1000 is returned with amount: 7450 — the request field and this one are in different currencies under one name.
originalAmountHow large this draft is, in the currency it was entered in — the currency currency names, and the one lines[].amount uses. A draft entered as EUR 1,000 is 1000 here, whatever your company reports in.
This is the figure to display beside currency. amount is the same draft in your company base currency: on that EUR draft this field reads 1000 and amount reads 7450. Reads 0 on a draft that carries no amount yet.
currencyThe currency this draft was entered in — EUR for a supplier invoice received in EUR. This is not the currency amount is in; originalAmount is the figure in this currency.
journalTypeJournal type
Every line of the draft — the field to read. Always present, an empty array included, so a client never has to distinguish absent from empty. The name matches the lines the v2 draft endpoints take, so a v2 draft reads back through the field it was written with. The deprecated v1 create still takes splitEntries, so a v1 client sends these lines back under that name.
statusAlways "DRAFT" for draft journal entries
createdAtCreation timestamp
updatedAtLast update timestamp
documentDateDocument date (date on the invoice/voucher). Null when not set.
descriptionDescription
referenceReference number
externalSystemIdYour own system's id for this draft, when set. Not unique. null when unset or cleared.
typeTransaction type (debit or credit)
accountIdPrimary account ID
contraAccountIdContra account ID
customerIdCustomer ID
supplierIdSupplier ID
vatCodeIdVAT/Tax code ID
tagIdsTag/Dimension IDs
documentRefsDocument IDs attached to the draft
exchangeRateWhat one unit of currency is worth in your company base currency. 7.45 on a EUR draft of a company reporting in DKK means 1 EUR buys 7.45 DKK.
This is the rate the draft is converted at, not a factor between the two amounts on this response. amount is built from the draft lines rather than from originalAmount, so multiplying originalAmount by this rate does not reliably reproduce it — on a draft whose sides disagree the two figures differ with no rate involved at all. A draft already entered in your own currency carries 1. Null only while a foreign-currency draft has no rate yet — booking gives it one.
exchangeRateDateDate the exchangeRate was captured for. Null whenever exchangeRate is.
vendorIdDeprecated — use supplierId. Legacy alias, emitted with the same value.
Deprecated — use lines. Present only on drafts that need it: a plain one-debit-one-credit draft with no per-line memo or dimensions reports its accounts through accountId / contraAccountId and omits this field entirely. It also reports a placeholder line's account as "", which the write side rejects. lines is populated either way and round-trips.
Book a draft journal entry (v2)
Converts a draft journal entry into a posted journal entry. Validates the posting period and balance, then permanently deletes the draft. V2 drafts already carry the auto-appended AR/AP leg, so booking is a clean handoff to the posted-JE pipeline — no duplicate anchor leg is created. Returns 404 if the draft id does not belong to the API key's app company.
Required Scope
journal-entries-write
path Parameters
idDraft journal entry ID (UUID)
Headers
x-api-idempotency-keyOptional idempotency key for safely retrying mutating requests.
Book a draft journal entry (v2) › Responses
Draft booked successfully — returns the created journal entry
idUnique identifier of the created journal entry
entryNumberEntry number assigned to the journal entry
dateDate of the journal entry
amountAmount of the line accountId names, in that line's account currency (currency). On a two-line entry this is the entry total. On a wider entry it is one line of several — sum lines[].functionalAmount on either side for the total.
currencyCurrency amount is denominated in — the operating currency of that first line's account.
journalTypeJournal type
typeTransaction type
statusStatus of the journal entry
accountIdOne non-VAT line of the entry, paired with contraAccountId as a two-line summary. On a two-line entry the pair describes the whole entry. On a wider one it describes two of its lines and nothing about the rest — read lines for the full picture. Which two are picked is not part of this contract and differs by endpoint (a read takes them in posting order; the draft-booking response takes a debit and a credit), so do not infer a line's position from it.
createdAtTimestamp when the entry was created
documentDateDocument date (date on the invoice/voucher). Null for legacy entries.
originalAmountWhat you posted, in the currency you posted it in — the document figure, before any conversion. Distinct from amount, which is denominated in the ACCOUNT currency (currency): a EUR entry against DKK accounts reports originalAmount: 200 / originalCurrency: "EUR" and amount: 1492.24 / currency: "DKK". Null on historical entries that stored no document amount.
originalCurrencyThe currency the entry was posted in: what originalAmount is denominated in, and what exchangeRate converts FROM. Present independently of originalAmount — an entry can name a currency and store no document amount, which is the historical shape, and the currency still says what its rate converts from. Null only on an entry that names no currency at all.
exchangeRateRate converting originalCurrency to the company base currency — 7.45 on a EUR entry of a DKK-based company means 1 EUR = 7.45 DKK, so originalAmount * exchangeRate is the base-currency total that lines[].functionalAmount sums to. Reported whether you supplied it on create or the platform resolved it, which is what lets you compute the difference against the rate your own settlement later used. 1 on an entry posted in the base currency, since that is the conversion that was applied and stored. Null only on historical entries that stored no rate at all.
exchangeRateDateDate the exchangeRate was captured for. Null whenever exchangeRate is.
descriptionDescription of the journal entry
referenceReference number
externalSystemIdYour own system's id for this entry, when set. Not unique — several entries may share one id.
contraAccountIdThe other half of the accountId pair, when the entry has a second non-VAT line. Same caveat: it is one line, not a summary of every line other than accountId.
Every line of the entry, including VAT lines the platform derived. This is the complete picture: accountId, contraAccountId, amount, currency and vatCodeId above all describe one line, or two, and nothing about the rest. Debits and credits balance across the array on functionalAmount. The order is stable for a given entry — normally the order the lines were posted in — but match lines by account rather than by position, and use vatSourceLineIndex to link a VAT line to its source.
customerIdCustomer ID if applicable
supplierIdSupplier ID if applicable
vatCodeIdVAT/Tax code on the line accountId names, if it has one. Lines can carry different codes — read lines[].vatCodeId for the per-line codes.
tagIdsTag IDs attached to the entry as a whole. Per-line dimensions are on lines[].tagIds.
documentRefsDocument IDs attached to the entry
vendorIdDeprecated — use supplierId. Legacy alias, emitted with the same value.