Books: VAT and reports
30 operations in the Tolvi API, version 1. Paths are relative to https://api.tolvi.co.uk. Every amount is integer pence.
Get the profit and loss
GET/api/v1/books/reports/profit-and-loss
- Scopes
books:read
For any period, with a comparison period (by default the same dates a year earlier). Derived from posted entries. Every figure has a drillKey for the drill-down. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
compareTo | query | string (date) | An explicit comparison period (with compareFrom). |
compareFrom | query | string (date) | An explicit comparison period (with compareTo). |
compare | query | string | previous_year (default), previous_period or none. Ignored when explicit comparison dates are given. One of: |
to | query | string (date) | Defaults to today. |
from | query | string (date) | Defaults to the start of the financial year containing |
Responses
200 OK, application/json
Body: object (BooksProfitAndLoss).
Profit and loss for a period, with an optional comparison period (FRS 102 Section 1A layout, grouped by each account’s lead schedule line), and the same figures grouped for owners.
| Field | Type | Description |
|---|---|---|
fromrequired | string (date) | example "2026-03-31" |
torequired | string (date) | example "2026-03-31" |
compareFromrequired | string (date)nullable | example "2026-03-31" |
compareTorequired | string (date)nullable | example "2026-03-31" |
throughSeqrequired | integer | The ledger point the report reflects. |
sectionsrequired | array of BooksReportSection | |
sections[].idrequired | string | Profit and loss: turnover, cost_of_sales, overheads, other_income, interest, tax. Balance sheet: fixed_assets, current_assets, current_liabilities, long_term_liabilities, unsorted, capital. |
sections[].labelrequired | string | |
sections[].linesrequired | array of BooksReportLine | |
sections[].lines[].accountIdrequired | string (uuid)nullable | |
sections[].lines[].coderequired | string | |
sections[].lines[].namerequired | string | |
sections[].lines[].ownerNamerequired | string | |
sections[].lines[].ownerCategoryrequired | string (BooksOwnerCategory) | One of: |
sections[].lines[].currentPencerequired | integer | Integer pence (GBP). |
sections[].lines[].comparePencerequired | integernullable | Integer pence (GBP). Null when no comparison was asked for. |
sections[].lines[].drillKeyrequired | string | Pass to the drill-down to list the entries behind this figure. |
sections[].currentPencerequired | integer | Integer pence (GBP). |
sections[].comparePencerequired | integernullable | Integer pence (GBP). |
sections[].drillKeyrequired | string | |
totalsrequired | array of BooksReportTotal | |
totals[].idrequired | string | Profit and loss: gross_profit, operating_profit, profit_before_tax, net_profit. Balance sheet: net_current_assets, total_assets_less_current_liabilities, net_assets, capital_and_reserves. |
totals[].labelrequired | string | |
totals[].currentPencerequired | integer | Integer pence (GBP). |
totals[].comparePencerequired | integernullable | Integer pence (GBP). |
totals[].drillKeyrequired | string | |
ownerViewrequired | object (BooksProfitAndLossOwnerView) | |
ownerView.moneyInrequired | array of BooksReportOwnerGroup | |
ownerView.moneyIn[].categoryrequired | string (BooksOwnerCategory) | One of: |
ownerView.moneyIn[].labelrequired | string | |
ownerView.moneyIn[].linesrequired | array of BooksReportLine | |
ownerView.moneyIn[].lines[].accountIdrequired | string (uuid)nullable | |
ownerView.moneyIn[].lines[].coderequired | string | |
ownerView.moneyIn[].lines[].namerequired | string | |
ownerView.moneyIn[].lines[].ownerNamerequired | string | |
ownerView.moneyIn[].lines[].ownerCategoryrequired | string (BooksOwnerCategory) | One of: |
ownerView.moneyIn[].lines[].currentPencerequired | integer | Integer pence (GBP). |
ownerView.moneyIn[].lines[].comparePencerequired | integernullable | Integer pence (GBP). Null when no comparison was asked for. |
ownerView.moneyIn[].lines[].drillKeyrequired | string | Pass to the drill-down to list the entries behind this figure. |
ownerView.moneyIn[].currentPencerequired | integer | Integer pence (GBP). |
ownerView.moneyIn[].comparePencerequired | integernullable | Integer pence (GBP). |
ownerView.moneyIn[].drillKeyrequired | string | |
ownerView.costsrequired | array of BooksReportOwnerGroup | |
ownerView.costs[].categoryrequired | string (BooksOwnerCategory) | One of: |
ownerView.costs[].labelrequired | string | |
ownerView.costs[].linesrequired | array of BooksReportLine | |
ownerView.costs[].lines[].accountIdrequired | string (uuid)nullable | |
ownerView.costs[].lines[].coderequired | string | |
ownerView.costs[].lines[].namerequired | string | |
ownerView.costs[].lines[].ownerNamerequired | string | |
ownerView.costs[].lines[].ownerCategoryrequired | string (BooksOwnerCategory) | One of: |
ownerView.costs[].lines[].currentPencerequired | integer | Integer pence (GBP). |
ownerView.costs[].lines[].comparePencerequired | integernullable | Integer pence (GBP). Null when no comparison was asked for. |
ownerView.costs[].lines[].drillKeyrequired | string | Pass to the drill-down to list the entries behind this figure. |
ownerView.costs[].currentPencerequired | integer | Integer pence (GBP). |
ownerView.costs[].comparePencerequired | integernullable | Integer pence (GBP). |
ownerView.costs[].drillKeyrequired | string | |
ownerView.moneyInPencerequired | integer | Integer pence (GBP). |
ownerView.moneyInComparePencerequired | integernullable | Integer pence (GBP). |
ownerView.costsPencerequired | integer | Integer pence (GBP). |
ownerView.costsComparePencerequired | integernullable | Integer pence (GBP). |
netProfitPencerequired | integer | Integer pence (GBP). |
netProfitComparePencerequired | integernullable | Integer pence (GBP). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the balance sheet
GET/api/v1/books/reports/balance-sheet
- Scopes
books:read
What the business owns and owes at a date, with a comparison date. Profit for the year so far is a line in capital and reserves. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
compareAsAt | query | string (date) | An explicit comparison date. |
compare | query | string | previous_year (default: a year earlier), previous_period (the end of the previous financial year) or none. One of: |
asAt | query | string (date) | Defaults to today. |
Responses
200 OK, application/json
Body: object (BooksBalanceSheet).
What the business owns and owes at a date, with an optional comparison date. Profit for the year so far is a line in capital and reserves.
| Field | Type | Description |
|---|---|---|
asAtrequired | string (date) | example "2026-03-31" |
yearStartrequired | string (date) | example "2026-03-31" |
compareAsAtrequired | string (date)nullable | example "2026-03-31" |
compareYearStartrequired | string (date)nullable | example "2026-03-31" |
throughSeqrequired | integer | |
sectionsrequired | array of BooksReportSection | |
sections[].idrequired | string | Profit and loss: turnover, cost_of_sales, overheads, other_income, interest, tax. Balance sheet: fixed_assets, current_assets, current_liabilities, long_term_liabilities, unsorted, capital. |
sections[].labelrequired | string | |
sections[].linesrequired | array of BooksReportLine | |
sections[].lines[].accountIdrequired | string (uuid)nullable | |
sections[].lines[].coderequired | string | |
sections[].lines[].namerequired | string | |
sections[].lines[].ownerNamerequired | string | |
sections[].lines[].ownerCategoryrequired | string (BooksOwnerCategory) | One of: |
sections[].lines[].currentPencerequired | integer | Integer pence (GBP). |
sections[].lines[].comparePencerequired | integernullable | Integer pence (GBP). Null when no comparison was asked for. |
sections[].lines[].drillKeyrequired | string | Pass to the drill-down to list the entries behind this figure. |
sections[].currentPencerequired | integer | Integer pence (GBP). |
sections[].comparePencerequired | integernullable | Integer pence (GBP). |
sections[].drillKeyrequired | string | |
totalsrequired | array of BooksReportTotal | |
totals[].idrequired | string | Profit and loss: gross_profit, operating_profit, profit_before_tax, net_profit. Balance sheet: net_current_assets, total_assets_less_current_liabilities, net_assets, capital_and_reserves. |
totals[].labelrequired | string | |
totals[].currentPencerequired | integer | Integer pence (GBP). |
totals[].comparePencerequired | integernullable | Integer pence (GBP). |
totals[].drillKeyrequired | string | |
balancedrequired | boolean | Net assets equal capital and reserves. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get aged debtors (who owes the business)
GET/api/v1/books/reports/aged-debtors
- Scopes
books:read
By customer and by how overdue. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
asAt | query | string (date) | Defaults to today. |
Responses
200 OK, application/json
Body: object (BooksAgedReport).
Who owes the business (debtors) or whom it owes (creditors), by how overdue. Worked from unpaid invoices and bills where they exist (basis: documents), otherwise from the trade debtors or creditors control account by contact, payments set against the oldest amounts first and amounts assumed due termsDays after their date (basis: ledger). The control account balance is shown beside the total.
| Field | Type | Description |
|---|---|---|
siderequired | string (BooksAgedSide) | One of: |
asAtrequired | string (date) | example "2026-03-31" |
basisrequired | string (BooksAgedBasis) | One of: |
termsDaysrequired | integernullable | |
contactsrequired | array of BooksAgedContact | |
contacts[].contactIdrequired | stringnullable | |
contacts[].namerequired | string | |
contacts[].bucketsrequired | object (BooksAgedBuckets) | Pence by how overdue: not yet due, 1–30, 31–60, 61–90 and over 90 days. |
contacts[].buckets.currentrequired | integer | |
contacts[].buckets.days_1_30required | integer | |
contacts[].buckets.days_31_60required | integer | |
contacts[].buckets.days_61_90required | integer | |
contacts[].buckets.over_90required | integer | |
contacts[].totalPencerequired | integer | Integer pence (GBP). |
contacts[].overduePencerequired | integer | Integer pence (GBP). |
contacts[].itemsrequired | array of BooksAgedItem | |
contacts[].items[].referencerequired | string | |
contacts[].items[].daterequired | string (date) | example "2026-03-31" |
contacts[].items[].dueDaterequired | string (date)nullable | example "2026-03-31" |
contacts[].items[].daysOverduerequired | integer | |
contacts[].items[].bucketrequired | string (BooksAgedBucket) | One of: |
contacts[].items[].outstandingPencerequired | integer | Integer pence (GBP). Still owed; negative for money paid on account. |
contacts[].items[].documentIdrequired | stringnullable | The invoice or bill, when worked from documents. |
contacts[].items[].entryIdrequired | string (uuid)nullable | The ledger entry, when worked from the ledger. |
totalsrequired | object (BooksAgedBuckets) | Pence by how overdue: not yet due, 1–30, 31–60, 61–90 and over 90 days. |
totals.currentrequired | integer | |
totals.days_1_30required | integer | |
totals.days_31_60required | integer | |
totals.days_61_90required | integer | |
totals.over_90required | integer | |
totalPencerequired | integer | Integer pence (GBP). |
overduePencerequired | integer | Integer pence (GBP). |
controlBalancePencerequired | integer | Integer pence (GBP). The control account at |
differencePencerequired | integer | Integer pence (GBP). Control balance less the aged total (nil on the ledger basis). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get aged creditors (whom the business owes)
GET/api/v1/books/reports/aged-creditors
- Scopes
books:read
By supplier and by how overdue. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
asAt | query | string (date) | Defaults to today. |
Responses
200 OK, application/json
Body: object (BooksAgedReport).
Who owes the business (debtors) or whom it owes (creditors), by how overdue. Worked from unpaid invoices and bills where they exist (basis: documents), otherwise from the trade debtors or creditors control account by contact, payments set against the oldest amounts first and amounts assumed due termsDays after their date (basis: ledger). The control account balance is shown beside the total.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the VAT owed per the books
GET/api/v1/books/reports/vat
- Scopes
books:read
The VAT control account over a period, explained. The VAT return itself is worked separately from each transaction’s VAT. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
to | query | string (date) | Defaults to today. |
from | query | string (date) | Defaults to the start of the financial year containing |
Responses
200 OK, application/json
Body: object (BooksVatOwed).
The VAT control account over a period: owed to HMRC at the start, VAT on sales, VAT on purchases, payments to or from HMRC, other adjustments, and owed at the end. Owed to HMRC is positive. This is what the books say; the VAT return is worked from each transaction’s VAT by tax point.
| Field | Type | Description |
|---|---|---|
fromrequired | string (date) | example "2026-03-31" |
torequired | string (date) | example "2026-03-31" |
accountIdrequired | string (uuid) | |
openingPencerequired | integer | Integer pence (GBP). |
salesPencerequired | integer | Integer pence (GBP). |
purchasesPencerequired | integer | Integer pence (GBP). Negative: VAT you can reclaim reduces what is owed. |
paymentsPencerequired | integer | Integer pence (GBP). Negative for a payment to HMRC. |
otherPencerequired | integer | Integer pence (GBP). |
closingPencerequired | integer | Integer pence (GBP). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get cash flow by month
GET/api/v1/books/reports/cash-flow
- Scopes
books:read
Money in and out of the bank, cash and card accounts, month by month, with the balance at each month end. Moving money between the business’s own accounts is neither. Up to five years. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
to | query | string (date) | Defaults to today. |
from | query | string (date) | Defaults to the start of the financial year containing |
Responses
200 OK, application/json
Body: object (BooksCashFlow).
Money in and out of the bank, cash and card accounts, by month. Moving money between the business’s own accounts is neither.
| Field | Type | Description |
|---|---|---|
fromrequired | string (date) | example "2026-03-31" |
torequired | string (date) | example "2026-03-31" |
openingPencerequired | integer | Integer pence (GBP). |
monthsrequired | array of BooksCashFlowMonth | |
months[].monthrequired | string | example "2026-10" |
months[].fromrequired | string (date) | example "2026-03-31" |
months[].torequired | string (date) | example "2026-03-31" |
months[].inPencerequired | integer | Integer pence (GBP). |
months[].outPencerequired | integer | Integer pence (GBP). |
months[].netPencerequired | integer | Integer pence (GBP). |
months[].closingPencerequired | integer | Integer pence (GBP). Across bank, cash and card accounts at the end of the month. |
inPencerequired | integer | Integer pence (GBP). |
outPencerequired | integer | Integer pence (GBP). |
netPencerequired | integer | Integer pence (GBP). |
closingPencerequired | integer | Integer pence (GBP). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get SA103 totals (sole traders)
GET/api/v1/books/reports/sa103
- Scopes
books:read
The income and expense boxes of HMRC’s SA103F (boxes 15 to 31 and 32 to 46, net profit or loss in 47 or 48), added up from each account’s SA103 line on the chart. An aid for the return, never a filing. The balance sheet boxes aren’t included. 422 unless the business is a sole trader. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
taxYear | query | integer | By the year it starts: 2026 is 2026/27. Defaults to the current tax year. 2024 to 2100 |
Responses
200 OK, application/json
Body: object (BooksSa103Totals).
The income and expense boxes of HMRC’s SA103F for a sole trader, added up from the chart. An aid for the return, never a filing.
| Field | Type | Description |
|---|---|---|
fromrequired | string (date) | example "2026-03-31" |
torequired | string (date) | example "2026-03-31" |
taxYearrequired | integer | The tax year, by the year it starts: 2026 is 2026/27. |
taxYearLabelrequired | string | |
sourcerequired | string | The HMRC form the box numbers come from. |
incomerequired | array of BooksSa103IncomeBox | |
income[].boxrequired | integer | |
income[].labelrequired | string | |
income[].amountPencerequired | integer | Integer pence (GBP). |
income[].accountsrequired | array of BooksSa103Account | |
income[].accounts[].coderequired | string | |
income[].accounts[].namerequired | string | |
income[].accounts[].amountPencerequired | integer | Integer pence (GBP). |
income[].accounts[].drillKeyrequired | string | Pass to the drill-down (report profit-and-loss) to list the entries. |
expensesrequired | array of BooksSa103ExpenseBox | |
expenses[].boxrequired | integer | |
expenses[].labelrequired | string | |
expenses[].amountPencerequired | integer | Integer pence (GBP). |
expenses[].accountsrequired | array of BooksSa103Account | |
expenses[].accounts[].coderequired | string | |
expenses[].accounts[].namerequired | string | |
expenses[].accounts[].amountPencerequired | integer | Integer pence (GBP). |
expenses[].accounts[].drillKeyrequired | string | Pass to the drill-down (report profit-and-loss) to list the entries. |
expenses[].disallowableBoxrequired | integer | |
expenses[].disallowablePencerequired | integer | Integer pence (GBP). The part of this box tax doesn’t allow. |
totalExpensesBoxrequired | integer | |
totalExpensesPencerequired | integer | Integer pence (GBP). |
totalDisallowableBoxrequired | integer | |
totalDisallowablePencerequired | integer | Integer pence (GBP). |
netProfitBoxrequired | integer | |
netLossBoxrequired | integer | |
netPencerequired | integer | Integer pence (GBP). Income less expenses; negative is a loss. |
netPoundsrequired | integer | The net figure in whole pounds, rounded down, without a sign. |
notTradingrequired | array of BooksSa103Account | Income left out: it goes on SA100. |
notTrading[].coderequired | string | |
notTrading[].namerequired | string | |
notTrading[].amountPencerequired | integer | Integer pence (GBP). |
notTrading[].drillKeyrequired | string | Pass to the drill-down (report profit-and-loss) to list the entries. |
unmappedrequired | array of BooksSa103Account | Income or cost accounts with no SA103 line. |
unmapped[].coderequired | string | |
unmapped[].namerequired | string | |
unmapped[].amountPencerequired | integer | Integer pence (GBP). |
unmapped[].drillKeyrequired | string | Pass to the drill-down (report profit-and-loss) to list the entries. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List the entries behind a report figure
GET/api/v1/books/reports/drill-down
- Scopes
books:read
The postings that make up one figure, adding up to it. 422 for a figure that isn’t in the report. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
asAt | query | string (date) | Balance sheet: the date (defaults to today); lines run from the year start. |
to | query | string (date) | Profit and loss: the period end (defaults to today). |
from | query | string (date) | Profit and loss: the period start (defaults to the year start). |
keyrequired | query | string | A figure’s drillKey, e.g. account:6100, section:overheads, category:premises, total:net_profit. up to 80 characters |
reportrequired | query | string | One of: |
Responses
200 OK, application/json
Body: object (BooksDrillDown).
The postings behind one report figure. Lines are in the figure’s sense (sense), so they add up to it; a balance sheet figure starts from the balance brought forward at the start of the year (including earlier years’ profit in reserves).
| Field | Type | Description |
|---|---|---|
reportrequired | string (BooksDrillReport) | One of: |
keyrequired | string | |
labelrequired | string | |
fromrequired | string (date) | example "2026-03-31" |
torequired | string (date) | example "2026-03-31" |
senserequired | string (BooksNormalBalance) | One of: |
accountsrequired | array of BooksDrillAccount | |
accounts[].accountIdrequired | string (uuid) | |
accounts[].coderequired | string | |
accounts[].namerequired | string | |
accounts[].ownerNamerequired | string | |
openingPencerequired | integer | Integer pence (GBP). |
linesrequired | array of BooksDrillLine | |
lines[].entryIdrequired | string (uuid) | |
lines[].seqrequired | integer | |
lines[].lineIndexrequired | integer | |
lines[].daterequired | string (date) | example "2026-03-31" |
lines[].kindrequired | string | |
lines[].descriptionrequired | string | |
lines[].accountIdrequired | string (uuid) | |
lines[].accountCoderequired | string | |
lines[].amountPencerequired | integer | Integer pence (GBP). In the figure’s sense, so the lines add up to it. |
lines[].runningPencerequired | integer | Integer pence (GBP). |
closingPencerequired | integer | Integer pence (GBP). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Estimate the tax to set aside
GET/api/v1/books/reports/tax-estimate
- Scopes
books:read
Income tax and Class 4 National Insurance (sole traders) or corporation tax (companies) on the profit so far, annualised. Uses the extra information the owner saved for the tax year (other income, PAYE and pension contributions; a director’s salary and dividends), if any, and says which figures it used. Always an estimate, with its working and assumptions; never a figure for filing. 422 for a partnership or LLP, whose partners are taxed on their shares. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
otherIncomePence | query | integer | Sole traders: other non-savings income for the year (a salary, say), in pence, assumed already taxed. When given, it is used instead of the extra information saved for the tax year (PUT /books/tax-details/{taxYear}). at least 0 |
asAt | query | string (date) | Defaults to today. |
Responses
200 OK, application/json
Body: object (BooksTaxEstimate).
An estimate (never a calculation for filing) of the tax to set aside: income tax and Class 4 National Insurance for a sole trader, corporation tax for a company, on the profit so far annualised over the year. Shows its working and what it assumed.
| Field | Type | Description |
|---|---|---|
estimaterequired | boolean | One of: |
kindrequired | string (BooksTaxEstimateKind) | One of: |
periodFromrequired | string (date) | example "2026-03-31" |
periodTorequired | string (date) | example "2026-03-31" |
asAtrequired | string (date) | example "2026-03-31" |
daysElapsedrequired | integer | |
daysInPeriodrequired | integer | |
profitBeforeTaxPencerequired | integer | Integer pence (GBP). |
addBacksrequired | array of BooksTaxWorkingLine | Costs tax doesn’t allow, added back. |
addBacks[].accountIdrequired | string (uuid)nullable | |
addBacks[].coderequired | string | |
addBacks[].namerequired | string | |
addBacks[].amountPencerequired | integer | Integer pence (GBP). |
notTradingrequired | array of BooksTaxWorkingLine | Income that isn’t trading income, taken out (sole traders). |
notTrading[].accountIdrequired | string (uuid)nullable | |
notTrading[].coderequired | string | |
notTrading[].namerequired | string | |
notTrading[].amountPencerequired | integer | Integer pence (GBP). |
profitToDatePencerequired | integer | Integer pence (GBP). Taxable profit so far, whole pounds (floored). |
annualisedProfitPencerequired | integer | Integer pence (GBP). For the whole year at the same rate, whole pounds (floored). |
taxYearLabelrequired | stringnullable | example "2026/27" |
financialYearsrequired | array of integer | Companies: the financial years whose rates were used. |
otherIncomePencerequired | integer | Integer pence (GBP). |
linesrequired | array of BooksTaxEstimateLine | |
lines[].keyrequired | string (BooksTaxEstimateLineKey) | other_income_tax: a sole trader’s tax on their other income less what PAYE took (only when they entered other income). One of: |
lines[].labelrequired | string | |
lines[].annualPencerequired | integer | Integer pence (GBP). For the whole year, if profit carries on at the same rate. |
lines[].toDatePencerequired | integer | Integer pence (GBP). The share to have set aside by now. |
annualTaxPencerequired | integer | Integer pence (GBP). |
setAsideToDatePencerequired | integer | Integer pence (GBP). |
effectiveRateBpsrequired | integer | Annual tax as a share of annualised profit, basis points. |
assumptionsrequired | array of string | |
extraInfoTaxYearrequired | integer | The tax year the owner’s extra information is for (starting year): the one containing the year end. Use it with /books/tax-details/{taxYear}. |
extraInfoTaxYearLabelrequired | string | example "2026/27" |
basedOnrequired | object (BooksTaxEstimateBasedOn)nullable | The owner’s extra information used, or null when none was entered. |
basedOn.otherIncomerequired | integer | Sole traders: employment, pensions, property profits and savings interest added up. |
basedOn.taxPaidThroughPayerequired | integer | Sole traders: tax already taken under PAYE. |
basedOn.pensionContributionsrequired | integer | Sole traders: personal pension contributions paid in. |
basedOn.directorSalaryrequired | integer | Companies: the director’s planned salary. |
basedOn.dividendsrequired | integer | Companies: the director’s planned dividends. |
ownerDividendTaxrequired | object (BooksOwnerDividendTax)nullable | Companies with planned dividends: the director’s own tax on them. |
ownerDividendTax.taxYearrequired | integer | |
ownerDividendTax.taxYearLabelrequired | string | example "2026/27" |
ownerDividendTax.directorSalaryrequired | integer | |
ownerDividendTax.dividendsrequired | integer | |
ownerDividendTax.dividendTaxrequired | integer | Income tax on the dividends, after the salary uses the personal allowance and bands first. |
ownerDividendTax.assumptionsrequired | array of string |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Download a report as CSV or PDF
GET/api/v1/books/reports/{report}/export
- Scopes
books:read
The same figures as the report (same query parameters), as a CSV (RFC 4180; amounts in pounds with two decimals, minus for negatives) or an A4 PDF (credits in brackets). Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
reportrequired | path | string | One of: |
drillReport | query | string | drill-down only: profit-and-loss or balance-sheet. One of: |
key | query | string | drill-down only: the figure’s drillKey. up to 80 characters |
taxYear | query | integer | sa103 only: the year the tax year starts. |
compareAsAt | query | string (date) | |
compareTo | query | string (date) | |
compareFrom | query | string (date) | |
compare | query | string | previous_year (default), previous_period or none. Ignored when explicit comparison dates are given. One of: |
asAt | query | string (date) | |
to | query | string (date) | |
from | query | string (date) | |
formatrequired | query | string | One of: |
Responses
200 OK, text/csv
The file, as an attachment.
Body: string (binary).
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Get the home screen figures
GET/api/v1/books/home
- Scopes
books:read
How the business is doing (money in and out this month, profit so far this year, cash across accounts, who owes it and whom it owes, VAT owed), the tax to set aside (an estimate) and what needs the owner. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
asAt | query | string (date) | Defaults to today. |
Responses
200 OK, application/json
Body: object (BooksHome).
The owner’s home screen: money in and out this month, profit so far this year, cash across accounts, who owes the business and whom it owes, VAT owed, the tax to set aside (an estimate) and what needs the owner.
| Field | Type | Description |
|---|---|---|
asAtrequired | string (date) | example "2026-03-31" |
businessNamerequired | string | |
yearStartrequired | string (date) | example "2026-03-31" |
yearEndrequired | string (date) | example "2026-03-31" |
thisMonthrequired | object (BooksMoneyInOut) | Money in and out of bank, cash and card accounts; moves between the business’s own accounts are neither. |
thisMonth.fromrequired | string (date) | example "2026-03-31" |
thisMonth.torequired | string (date) | example "2026-03-31" |
thisMonth.inPencerequired | integer | Integer pence (GBP). |
thisMonth.outPencerequired | integer | Integer pence (GBP). |
lastMonthrequired | object (BooksMoneyInOut) | Money in and out of bank, cash and card accounts; moves between the business’s own accounts are neither. |
lastMonth.fromrequired | string (date) | example "2026-03-31" |
lastMonth.torequired | string (date) | example "2026-03-31" |
lastMonth.inPencerequired | integer | Integer pence (GBP). |
lastMonth.outPencerequired | integer | Integer pence (GBP). |
profitrequired | object (BooksHomeProfit) | Profit for the year so far, with the same stretch of last year when the books go back that far. |
profit.fromrequired | string (date) | example "2026-03-31" |
profit.torequired | string (date) | example "2026-03-31" |
profit.profitPencerequired | integer | Integer pence (GBP). |
profit.moneyInPencerequired | integer | Integer pence (GBP). |
profit.costsPencerequired | integer | Integer pence (GBP). |
profit.compareFromrequired | string (date)nullable | example "2026-03-31" |
profit.compareTorequired | string (date)nullable | example "2026-03-31" |
profit.comparePencerequired | integernullable | Integer pence (GBP). |
cashrequired | object (BooksHomeCash) | |
cash.totalPencerequired | integer | Integer pence (GBP). |
cash.accountsrequired | array of BooksCashAccount | |
cash.accounts[].accountIdrequired | string (uuid) | |
cash.accounts[].coderequired | string | |
cash.accounts[].namerequired | string | |
cash.accounts[].ownerNamerequired | string | |
cash.accounts[].kindrequired | string (BooksCashAccountKind) | One of: |
cash.accounts[].balancePencerequired | integer | Integer pence (GBP). Debit positive: money held is positive, a card balance owed negative. |
owedToYourequired | object (BooksHomeOwed) | |
owedToYou.totalPencerequired | integer | Integer pence (GBP). |
owedToYou.overduePencerequired | integer | Integer pence (GBP). |
owedToYou.contactsrequired | integer | Customers (or suppliers) with something outstanding. |
owedToYou.basisrequired | string (BooksAgedBasis) | One of: |
youOwerequired | object (BooksHomeOwed) | |
youOwe.totalPencerequired | integer | Integer pence (GBP). |
youOwe.overduePencerequired | integer | Integer pence (GBP). |
youOwe.contactsrequired | integer | Customers (or suppliers) with something outstanding. |
youOwe.basisrequired | string (BooksAgedBasis) | One of: |
vatrequired | object (BooksHomeVat) | |
vat.registeredrequired | boolean | |
vat.owedPencerequired | integer | Integer pence (GBP). The VAT control account: owed to HMRC is positive. |
taxrequired | object (BooksTaxEstimate)nullable | Null for partnerships and LLPs (each partner is taxed on their share). |
tax.estimaterequired | boolean | One of: |
tax.kindrequired | string (BooksTaxEstimateKind) | One of: |
tax.periodFromrequired | string (date) | example "2026-03-31" |
tax.periodTorequired | string (date) | example "2026-03-31" |
tax.asAtrequired | string (date) | example "2026-03-31" |
tax.daysElapsedrequired | integer | |
tax.daysInPeriodrequired | integer | |
tax.profitBeforeTaxPencerequired | integer | Integer pence (GBP). |
tax.addBacksrequired | array of BooksTaxWorkingLine | Costs tax doesn’t allow, added back. |
tax.addBacks[].accountIdrequired | string (uuid)nullable | |
tax.addBacks[].coderequired | string | |
tax.addBacks[].namerequired | string | |
tax.addBacks[].amountPencerequired | integer | Integer pence (GBP). |
tax.notTradingrequired | array of BooksTaxWorkingLine | Income that isn’t trading income, taken out (sole traders). |
tax.notTrading[].accountIdrequired | string (uuid)nullable | |
tax.notTrading[].coderequired | string | |
tax.notTrading[].namerequired | string | |
tax.notTrading[].amountPencerequired | integer | Integer pence (GBP). |
tax.profitToDatePencerequired | integer | Integer pence (GBP). Taxable profit so far, whole pounds (floored). |
tax.annualisedProfitPencerequired | integer | Integer pence (GBP). For the whole year at the same rate, whole pounds (floored). |
tax.taxYearLabelrequired | stringnullable | example "2026/27" |
tax.financialYearsrequired | array of integer | Companies: the financial years whose rates were used. |
tax.otherIncomePencerequired | integer | Integer pence (GBP). |
tax.linesrequired | array of BooksTaxEstimateLine | |
tax.lines[].keyrequired | string (BooksTaxEstimateLineKey) | other_income_tax: a sole trader’s tax on their other income less what PAYE took (only when they entered other income). One of: |
tax.lines[].labelrequired | string | |
tax.lines[].annualPencerequired | integer | Integer pence (GBP). For the whole year, if profit carries on at the same rate. |
tax.lines[].toDatePencerequired | integer | Integer pence (GBP). The share to have set aside by now. |
tax.annualTaxPencerequired | integer | Integer pence (GBP). |
tax.setAsideToDatePencerequired | integer | Integer pence (GBP). |
tax.effectiveRateBpsrequired | integer | Annual tax as a share of annualised profit, basis points. |
tax.assumptionsrequired | array of string | |
tax.extraInfoTaxYearrequired | integer | The tax year the owner’s extra information is for (starting year): the one containing the year end. Use it with /books/tax-details/{taxYear}. |
tax.extraInfoTaxYearLabelrequired | string | example "2026/27" |
tax.basedOnrequired | object (BooksTaxEstimateBasedOn)nullable | The owner’s extra information used, or null when none was entered. |
tax.basedOn.otherIncomerequired | integer | Sole traders: employment, pensions, property profits and savings interest added up. |
tax.basedOn.taxPaidThroughPayerequired | integer | Sole traders: tax already taken under PAYE. |
tax.basedOn.pensionContributionsrequired | integer | Sole traders: personal pension contributions paid in. |
tax.basedOn.directorSalaryrequired | integer | Companies: the director’s planned salary. |
tax.basedOn.dividendsrequired | integer | Companies: the director’s planned dividends. |
tax.ownerDividendTaxrequired | object (BooksOwnerDividendTax)nullable | Companies with planned dividends: the director’s own tax on them. |
tax.ownerDividendTax.taxYearrequired | integer | |
tax.ownerDividendTax.taxYearLabelrequired | string | example "2026/27" |
tax.ownerDividendTax.directorSalaryrequired | integer | |
tax.ownerDividendTax.dividendsrequired | integer | |
tax.ownerDividendTax.dividendTaxrequired | integer | Income tax on the dividends, after the salary uses the personal allowance and bands first. |
tax.ownerDividendTax.assumptionsrequired | array of string | |
taxUnavailablerequired | stringnullable | Why there is no estimate, in plain words. |
needsYourequired | array of BooksNeedsYou | |
needsYou[].keyrequired | string (BooksNeedsYouKey) | invoices appears only when a repeating invoice has made drafts to check. One of: |
needsYou[].labelrequired | string | |
needsYou[].countrequired | integernullable | Null when Tolvi can’t count it yet. |
needsYou[].detailrequired | stringnullable | |
monthSentencerequired | object (BooksMonthSentence) | "This month in a sentence": worked out from the figures with fixed wording, not written by AI. |
monthSentence.textrequired | string | |
monthSentence.partsrequired | array of BooksMonthSentencePart | |
monthSentence.parts[].textrequired | string | |
monthSentence.parts[].linkedrequired | boolean | A figure to link to the profit and loss for the period. |
monthSentence.fromrequired | string (date) | example "2026-03-31" |
monthSentence.torequired | string (date) | example "2026-03-31" |
monthSentence.hasFiguresrequired | boolean | False when nothing is in the books for the period. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the extra information for the tax estimate
GET/api/v1/books/tax-details/{taxYear}
- Scopes
books:read
What the owner entered for a more accurate tax to set aside, for one tax year; every figure is nil when nothing was entered. Anyone who can read the books: the business’s own people (including view-only) and a linked practice. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
taxYearrequired | path | integer | The tax year’s starting year (2026 = 2026/27). The tax estimate gives the year it uses as 2000 to 2100 |
Responses
200 OK, application/json
Body: object (BooksTaxDetails).
Extra information the owner gave for a more accurate tax to set aside, for one tax year. All figures are optional (nil when not entered) and only ever used for the estimate.
| Field | Type | Description |
|---|---|---|
taxYearrequired | integer | The tax year’s starting year (2026 = 2026/27). |
taxYearLabelrequired | string | example "2026/27" |
employmentIncomePencerequired | integer | Integer pence (GBP). Sole traders. Pay from a job, before tax. |
employmentTaxPaidPencerequired | integer | Integer pence (GBP). Sole traders. Tax taken from that pay under PAYE. |
pensionIncomePencerequired | integer | Integer pence (GBP). Sole traders. State and private pensions received, before tax. |
pensionTaxPaidPencerequired | integer | Integer pence (GBP). Sole traders. Tax taken from pensions under PAYE. |
propertyIncomePencerequired | integer | Integer pence (GBP). Sole traders. Profit from letting property. |
savingsInterestPencerequired | integer | Integer pence (GBP). Sole traders. Interest from savings. |
pensionContributionsPencerequired | integer | Integer pence (GBP). Sole traders. Personal pension contributions paid in. |
directorSalaryPencerequired | integer | Integer pence (GBP). Companies: the director’s own figure. Planned salary. |
dividendsPencerequired | integer | Integer pence (GBP). Companies: the director’s own figure. Planned dividends. |
updatedAtrequired | string (date-time)nullable | When it was last saved; null when nothing has been entered. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Save the extra information for the tax estimate
PUT/api/v1/books/tax-details/{taxYear}
- Scopes
books:write
Replaces the figures for the tax year (anything left out is saved as nil). Only ever used for the estimate on Home; nothing is filed. The business’s people who can change the books, or an accountant whose link lets them (year_end or full_bookkeeping). View-only people and read-only links get 403. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
taxYearrequired | path | integer | The tax year’s starting year (2026 = 2026/27). The tax estimate gives the year it uses as 2000 to 2100 |
Request body
Required. application/json: object (BooksTaxDetailsUpdate).
Replaces the extra information for the tax year. A figure left out is saved as nil.
| Field | Type | Description |
|---|---|---|
employmentIncomePence | integer | Integer pence (GBP). Sole traders. Pay from a job, before tax. at least 0 |
employmentTaxPaidPence | integer | Integer pence (GBP). Sole traders. Tax taken from that pay under PAYE. at least 0 |
pensionIncomePence | integer | Integer pence (GBP). Sole traders. State and private pensions received, before tax. at least 0 |
pensionTaxPaidPence | integer | Integer pence (GBP). Sole traders. Tax taken from pensions under PAYE. at least 0 |
propertyIncomePence | integer | Integer pence (GBP). Sole traders. Profit from letting property. at least 0 |
savingsInterestPence | integer | Integer pence (GBP). Sole traders. Interest from savings. at least 0 |
pensionContributionsPence | integer | Integer pence (GBP). Sole traders. Personal pension contributions paid in (relief at source); Tolvi adds basic rate relief. at least 0 |
directorSalaryPence | integer | Integer pence (GBP). Companies: the director’s own figure. Planned salary for the tax year. at least 0 |
dividendsPence | integer | Integer pence (GBP). Companies: the director’s own figure. Planned dividends for the tax year. at least 0 |
Responses
200 OK, application/json
Body: object (BooksTaxDetails).
Extra information the owner gave for a more accurate tax to set aside, for one tax year. All figures are optional (nil when not entered) and only ever used for the estimate.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Get VAT at a glance
GET/api/v1/books/vat/summary
- Scopes
books:read
The next return and its deadline, what is owed (or due back) so far, the HMRC connection, whether filing from Tolvi is switched on, and anything that needs attention. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksVatSummary).
VAT at a glance: the next deadline, what is owed so far, and anything that needs attention.
| Field | Type | Description |
|---|---|---|
registeredrequired | boolean | |
configuredrequired | boolean | |
schemerequired | string (VatScheme)nullable | One of: |
connectionrequired | object (BooksHmrcConnection) | Tolvi’s authority to file VAT returns for the business at HMRC. |
connection.statusrequired | string (BooksHmrcConnectionStatus) | One of: |
connection.environmentrequired | stringnullable | sandbox, production, custom, or fake (development). |
connection.connectedAtrequired | string (date-time)nullable | |
connection.grantExpiresAtrequired | string (date-time)nullable | HMRC’s authority lasts 18 months, then you connect again. |
connection.reconnectSoonrequired | boolean | |
connection.messagerequired | string | |
submissionEnabledrequired | boolean | Whether returns can be sent to HMRC from this Tolvi (switched on once HMRC approves Tolvi). |
submissionMessagerequired | stringnullable | |
nextrequired | object (BooksVatNextReturn)nullable | |
next.periodKeyrequired | string | |
next.startrequired | string (date) | example "2026-03-31" |
next.endrequired | string (date) | example "2026-03-31" |
next.duerequired | string (date) | example "2026-03-31" |
next.sourcerequired | string (BooksVatObligationSource) | One of: |
next.statusrequired | string (BooksVatPeriodStatus) | One of: |
next.receivedrequired | string (date)nullable | example "2026-03-31" |
next.box5required | integer | What is owed (or due back) so far, in pence. |
next.directionrequired | string (BooksVatDirection) | One of: |
lastFiledrequired | object (BooksVatSubmission)nullable | A return sent to HMRC, with HMRC’s receipt once accepted. The figures never change. |
lastFiled.idrequired | string (uuid) | |
lastFiled.periodKeyrequired | string | example "26AC" |
lastFiled.startrequired | string (date) | example "2026-03-31" |
lastFiled.endrequired | string (date) | example "2026-03-31" |
lastFiled.duerequired | string (date)nullable | example "2026-03-31" |
lastFiled.statusrequired | string (BooksVatSubmissionStatus) | pending (being sent), accepted, rejected, or unknown (no answer from HMRC: Tolvi checks with HMRC before trying again). One of: |
lastFiled.boxesrequired | object (BooksVatBoxes) | The nine boxes in pence. Boxes 6 to 9 are whole pounds (123400 is £1,234). Box 5 is never negative: see |
lastFiled.boxes.box1required | integer | |
lastFiled.boxes.box2required | integer | |
lastFiled.boxes.box3required | integer | |
lastFiled.boxes.box4required | integer | |
lastFiled.boxes.box5required | integer | |
lastFiled.boxes.box6required | integer | |
lastFiled.boxes.box7required | integer | |
lastFiled.boxes.box8required | integer | |
lastFiled.boxes.box9required | integer | |
lastFiled.directionrequired | string (BooksVatDirection) | One of: |
lastFiled.declaredAtrequired | string (date-time) | |
lastFiled.declaredByNamerequired | stringnullable | |
lastFiled.asAgentrequired | boolean | Filed by an accountant for the business. |
lastFiled.processingDaterequired | string (date-time)nullable | |
lastFiled.formBundleNumberrequired | stringnullable | example "123456789012" |
lastFiled.paymentIndicatorrequired | stringnullable | DD: HMRC collects by Direct Debit. BANK: HMRC repays to the bank account it holds. |
lastFiled.chargeRefNumberrequired | stringnullable | |
lastFiled.errorMessagerequired | stringnullable | |
problemsrequired | array of BooksVatProblem | |
problems[].coderequired | string | |
problems[].severityrequired | string (BooksVatFindingSeverity) | One of: |
problems[].messagerequired | string |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the VAT settings
GET/api/v1/books/vat/settings
- Scopes
books:read
The VAT number, return periods (stagger), registration date and the schemes over time, with the flat rate business types. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksVatSettings).
The business’s VAT registration, return periods and schemes.
| Field | Type | Description |
|---|---|---|
registeredrequired | boolean | |
configuredrequired | boolean | False until VAT settings are saved (the return periods are then a guess). |
vrnrequired | stringnullable | |
staggerrequired | string (BooksVatStagger) | One of: |
staggerLabelrequired | string | |
annualEndMonthrequired | integernullable | |
registeredFromrequired | string (date)nullable | example "2026-03-31" |
schemerequired | string (VatScheme)nullable | One of: |
schemesrequired | array of BooksVatSchemePeriod | |
schemes[].fromrequired | string (date) | example "2026-03-31" |
schemes[].torequired | string (date)nullable | example "2026-03-31" |
schemes[].accountingrequired | string (BooksVatAccounting) | One of: |
schemes[].flatRateSectorrequired | stringnullable | |
schemes[].flatRateSectorLabelrequired | stringnullable | |
schemes[].flatRatePercentBpsrequired | integernullable | The flat rate percentage in basis points today (the sector’s, less 1% in the first year). |
schemes[].firstYearDiscountrequired | boolean | |
schemes[].annualAccountingrequired | boolean | |
filedTorequired | string (date)nullable | Returns are filed up to this date: earlier scheme stretches can’t change. example "2026-03-31" |
flatRateSectorsrequired | array of BooksVatFlatRateSectorOption | |
flatRateSectors[].keyrequired | string | |
flatRateSectors[].labelrequired | string | |
flatRateSectors[].percentBpsrequired | integer |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Change the VAT settings
PUT/api/v1/books/vat/settings
- Scopes
books:admin- Access
- A signed-in Tolvi session only, never an API token.
The business’s owners and admins, signed in. The VAT number’s check digits are checked. A change of scheme (cash accounting, flat rate, annual accounting) starts a new stretch from schemeFrom; nothing a filed return covers can change. Part of the books product: the firm must have it and you must have access to it.
Request body
Required. application/json: object (BooksUpdateVatSettings).
The business’s VAT registration and scheme. Changing the scheme starts a new stretch from schemeFrom; stretches covering a filed return never change.
| Field | Type | Description |
|---|---|---|
vrnrequired | string | The 9-digit VAT registration number, with or without GB. Its check digits are checked. example "123456782" |
staggerrequired | string (BooksVatStagger) | One of: |
annualEndMonth | integernullable | Annual accounting: the month the VAT year ends. |
registeredFromrequired | string (date) | The effective date of registration. example "2026-03-31" |
accountingrequired | string (BooksVatAccounting) | accrual (invoice dates) or cash (when money moves, Notice 731). One of: |
flatRate | object (BooksVatFlatRateChoice)nullable | The flat rate scheme; leave it out (or null) when not on it. |
flatRate.sectorrequired | string (BooksVatFlatRateSector) | One of: |
flatRate.firstYearDiscountrequired | boolean | The 1% reduction in the first year of VAT registration (Notice 733 para 4.7). |
annualAccounting | boolean | |
schemeFrom | string (date) | example "2026-03-31" |
Responses
200 OK, application/json
Body: object (BooksVatSettings).
The business’s VAT registration, return periods and schemes.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List the VAT periods
GET/api/v1/books/vat/obligations
- Scopes
books:read
HMRC’s obligations as last fetched; before Tolvi is connected to HMRC, the periods expected from the return periods. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksVatPeriods).
| Field | Type | Description |
|---|---|---|
periodsrequired | array of BooksVatPeriod | |
periods[].periodKeyrequired | string | |
periods[].startrequired | string (date) | example "2026-03-31" |
periods[].endrequired | string (date) | example "2026-03-31" |
periods[].duerequired | string (date) | example "2026-03-31" |
periods[].sourcerequired | string (BooksVatObligationSource) | One of: |
periods[].statusrequired | string (BooksVatPeriodStatus) | One of: |
periods[].receivedrequired | string (date)nullable | example "2026-03-31" |
fetchedAtrequired | string (date-time)nullable | When HMRC was last asked (null: never, the periods are expected ones). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get a VAT return
GET/api/v1/books/vat/returns/{periodKey}
- Scopes
books:read
Worked out from the books now, with Tolvi’s own checks and the latest check before filing; once filed, the figures HMRC accepted and its receipt. returnHash identifies these exact figures. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
periodKeyrequired | path | string | HMRC’s period key (4 characters; example "26AC" |
Responses
200 OK, application/json
Body: object (BooksVatReturn).
A VAT return for one period: worked out from the books until it is filed, then the figures HMRC accepted.
| Field | Type | Description |
|---|---|---|
periodKeyrequired | string | example "26AC" |
startrequired | string (date) | example "2026-03-31" |
endrequired | string (date) | example "2026-03-31" |
duerequired | string (date)nullable | example "2026-03-31" |
sourcerequired | string (BooksVatPeriodSource) | hmrc (an obligation), filed, or expected (from the return periods; not connected to HMRC yet). One of: |
statusrequired | string (BooksVatReturnStatus) | One of: |
boxesrequired | object (BooksVatBoxes) | The nine boxes in pence. Boxes 6 to 9 are whole pounds (123400 is £1,234). Box 5 is never negative: see |
boxes.box1required | integer | |
boxes.box2required | integer | |
boxes.box3required | integer | |
boxes.box4required | integer | |
boxes.box5required | integer | |
boxes.box6required | integer | |
boxes.box7required | integer | |
boxes.box8required | integer | |
boxes.box9required | integer | |
directionrequired | string (BooksVatDirection) | One of: |
boxesInPencerequired | object (BooksVatBoxesInPence) | |
boxesInPence.box6required | integer | |
boxesInPence.box7required | integer | |
boxesInPence.box8required | integer | |
boxesInPence.box9required | integer | |
lineCountrequired | integer | |
lateCountrequired | integer | Payments brought in from a period already filed (cash accounting). |
flatRaterequired | object (BooksVatFlatRateSummary)nullable | |
flatRate.turnoverGrossPencerequired | integer | Integer pence (GBP). |
flatRate.limitedCostrequired | boolean | |
flatRate.explanationrequired | string | |
flatRate.bucketsrequired | array of BooksVatFlatRateBucket | |
flatRate.buckets[].bpsrequired | integer | |
flatRate.buckets[].turnoverGrossPencerequired | integer | Integer pence (GBP). |
flatRate.buckets[].vatPencerequired | integer | Integer pence (GBP). |
flatRate.buckets[].firstYearReductionrequired | boolean | |
findingsrequired | array of BooksVatFinding | Tolvi’s own checks (no AI). |
findings[].kindrequired | string | example "missing_receipt" |
findings[].severityrequired | string (BooksVatFindingSeverity) | One of: |
findings[].titlerequired | string | |
findings[].detailrequired | string | |
findings[].boxrequired | integernullable | |
findings[].lineRefsrequired | array of string | The lines it is about, as |
errorNotificationrequired | object (BooksVatErrorNotification)nullable | Corrections to earlier returns over the Notice 700/45 limit. They are not on this return: the business must tell HMRC separately (form VAT652). |
errorNotification.netErrorPencerequired | integer | What the corrections do to box 5, in pence: positive is more owed to HMRC. |
errorNotification.limitPencerequired | integer | The limit for this return, in pence. |
errorNotification.box6Pencerequired | integer | Integer pence (GBP). |
errorNotification.messagerequired | string | |
errorNotification.sourcerequired | string | The legislation or HMRC notice. |
errorNotification.lineRefsrequired | array of string | |
takenBackCountrequired | integer | Payments undone after an earlier return included them, taken back on this one. |
returnHashrequired | string | Send it back when filing: the figures filed are the ones reviewed. |
checkrequired | object (BooksVatCheck)nullable | The check before filing: Tolvi’s own checks and Tolvi AI’s suggestions. It never changes the return. |
check.idrequired | string (uuid) | |
check.createdAtrequired | string (date-time) | |
check.returnHashrequired | string | |
check.currentrequired | boolean | |
check.summaryrequired | string | |
check.findingsrequired | array of BooksVatFinding | |
check.findings[].kindrequired | string | example "missing_receipt" |
check.findings[].severityrequired | string (BooksVatFindingSeverity) | One of: |
check.findings[].titlerequired | string | |
check.findings[].detailrequired | string | |
check.findings[].boxrequired | integernullable | |
check.findings[].lineRefsrequired | array of string | The lines it is about, as |
check.airequired | boolean | |
submissionrequired | object (BooksVatSubmission)nullable | A return sent to HMRC, with HMRC’s receipt once accepted. The figures never change. |
submission.idrequired | string (uuid) | |
submission.periodKeyrequired | string | example "26AC" |
submission.startrequired | string (date) | example "2026-03-31" |
submission.endrequired | string (date) | example "2026-03-31" |
submission.duerequired | string (date)nullable | example "2026-03-31" |
submission.statusrequired | string (BooksVatSubmissionStatus) | pending (being sent), accepted, rejected, or unknown (no answer from HMRC: Tolvi checks with HMRC before trying again). One of: |
submission.boxesrequired | object (BooksVatBoxes) | The nine boxes in pence. Boxes 6 to 9 are whole pounds (123400 is £1,234). Box 5 is never negative: see |
submission.boxes.box1required | integer | |
submission.boxes.box2required | integer | |
submission.boxes.box3required | integer | |
submission.boxes.box4required | integer | |
submission.boxes.box5required | integer | |
submission.boxes.box6required | integer | |
submission.boxes.box7required | integer | |
submission.boxes.box8required | integer | |
submission.boxes.box9required | integer | |
submission.directionrequired | string (BooksVatDirection) | One of: |
submission.declaredAtrequired | string (date-time) | |
submission.declaredByNamerequired | stringnullable | |
submission.asAgentrequired | boolean | Filed by an accountant for the business. |
submission.processingDaterequired | string (date-time)nullable | |
submission.formBundleNumberrequired | stringnullable | example "123456789012" |
submission.paymentIndicatorrequired | stringnullable | DD: HMRC collects by Direct Debit. BANK: HMRC repays to the bank account it holds. |
submission.chargeRefNumberrequired | stringnullable | |
submission.errorMessagerequired | stringnullable | |
canSubmitrequired | boolean | |
cannotSubmitReasonrequired | stringnullable | |
declarationrequired | object (BooksVatDeclaration) | The legal declaration to show and accept before filing (HMRC’s wording). |
declaration.versionrequired | string | |
declaration.textrequired | string | |
declaration.asAgentrequired | boolean |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
List the lines behind a VAT return
GET/api/v1/books/vat/returns/{periodKey}/lines
- Scopes
books:read
Each line (or payment, under cash accounting) in the return and what it adds to each box, with why. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
periodKeyrequired | path | string | HMRC’s period key (4 characters; example "26AC" |
box | query | integer | Only the lines that add to this box (1 to 9). Box 3 shows boxes 1 and 2; box 5 shows 1, 2 and 4. 1 to 9 |
Responses
200 OK, application/json
Body: object (BooksVatReturnLines).
| Field | Type | Description |
|---|---|---|
boxrequired | integernullable | |
totalPencerequired | integer | The box’s total, in pence. |
titlerequired | string | |
meaningrequired | string | |
linesrequired | array of BooksVatReturnLine | |
lines[].refrequired | string |
example "12:0" |
lines[].lineIdrequired | string (uuid) | |
lines[].entryIdrequired | string (uuid) | |
lines[].entrySeqrequired | integer | |
lines[].daterequired | string (date) | example "2026-03-31" |
lines[].taxPointrequired | string (date) | example "2026-03-31" |
lines[].accountingDaterequired | string (date)nullable | The date that put it in this return (the tax point, or when it was paid under cash accounting). example "2026-03-31" |
lines[].descriptionrequired | string | |
lines[].accountCoderequired | string | |
lines[].accountNamerequired | string | |
lines[].accountOwnerNamerequired | string | |
lines[].vatCodeIdrequired | string | |
lines[].siderequired | string (BooksVatSide) | One of: |
lines[].netPencerequired | integer | Integer pence (GBP). |
lines[].vatPencerequired | integer | Integer pence (GBP). |
lines[].boxesrequired | array of BooksVatBoxAmount | |
lines[].boxes[].boxrequired | integer | |
lines[].boxes[].amountPencerequired | integer | Integer pence (GBP). |
lines[].flatRateTurnoverPencerequired | integer | Integer pence (GBP). |
lines[].explanationrequired | string | |
lines[].laterequired | boolean | |
lines[].settlementIdrequired | string (uuid)nullable | |
lines[].hasReceiptrequired | boolean |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Check a VAT return before filing
POST/api/v1/books/vat/returns/{periodKey}/check
- Scopes
books:write
Tolvi’s own checks and Tolvi AI’s suggestions: unusual movements, purchases without receipts and reverse charge oddities. Suggestions only: nothing in the return or the books changes. Included in the Books price; not available through MCP. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
periodKeyrequired | path | string | HMRC’s period key (4 characters; example "26AC" |
Responses
200 OK, application/json
Body: object (BooksVatCheck).
The check before filing: Tolvi’s own checks and Tolvi AI’s suggestions. It never changes the return.
| Field | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
createdAtrequired | string (date-time) | |
returnHashrequired | string | |
currentrequired | boolean | |
summaryrequired | string | |
findingsrequired | array of BooksVatFinding | |
findings[].kindrequired | string | example "missing_receipt" |
findings[].severityrequired | string (BooksVatFindingSeverity) | One of: |
findings[].titlerequired | string | |
findings[].detailrequired | string | |
findings[].boxrequired | integernullable | |
findings[].lineRefsrequired | array of string | The lines it is about, as |
airequired | boolean |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Record telling HMRC about corrections over the limit
POST/api/v1/books/vat/returns/{periodKey}/error-notification
- Scopes
books:write- Access
- A signed-in Tolvi session only, never an API token.
Corrections to earlier returns over the VAT Notice 700/45 limit can’t go on a return: the business tells HMRC separately (form VAT652), then records it here so they stay off every return. The business’s owners and admins, or an accountant through a full bookkeeping link (who accepts the agent’s declaration). Signed in only: never an API token, a connected app or MCP. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
periodKeyrequired | path | string | HMRC’s period key (4 characters; example "26AC" |
Request body
Required. application/json: object (BooksVatRecordErrorNotification).
The business has told HMRC about corrections over the limit: they stay off every return from now on.
| Field | Type | Description |
|---|---|---|
notifiedOnrequired | string (date) | example "2026-03-31" |
reference | string |
Responses
200 OK, application/json
Body: object (BooksVatReturn).
A VAT return for one period: worked out from the books until it is filed, then the figures HMRC accepted.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
File a VAT return with HMRC
POST/api/v1/books/vat/returns/{periodKey}/submit
- Scopes
books:write- Access
- A signed-in Tolvi session only, never an API token.
- Retries
- Send an
Idempotency-Keyto make retries safe.
The business’s owners and admins, or an accountant through a full bookkeeping link (who accepts the agent’s declaration). Signed in only: never an API token, a connected app or MCP. The person accepts HMRC’s declaration; returnHash must match the return they reviewed, or 409. A pending record is written before HMRC is called, so a lost answer is checked with HMRC rather than sent twice. When HMRC accepts, the period’s VAT lines lock. Answers 503 while filing from Tolvi is switched off (until HMRC approves Tolvi). Open even while Books is suspended. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
periodKeyrequired | path | string | HMRC’s period key (4 characters; example "26AC" |
Idempotency-Key | header | string | A unique key (1-255 visible characters) to make retries safe. Replayed for 24 hours. 1 to 255 characters |
Request body
Required. application/json: object (BooksSubmitVatReturn).
File the return. The person accepts HMRC’s declaration; returnHash must be the preview’s, so what was reviewed is what is filed.
| Field | Type | Description |
|---|---|---|
returnHashrequired | string | |
declaration | object | |
declaration.acceptedrequired | boolean | One of: |
declaration.versionrequired | string | |
checkId | string (uuid) | |
clientFactsrequired | object (BooksVatClientFacts) | What the browser knows about itself, for HMRC’s fraud prevention headers (Gov-Client-*). Send it with every request that reaches HMRC. |
clientFacts.deviceIdrequired | string (uuid) | A UUID kept on this device (local storage). |
clientFacts.userAgentrequired | string | navigator.userAgent |
clientFacts.screensrequired | array of object | |
clientFacts.screens[].widthrequired | integer | |
clientFacts.screens[].heightrequired | integer | |
clientFacts.screens[].scalingFactorrequired | number | |
clientFacts.screens[].colourDepthrequired | integer | |
clientFacts.windowWidthrequired | integer | |
clientFacts.windowHeightrequired | integer | |
clientFacts.timezonerequired | string | example "UTC+01:00" |
clientFacts.appVersion | string |
Responses
200 OK, application/json
Body: object (BooksVatReturn).
A VAT return for one period: worked out from the books until it is filed, then the figures HMRC accepted.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
List VAT returns sent to HMRC
GET/api/v1/books/vat/submissions
- Scopes
books:read
Newest first, with HMRC’s receipts. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksVatSubmissions).
| Field | Type | Description |
|---|---|---|
submissionsrequired | array of BooksVatSubmission | |
submissions[].idrequired | string (uuid) | |
submissions[].periodKeyrequired | string | example "26AC" |
submissions[].startrequired | string (date) | example "2026-03-31" |
submissions[].endrequired | string (date) | example "2026-03-31" |
submissions[].duerequired | string (date)nullable | example "2026-03-31" |
submissions[].statusrequired | string (BooksVatSubmissionStatus) | pending (being sent), accepted, rejected, or unknown (no answer from HMRC: Tolvi checks with HMRC before trying again). One of: |
submissions[].boxesrequired | object (BooksVatBoxes) | The nine boxes in pence. Boxes 6 to 9 are whole pounds (123400 is £1,234). Box 5 is never negative: see |
submissions[].boxes.box1required | integer | |
submissions[].boxes.box2required | integer | |
submissions[].boxes.box3required | integer | |
submissions[].boxes.box4required | integer | |
submissions[].boxes.box5required | integer | |
submissions[].boxes.box6required | integer | |
submissions[].boxes.box7required | integer | |
submissions[].boxes.box8required | integer | |
submissions[].boxes.box9required | integer | |
submissions[].directionrequired | string (BooksVatDirection) | One of: |
submissions[].declaredAtrequired | string (date-time) | |
submissions[].declaredByNamerequired | stringnullable | |
submissions[].asAgentrequired | boolean | Filed by an accountant for the business. |
submissions[].processingDaterequired | string (date-time)nullable | |
submissions[].formBundleNumberrequired | stringnullable | example "123456789012" |
submissions[].paymentIndicatorrequired | stringnullable | DD: HMRC collects by Direct Debit. BANK: HMRC repays to the bank account it holds. |
submissions[].chargeRefNumberrequired | stringnullable | |
submissions[].errorMessagerequired | stringnullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get VAT owed and paid at HMRC
GET/api/v1/books/vat/payments
- Scopes
books:read
HMRC’s liabilities and payments for the last year, as last fetched. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksVatPaymentsAndLiabilities).
What HMRC says is owed and has been paid, as last fetched.
| Field | Type | Description |
|---|---|---|
liabilitiesrequired | array of BooksVatLiability | |
liabilities[].periodStartrequired | string (date)nullable | example "2026-03-31" |
liabilities[].periodEndrequired | string (date)nullable | example "2026-03-31" |
liabilities[].typerequired | string | example "VAT Return Debit Charge" |
liabilities[].originalAmountPencerequired | integer | Integer pence (GBP). |
liabilities[].outstandingAmountPencerequired | integernullable | Integer pence (GBP). |
liabilities[].duerequired | string (date)nullable | example "2026-03-31" |
paymentsrequired | array of BooksVatPayment | |
payments[].amountPencerequired | integer | Integer pence (GBP). |
payments[].receivedrequired | string (date)nullable | example "2026-03-31" |
fetchedAtrequired | string (date-time)nullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the HMRC connection
GET/api/v1/books/vat/hmrc
- Scopes
books:read
Whether Tolvi may file for the business at HMRC, and when the authority ends (HMRC’s lasts 18 months). Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksHmrcConnection).
Tolvi’s authority to file VAT returns for the business at HMRC.
| Field | Type | Description |
|---|---|---|
statusrequired | string (BooksHmrcConnectionStatus) | One of: |
environmentrequired | stringnullable | sandbox, production, custom, or fake (development). |
connectedAtrequired | string (date-time)nullable | |
grantExpiresAtrequired | string (date-time)nullable | HMRC’s authority lasts 18 months, then you connect again. |
reconnectSoonrequired | boolean | |
messagerequired | string |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Disconnect from HMRC
DELETE/api/v1/books/vat/hmrc
- Scopes
books:admin- Access
- A signed-in Tolvi session only, never an API token.
The business’s owners and admins, signed in. Tolvi forgets the tokens; filed returns stay. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksHmrcConnection).
Tolvi’s authority to file VAT returns for the business at HMRC.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Finish connecting to HMRC
POST/api/v1/books/vat/hmrc/callback
- Scopes
books:admin- Access
- A signed-in Tolvi session only, never an API token.
Called by the Books app’s HMRC return page with the code and state, as the person who started. The state is single use and lasts 10 minutes. Tokens are encrypted and never returned. Part of the books product: the firm must have it and you must have access to it.
Request body
Required. application/json: object (BooksHmrcCallback).
| Field | Type | Description |
|---|---|---|
coderequired | string | |
staterequired | string |
Responses
200 OK, application/json
Body: object (BooksHmrcConnection).
Tolvi’s authority to file VAT returns for the business at HMRC.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the VAT periods from HMRC
POST/api/v1/books/vat/hmrc/obligations/refresh
- Scopes
books:write
Asks HMRC for the business’s obligations (about eight months back and four ahead) and keeps them. Send the browser’s facts for the fraud prevention headers. Part of the books product: the firm must have it and you must have access to it.
Request body
Required. application/json: object (BooksVatWithClientFacts).
| Field | Type | Description |
|---|---|---|
clientFactsrequired | object (BooksVatClientFacts) | What the browser knows about itself, for HMRC’s fraud prevention headers (Gov-Client-*). Send it with every request that reaches HMRC. |
clientFacts.deviceIdrequired | string (uuid) | A UUID kept on this device (local storage). |
clientFacts.userAgentrequired | string | navigator.userAgent |
clientFacts.screensrequired | array of object | |
clientFacts.screens[].widthrequired | integer | |
clientFacts.screens[].heightrequired | integer | |
clientFacts.screens[].scalingFactorrequired | number | |
clientFacts.screens[].colourDepthrequired | integer | |
clientFacts.windowWidthrequired | integer | |
clientFacts.windowHeightrequired | integer | |
clientFacts.timezonerequired | string | example "UTC+01:00" |
clientFacts.appVersion | string |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get VAT owed and paid from HMRC
POST/api/v1/books/vat/hmrc/payments/refresh
- Scopes
books:write
Asks HMRC for the business’s liabilities and payments over the last year and keeps them. Part of the books product: the firm must have it and you must have access to it.
Request body
Required. application/json: object (BooksVatWithClientFacts).
The fields are as shown above.
Responses
200 OK, application/json
Body: object (BooksVatPaymentsAndLiabilities).
What HMRC says is owed and has been paid, as last fetched.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).