# FreeAgent — Accounting API: gotchas

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

## GET /accounting/bills

`filter[status]` supports `paid` and `unpaid`. `unpaid` returns every bill with an amount
still due, including partly paid bills (`status: partially_paid`); check `balance` to tell
them apart. `partially_paid` cannot be filtered and is rejected with a 400.

The list includes supplier credit notes, which FreeAgent stores as bills with a negative
total. `filter[updated_since]` is supported.

## GET /accounting/invoices

FreeAgent's `Overdue` status is returned as `submitted` (or `partially_paid` when part of
the invoice has been paid); there is no separate overdue status. Treat an invoice as overdue
when `balance > 0` and `due_date` is in the past. For the same reason an invoice turning
overdue does not by itself produce an `accounting.invoice.updated` webhook.

## POST /accounting/invoices

FreeAgent needs payment terms in days. Send `terms` containing the number of days (for
example `Net 30`), or a `due_date`, from which the days after `invoice_date` are used. An
invoice with neither is rejected.

FreeAgent creates every invoice as a draft. Send `status: submitted` (or `authorised`) to
have it marked as sent straight after creation; the customer is not emailed. If that step
fails the invoice is still created, as a draft, and the response carries a warning. Other
statuses cannot be set on create.

## GET /accounting/invoice-items

We use **price list items** from FreeAgent to support invoice items. 
To access stock items, please use the [Proxy API](https://developers.apideck.com/apis/proxy/reference).

**Related resources:**
- [Price List Items](https://dev.freeagent.com/docs/price_list_items)
- [Stock Items](https://dev.freeagent.com/docs/stock_items) 
- [Stock Items via Proxy API](https://developers.apideck.com/apis/proxy/reference)

## POST /accounting/invoice-items

We use **price list items** from FreeAgent to support invoice items. 
To access stock items, please use the [Proxy API](https://developers.apideck.com/apis/proxy/reference).

**Related resources:**
- [Price List Items](https://dev.freeagent.com/docs/price_list_items)
- [Stock Items](https://dev.freeagent.com/docs/stock_items) 
- [Stock Items via Proxy API](https://developers.apideck.com/apis/proxy/reference)

## GET /accounting/invoice-items/{id}

We use **price list items** from FreeAgent to support invoice items. 
To access stock items, please use the [Proxy API](https://developers.apideck.com/apis/proxy/reference).

**Related resources:**
- [Price List Items](https://dev.freeagent.com/docs/price_list_items)
- [Stock Items](https://dev.freeagent.com/docs/stock_items) 
- [Stock Items via Proxy API](https://developers.apideck.com/apis/proxy/reference)

## GET /accounting/credit-notes

FreeAgent's `Overdue` status is returned as `authorised` (or `partially_paid` when part of
the credit note has been refunded); there is no separate overdue status. `balance` is the
amount FreeAgent still has to refund, carrying FreeAgent's sign (negative, like
`total_amount`), and is `0` once the credit note is `paid`.

## GET /accounting/bill-credit-notes

FreeAgent has no dedicated bill credit note object — a bill credit note is a FreeAgent bill with a negative total. This resource does not support listing: FreeAgent has no server-side filter for credit notes, and isolating them client-side from the bills list would break that list's pagination. Fetch a credit note by id (a bill id whose total is not negative returns 404), and discover credit notes through the bills resource, where they appear as bills with a negative total.

FreeAgent stores credit amounts as negative numbers; on read every amount is normalized to positive — the header totals `total_amount`, `sub_total`, `total_tax` and `balance`, and each `line_items[].total_amount` (`total_tax` is derived as `total_amount` − `sub_total`, since FreeAgent returns no tax field on a bill header). Header `sub_total` is net; a line's `total_amount` is gross (tax-inclusive). A line's tax rate is the FreeAgent sales-tax percentage, exposed as `line_items[].tax_rate.code`; FreeAgent has no tax-rate objects, so `tax_rate.id` is not available.

`status` reflects payment state: `posted` while a balance is outstanding, `paid` once fully settled. There is no draft state. `remaining_credit`, `allocations`, `tax_code`, `subsidiary`, `tracking_categories`, `custom_fields` and `row_version` are not populated.

## POST /accounting/bill-credit-notes

A bill credit note is created as a FreeAgent bill with negative line amounts, producing a record with a negative total. Amounts are sent positive and stored negative.

FreeAgent requires `reference` and a ledger account on every line (`line_items[].ledger_account.id`); a create without them is rejected. `due_date` is not part of the bill credit note; FreeAgent's required due date is set to `date_issued`. A line's tax rate is supplied as a percentage via `line_items[].tax_rate.code` (FreeAgent has no tax-rate objects, so `tax_rate.id` is ignored).

On update, existing lines can be edited or removed (send the surviving lines with their `id`; a dropped line is deleted), but a new line cannot be added — FreeAgent requires every line on an update to reference an existing one.

`status` cannot be set — it is derived from payment state (`posted` until fully settled, then `paid`). There is no draft state.

## GET /accounting/customers

In FreeAgent, newly created customers will not appear in customer list responses until an invoice has been assigned to them. This is because FreeAgent only classifies contacts as customers once they have associated invoices. See [Set up a contact as a customer or a supplier](https://support.freeagent.com/hc/en-gb/articles/115001217850-Set-up-a-contact-as-a-customer-or-a-supplier) for more details.

`balance` reflects the contact's net position across both sales invoices and purchase bills. FreeAgent does not keep a separate ledger balance for a contact's customer role versus its supplier role, so if the same contact is also used as a supplier, an outstanding bill against them lowers `balance` (or makes it negative) rather than being excluded. Draft invoices and bills are not included until posted.

## POST /accounting/customers

You can create customer contacts, but they will not be visible in customer list responses until an invoice is assigned to them. FreeAgent only classifies contacts as customers after they have associated transactions. See [Set up a contact as a customer or a supplier](https://support.freeagent.com/hc/en-gb/articles/115001217850-Set-up-a-contact-as-a-customer-or-a-supplier) for more information.

## GET /accounting/customers/{id}

`balance` reflects the contact's net position across both sales invoices and purchase
bills. FreeAgent does not keep a separate ledger balance for a contact's customer role
versus its supplier role, so if the same contact is also used as a supplier, an
outstanding bill against them lowers `balance` (or makes it negative) rather than being
excluded. Draft invoices and bills are not included until posted.

## GET /accounting/payments

FreeAgent stores a payment as an explanation on a bank account, and it can only list those
per bank account. Each list request therefore reads the explanations of every bank account
before returning a page, so it gets slower as the ledger grows. Use `filter[updated_since]`
for incremental syncs, and `filter[invoice_id]` to read the payments of one invoice.
`meta.total_count` is not returned.

`customer` is not populated. Read the invoice in `allocations[0].id` to get its customer.

A refund paid out against a credit note is returned with `type: accounts_receivable_credit`
and an allocation of type `credit_memo`; a customer payment with `type: accounts_receivable`
and an allocation of type `invoice`. `total_amount` is always positive.

## POST /accounting/payments

A FreeAgent payment settles exactly one invoice or credit note: send one `allocations` entry
with `type: invoice` (or `credit_memo` for a credit note refund) and its `id`. To settle
several documents, create one payment per document. `account.id` is required and must be a
FreeAgent bank account id.

Send `total_amount` as a positive number. A `credit_memo` allocation or
`type: accounts_receivable_credit` records money paid out to the customer.

`total_amount` is recorded in the bank account's currency. Paying an invoice in another
currency is not supported here; record it in FreeAgent. `reference` is set by FreeAgent and
cannot be supplied.

## GET /accounting/payments/{id}

Returns 404 when the id belongs to a FreeAgent bank transaction explanation that does not
settle an invoice or credit note (a bill payment, transfer or expense).

## PATCH /accounting/payments/{id}

Returns 404 when the id belongs to a FreeAgent bank transaction explanation that does not
settle an invoice or credit note (a bill payment, transfer or expense).

`allocations` is optional; when sent it must be one entry with `type: invoice` or
`credit_memo`.

## DELETE /accounting/payments/{id}

Returns 404 when the id belongs to a FreeAgent bank transaction explanation that does not
settle an invoice or credit note (a bill payment, transfer or expense).

## GET /accounting/profit-and-loss

FreeAgent's Profit and Loss API has a date range limitation. The specified period must be **12 months or less, or contained within a single accounting year**. Periods that exceed 12 months and span multiple accounting years will return an error.

## POST /accounting/journal-entries

Line items must balance to zero — FreeAgent rejects the whole entry otherwise, rather than
accepting it and leaving the books unbalanced. Only `ledger_account`, `total_amount`,
`type` and `description` are supported per line; a category that requires an associated
user, stock item, capital asset, property, or contact (used for opening balances and a
handful of specialised ledger accounts) cannot be targeted through this connector.

## PATCH /accounting/journal-entries/{id}

Line items must balance to zero. Only `ledger_account`, `total_amount`, `type` and
`description` are supported per line; a category that requires an associated user, stock
item, capital asset, property, or contact cannot be targeted through this connector. A
line included in the update is modified in place; lines from the existing entry that are
left out of the request are not removed.

## GET /accounting/general-ledger-transactions

FreeAgent records ledger lines without a transaction id. Lines are returned grouped into one
transaction per source document and date, with the `id` of the transaction's first line;
each transaction balances to zero. Zero-value documents, which post no amounts, are left out.

The list covers the company's current accounting year (FreeAgent does not return ledger
lines across accounting years in one request). `filter[source_type]`, `filter[source_id]` and
`filter[updated_since]` are not supported; `sort` supports `posted_at` only.

`source_type` is `invoice` for both invoices and credit notes (they share FreeAgent's invoice
ids), and `other` for bank transaction explanations such as payments and receipts, with the
explanation id in `source_id`. `currency` is not returned; amounts are in the company's
currency.

## GET /accounting/general-ledger-transactions/{id}

The id of any line of a transaction returns the whole transaction, with the id of its first
line.

## GET /accounting/attachments/{reference_type}/{reference_id}

Supported `reference_type` values are `bill`, `bill-credit-note` and `expense`. A FreeAgent
bill, bill credit note or expense holds at most one attachment, so the list has zero or one
entry. A bill credit note is only found as `bill-credit-note`, and a bill only as `bill`.

`file_url` is FreeAgent's own signed link and stops working 30 seconds after the list or get
call; use the download endpoint, which returns the file itself, instead of storing it.
`created_at` and `updated_at` are not returned.

## POST /accounting/attachments/{reference_type}/{reference_id}

Uploading replaces the attachment the bill, bill credit note or expense already has. FreeAgent
accepts PDF, PNG, JPEG and GIF files up to 5MB; other content types are refused with a 415
and larger files with a 413. A paid bill still accepts a new attachment.

## GET /accounting/attachments/{reference_type}/{reference_id}/{id}

Only the record's current attachment is found: an earlier attachment id returns 404 after a
newer file was uploaded, and so does an id that belongs to another record.

## DELETE /accounting/attachments/{reference_type}/{reference_id}/{id}

Only the record's current attachment is found: an earlier attachment id returns 404 after a
newer file was uploaded, and so does an id that belongs to another record.

## GET /accounting/attachments/{reference_type}/{reference_id}/{id}/download

Returns the file itself, with its content type and file name. An id that is not the record's
current attachment, or a bill requested as a `bill-credit-note` (or the reverse), returns a
400 validation error rather than a 404.

## GET /accounting/fiscal-periods

Years are FreeAgent's own accounting years for the company, listed newest first, with the year's start date as `id`; every record has `type: year`. `status` comes from the account locks (the year-end or filed-return locks and the user lock): `closed` when the year ends on or before the latest lock date, otherwise `open`; a lock date inside a year leaves it `open`. FreeAgent locks stop every user, accountants included, so `locked` is never returned. `locked_through` is the latest lock date capped at the year's end, when it is on or after the year's start, otherwise null. Account locks need FreeAgent's Full Access level: below it, `status` and `locked_through` are null.

## GET /accounting/fiscal-periods/{id}

FreeAgent has no endpoint for a single accounting year, so this reads the company's years and returns the matching one.

## GET /accounting/trial-balance

Built from FreeAgent's trial balance summary, requested up to `filter[end_date]` (today in UTC when left out, always sent to FreeAgent); FreeAgent reports from the start of the accounting year that contains that date, so income and expense accounts are year-to-date. Each line's signed total is netted into one debit or credit, reading a positive total as a debit; this assumption was checked against recorded examples only, not a live company, and a report with every sign flipped would still balance. Lines with a zero balance are left out. `account_id` is the FreeAgent category id, the id ledger accounts use. Bank account (750) and user (900–910) lines share their category's id, so several lines can have the same `account_id`; tell them apart by `name` and `code`, the code shown in FreeAgent's reports. `end_date` is the requested date, because FreeAgent's response carries no date; `start_date` and `currency` are null. `filter[accounting_method]=cash` returns a `400`: FreeAgent's cash basis applies to VAT only. A report whose debits and credits do not balance returns a `502`. The connection needs at least the "Tax, Accounting & Users" access level; a lower level, or API access turned off for the company, returns FreeAgent's `403`.
## GET /accounting/bill-payments

FreeAgent stores a bill payment as an explanation on a bank account, and it can only list
those per bank account. Each list request therefore reads the explanations of every bank
account before returning a page, so it gets slower as the ledger grows. Use
`filter[updated_since]` for incremental syncs, and `filter[bill_id]` to read the payments of
one bill. `meta.total_count` is not returned.

`supplier` is not populated. Read the bill in `allocations[0].id` to get its supplier.

A refund received against a bill credit note is returned with `type: accounts_payable_credit`;
payments made are returned with `type: accounts_payable`. `total_amount` is always positive.

## POST /accounting/bill-payments

A FreeAgent bill payment pays exactly one bill: send one `allocations` entry with
`type: bill` and the bill `id`. To pay several bills, create one bill payment per bill.
`account.id` is required and must be a FreeAgent bank account id.

Send `total_amount` as a positive number. Set `type: accounts_payable_credit` to record a
refund received from the supplier instead of a payment made.

`total_amount` is recorded in the bank account's currency. Paying a bill in another currency
is not supported here; record it in FreeAgent.

`reference` is set by FreeAgent (for example "Bill refund against BCN-001 (Supplier Ltd)")
and cannot be supplied.

## GET /accounting/bill-payments/{id}

Returns 404 when the id belongs to a FreeAgent bank transaction explanation that does not
pay a bill (an invoice receipt, transfer or expense).

## PATCH /accounting/bill-payments/{id}

Returns 404 when the id belongs to a FreeAgent bank transaction explanation that does not
pay a bill (an invoice receipt, transfer or expense).

`allocations` is optional; when sent it must be one entry with `type: bill`. Without `type`,
the payment keeps its direction (payment made or refund received).

## DELETE /accounting/bill-payments/{id}

Returns 404 when the id belongs to a FreeAgent bank transaction explanation that does not
pay a bill (an invoice receipt, transfer or expense).

## GET /accounting/expenses

A FreeAgent expense is a single claim, so each expense has exactly one entry in `line_items`.
`total_amount` is always positive; `type` is `expense` for money spent and `refund` for money
paid back to the claimant. `line_items[0].account.id` is the expense category, using the same ids
as Ledger Accounts.

`account` and `supplier` are not returned: FreeAgent records who claimed the expense, not the
account it was paid from or the supplier. `status` is always `posted`.

## POST /accounting/expenses

Send exactly one `line_items` entry with `total_amount` and `account.id` (a ledger account id);
more than one line is rejected. `transaction_date` and a description (`memo` or the line's
`description`) are required by FreeAgent.

The expense is claimed by the FreeAgent user who authorized the connection. To claim it for
another user, send their FreeAgent user URL as `user` through `pass_through`.

Send `total_amount` as a positive number and set `type: refund` for a refund; anything else is
recorded as money spent.

## PATCH /accounting/expenses/{id}

On update, `line_items` may hold at most one entry. Without `type`, the expense keeps its
direction (spent or refund).

## GET /accounting/bank-feed-statements

FreeAgent keeps no statement record: statements are rebuilt from the transactions FreeAgent
imported into each bank account, one per import, and identified by
`<bank_feed_account_id>:<first posted date>:<last posted date>`. A statement holds every
imported transaction of its account between those dates, so imports covering the same
dates are returned as one statement. Use `filter[bank_feed_account_id]` to read one account.
Each request reads all imported transactions of the accounts involved before returning a
page.

`status` is always `success` (only imported transactions exist), and balances and
`merchant_category_code` are not returned. `transaction_type` is returned when the import
recorded one (`credit`, `debit`, `deposit`, `transfer`, `payment`, otherwise `other`).

## POST /accounting/bank-feed-statements

FreeAgent imports the statement lines into the bank account `bank_feed_account_id`. The
import runs in the background: a successful response means FreeAgent accepted the
statement, and the lines appear on the bank account within seconds. The returned `id` is
`<bank_feed_account_id>:<first posted date>:<last posted date>`, so sending the same
statement again returns the same id, and it can be used to read or delete the statement.

A line whose `source_transaction_id` was imported before is skipped, so a statement can
safely be sent again after a timeout. Send `amount` as a positive number with
`credit_or_debit`; `description` falls back to `counterparty`, then `reference`.
Balances, `merchant_category_code` and `status` are not sent to FreeAgent.

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

Returns every transaction FreeAgent imported into the bank account between the two dates in
the id (`<bank_feed_account_id>:<first posted date>:<last posted date>`), including lines
from other imports covering the same dates. An id in another format, or a range without
imported transactions, returns 404.

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

Not supported: a FreeAgent statement is the set of transactions an import created and cannot
be changed. Delete it (while none of its transactions has been explained) and create it
again.

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

Deletes every transaction FreeAgent imported into the bank account between the two dates in
the id. If any of them has already been explained (matched to an invoice, bill, transfer or
expense) the request is refused with 409 and nothing is deleted; remove those explanations
first. Each transaction is deleted with its own FreeAgent request, so a large statement uses
a correspondingly large share of FreeAgent's rate limit; if a delete stops part-way, the
error says how many were removed and repeating the request deletes the rest.

## GET /accounting/quotes

FreeAgent estimates are returned as quotes. FreeAgent stores a sent estimate as Open, which is
returned as `sent`; an estimate converted to an invoice is returned as `converted`.

`sub_total` and `total` are after `discount_percentage`; `discount_amount` is the discount.
`expiry_date` and `terms` are not available in FreeAgent.

## POST /accounting/quotes

`number` becomes the FreeAgent estimate reference; without it FreeAgent numbers the quote
itself. `status` can be `draft` (the default), `sent`, `accepted` or `rejected`; other
statuses are rejected. A line without a FreeAgent unit in `unit_of_measure` (for example
`Hours`, `Services` or `Products`) takes one from its `type`, or `-no unit-`. Every line needs
a `description`.

## PATCH /accounting/quotes/{id}

`status` can be set to `draft`, `sent`, `accepted` or `rejected`. `converted` is not settable:
convert the estimate to an invoice in FreeAgent.

When `line_items` is sent, it replaces the quote's lines: lines with an `id` are updated, lines
without one are added, and existing lines left out are deleted. Leave `line_items` out to keep
the lines as they are.

## DELETE /accounting/quotes/{id}

FreeAgent only deletes draft estimates, so a sent, accepted or rejected quote is set back to
draft and then deleted.

## GET /accounting/projects

`status` is `active`, `completed`, `cancelled`, or `other` for FreeAgent's Hidden projects;
`filter[status]` accepts those four values. `filter[customer_id]` with an id that is not a
FreeAgent contact returns 404 rather than an empty list. `filter[name]` and
`filter[updated_since]` are not supported.

FreeAgent keeps one budget per project: `budget_amount` is returned for a monetary budget and
`budget_hours` for a budget in hours (or days, converted with the project's hours per day).

## POST /accounting/projects

`customer.id` is required: every FreeAgent project belongs to a contact. When not supplied,
`status` defaults to `active` and `currency` to the company's currency. Send either
`budget_amount` or `budget_hours`, not both; `hourly_rate` is billed per hour.
`on_hold`, `draft`, `in_progress` and `approved` are not FreeAgent statuses and are
rejected.

## PATCH /accounting/projects/{id}

`on_hold`, `draft`, `in_progress` and `approved` are not FreeAgent statuses and are
rejected. Sending `budget_amount` or `budget_hours` switches the project's budget to that
unit.


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