# Moneybird — Accounting API: gotchas

> Canonical URL: https://developers.apideck.com/apis/accounting/moneybird/gotchas

## GET /accounting/tax-rates

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/bills

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

Moneybird's native purchase invoice `state` is normalized to unified `status` as follows: `open`, `late`,
and `pending_payment` all map to `submitted` (a booked bill awaiting payment — Moneybird has no distinct
unified `overdue` value, so an overdue bill reads as `submitted`, not `draft`); `new` maps to `draft`
(not yet booked); `saved` maps to `posted` (booked, no payment lifecycle); `paid` maps to `paid`. To
distinguish these underlying states (e.g. `late` vs `open`), read the native `state` via `raw: true`.

## GET /accounting/invoices

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

Moneybird's native invoice `state` is normalized to unified `status` as follows: `open`, `late`,
`reminded`, and `pending_payment` all map to `submitted` (a sent invoice awaiting payment — Moneybird
has no distinct unified `overdue`/`unpaid` value, so an overdue invoice reads as `submitted`, not `draft`);
`scheduled` maps to `draft` (not yet sent); `uncollectible` maps to `void`. To distinguish these
underlying states (e.g. `late` vs `open`), read the native `state` via `raw: true`.

## POST /accounting/invoices

Moneybird creates every invoice as a draft. Send `status: "submitted"` (or `"authorised"`)
to finalise it on create: it is marked as sent, without Moneybird emailing the customer, and
is returned with `status: "submitted"`. Any other status leaves a draft.

Payments can only be registered on a finalised invoice; a payment allocated to a draft is
rejected with `Cannot register a payment for this SalesInvoice`.

## GET /accounting/ledger-accounts

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## POST /accounting/ledger-accounts

Moneybird requires an `rgs_code` when creating a ledger account and returns
`Rgs Code is required` otherwise. This applies to administrations outside the Netherlands too.
The code must be an RGS 3.5 code that suits the account `type` (an unknown code is rejected with
`Given RGS code does not belong to a taxonomy item of version 3.5`). Provide it via pass_through:
`"pass_through": [{"service_id": "moneybird", "extend_object": {"rgs_code": "WOmzNopOlv"}}]`

## GET /accounting/invoice-items

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/customers

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/suppliers

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/payments

Moneybird has no way to list payments, so this list operation is not supported. Retrieve a
single payment by `id`, or read the payments on the parent sales invoice.

Moneybird ties a payment to one sales invoice. A payment allocated to several sales invoices is
stored as one Moneybird payment per allocation: every allocation needs an `amount`, the amounts
must add up to `total_amount`, and no sales invoice may appear twice. Such a payment gets a
composite `id` (the Moneybird ids joined with `:`), which works for retrieving and deleting it
as a whole. If any part is refused, the parts already registered are removed and nothing is
kept. A composite `id` is only valid as returned: repeating a part is rejected. Moneybird
reports each part separately, so webhooks deliver one `payment.created` per part, carrying that
part's own id, and a refused split can deliver a created and a deleted event for a part
that was removed again.

The payment is recorded in the sales invoice's own currency for `total_amount`; `currency`,
`currency_rate` and `payment_method` are not applied. `reference` is stored. Allocations must
be of type `invoice` (or untyped), and an allocation `amount` must match
`total_amount`. Moneybird accepts a payment larger than the open balance, leaving a
negative balance on the sales invoice.

Moneybird does not say whether a deleted payment belonged to a sales invoice or a purchase
invoice. Every payment deletion is therefore delivered as both `payment.deleted` and
`bill_payment.deleted`, with the same `entity_id`. Ignore the event for an `id` you do not hold.

## GET /accounting/journal-entries

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/subsidiaries

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/bank-accounts

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/bank-accounts/{id}

Moneybird does not provide a financial account detail endpoint — this operation is unsupported.
Use the bank accounts list to find the account.

## GET /accounting/trial-balance

Built from Moneybird's general ledger report. Moneybird reports whole months, so `filter[end_date]` (today by default) is resolved to the month that contains it and the report is as of the end of that month, returned as `end_date`. Moneybird's fiscal years are always calendar years, so the report covers 1 January of that year up to that month (`start_date`): income and expense accounts are year-to-date and balance sheet accounts carry earlier years' balances. Each account's final balance is netted into one debit or credit, and accounts with a zero balance are left out. Until a year is closed, Moneybird keeps its result on the balance sheet as "Resultaat openstaand boekjaar", which is not a ledger account; the report then has one extra line, "Result of previous fiscal years (not yet appropriated)", with `line_type` `unappropriated_result` and null `account_id` and `code`. That line is taken from Moneybird's balance sheet report, not derived from the other lines, so a general ledger report that does not agree with the balance sheet (debits and credits differ) returns a `502`. `account_id` is the ledger account's id, the id `GET /accounting/ledger-accounts/{id}` takes, and every other line has `line_type` `account`. `code` is the ledger account's number, read with one extra request for the whole chart of accounts (Moneybird does not paginate it). A report as of the last month of a closed year includes Moneybird's closing entry, so income and expense accounts read near zero and the year's result sits in equity; the report has no filter to leave that entry out. `currency` is null. `filter[accounting_method]=cash` returns a `400`. Only the connection's administration is reported.
## GET /accounting/tracking-categories

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/bill-payments

Moneybird has no way to list bill payments, so this list operation is not supported. Retrieve a
single bill payment by `id`, or read the bill payments on the parent purchase invoice.

Moneybird ties a bill payment to one purchase invoice. A bill payment allocated to several purchase invoices is
stored as one Moneybird bill payment per allocation: every allocation needs an `amount`, the amounts
must add up to `total_amount`, and no purchase invoice may appear twice. Such a bill payment gets a
composite `id` (the Moneybird ids joined with `:`), which works for retrieving and deleting it
as a whole. If any part is refused, the parts already registered are removed and nothing is
kept. A composite `id` is only valid as returned: repeating a part is rejected. Moneybird
reports each part separately, so webhooks deliver one `bill_payment.created` per part, carrying that
part's own id, and a refused split can deliver a created and a deleted event for a part
that was removed again.

The bill payment is recorded in the purchase invoice's own currency for `total_amount`; `currency`,
`currency_rate` and `payment_method` are not applied. `reference` is stored. Allocations must
be of type `bill` (or untyped), and an allocation `amount` must match
`total_amount`. Moneybird accepts a bill payment larger than the open balance, leaving a
negative balance on the purchase invoice.

Moneybird does not say whether a deleted payment belonged to a sales invoice or a purchase
invoice. Every payment deletion is therefore delivered as both `bill_payment.deleted` and
`payment.deleted`, with the same `entity_id`. Ignore the event for an `id` you do not hold.

## GET /accounting/expenses

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.

## GET /accounting/bank-feed-accounts

Moneybird cash accounts (financial accounts with `type: "cash"`) are surfaced with
`bank_account_type: "bank"`, since the unified enum has no `cash` value. Cash accounts cannot
meaningfully receive bank feed statements and are not distinguishable from real bank accounts in
this list.

`source_account_id` is always `null` — Moneybird financial accounts have no concept of a separate
"source" account distinct from the ledger account itself.

## POST /accounting/bank-feed-accounts

Moneybird does not support creating financial accounts via its API — this operation returns an
unsupported-operation error. Bank accounts must be set up in the Moneybird UI; once created they
appear in the Bank Feed Accounts list and their IDs can be used as `bank_feed_account_id` when
creating bank feed statements.

## GET /accounting/bank-feed-accounts/{id}

Moneybird does not provide a financial account detail endpoint — this operation is unsupported.
Use the bank feed accounts list to find the account.

## GET /accounting/bank-feed-statements

Moneybird has no bank statement list endpoint, so this operation is backed by financial mutations:
each returned statement contains exactly one transaction, and statement-level balance fields
(`start_balance`, `end_balance`) are not populated.

Results default to the current financial year and are capped at 100 records per page. To read older
mutations, pass an explicit period via pass_through: `?pass_through[filter]=period:20250101..20251231`.

`status` reflects Moneybird's bookkeeping reconciliation state, not feed-delivery status: `pending`
means the transaction has not yet been manually reconciled in the Moneybird UI and can remain
`pending` indefinitely — it does not indicate a delivery problem. Moneybird never returns `rejected`.

## POST /accounting/bank-feed-statements

The `bank_feed_account_id` must be the ID of an existing Moneybird financial account, as returned by the
Bank Feed Accounts (or Bank Accounts) list. Financial accounts cannot be created via the Moneybird API —
they must be set up in the Moneybird UI first.

Transaction amounts are sent to Moneybird as signed values derived from `credit_or_debit`
(`debit` becomes negative).

Moneybird requires a statement reference; since the unified model has no statement-level reference field,
one is generated automatically from the statement period (e.g. `STMT-2026-06-30`) plus the target account
and a timestamp, to reduce (not guarantee) collisions between statements created in the same request burst.
Moneybird does not deduplicate transactions by any client-supplied identifier — retrying a timed-out
request will create duplicate transactions with no way to detect the duplication via the API.

Moneybird only stores `posted_date`, a `message` (from `description`, or `reference` when `description`
is absent), and the signed amount per transaction. `counterparty` and `source_transaction_id` sent on
write are not persisted and will not be returned on reads; if `description` is present, `reference` is
also dropped on write. Reads always return a `reference` value, but it is derived from Moneybird's own
data (batch reference or transaction id), never the value originally submitted.

The response `id` is the id of the first transaction created (Moneybird's mutation id), consistent with
the ids returned by the list/get-one operations — it is not the id of the underlying Moneybird financial
statement, which has no direct read endpoint.

## GET /accounting/bank-feed-statements/{id}

Moneybird has no endpoint to fetch a bank feed statement directly; this operation reads the underlying
financial mutation by id (the same id returned by the list operation and by creating a statement).

## DELETE /accounting/bank-feed-statements/{id}

Moneybird can only delete an entire bank statement batch, not a single transaction. The connector
resolves the passed id (a transaction id, as returned by list/get-one/create) to its parent statement
and deletes that statement — if the statement contains other transactions from the same creation
request, they are deleted too, not just the transaction identified by `id`.

## GET /accounting/categories

Moneybird does not expose a total record count in list responses. `meta.total_count` will not appear.


[API reference](https://developers.apideck.com/apis/accounting/reference.md) · [Coverage matrix](https://developers.apideck.com/apis/accounting/coverage)
