Books: purchases and receipts
28 operations in the Tolvi API, version 1. Paths are relative to https://api.tolvi.co.uk. Every amount is integer pence.
List receipts
GET/api/v1/books/receipts
- Scopes
books:read
Newest first. Filter by the person’s decision: to_review, confirmed, discarded or all. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer | 1 to 200; default 100 |
review | query | string | One of: |
Responses
200 OK, application/json
Body: object (ReceiptList).
| Field | Type | Description |
|---|---|---|
itemsrequired | array of Receipt | |
items[].idrequired | string (uuid) | |
items[].sourcerequired | string (ReceiptSource) | One of: |
items[].fileNamerequired | string | |
items[].contentTyperequired | string | |
items[].sizeBytesrequired | integer | |
items[].statusrequired | string (ReceiptFileStatus) | pending_upload, pending_scan (being checked for viruses), available, or rejected (with the reason). One of: |
items[].rejectedReasonrequired | stringnullable | |
items[].readingStatusrequired | string (ReceiptReadingStatus) | waiting (until the virus check passes), queued, reading, done or failed. One of: |
items[].readingErrorrequired | stringnullable | Why reading failed, in plain English. |
items[].readingrequired | object (ReceiptReading)nullable | What Tolvi AI read from the receipt: a suggestion for the person to confirm or correct. Money in integer pence. |
items[].reading.documentTyperequired | string (ReceiptDocumentType) | One of: |
items[].reading.supplierNamerequired | stringnullable | |
items[].reading.supplierVatNumberrequired | stringnullable | As printed on the receipt. |
items[].reading.documentNumberrequired | stringnullable | |
items[].reading.daterequired | string (date)nullable | example "2026-03-31" |
items[].reading.currencyrequired | stringnullable | example "GBP" |
items[].reading.totalPencerequired | integernullable | Integer pence (GBP). |
items[].reading.netPencerequired | integernullable | Integer pence (GBP). |
items[].reading.vatPencerequired | integernullable | Integer pence (GBP). |
items[].reading.vatBreakdownrequired | array of ReceiptVatRow | |
items[].reading.vatBreakdown[].rateBpsrequired | integer | VAT rate in basis points (2000 = 20%). |
items[].reading.vatBreakdown[].netPencerequired | integernullable | Integer pence (GBP). |
items[].reading.vatBreakdown[].vatPencerequired | integer | Integer pence (GBP). |
items[].reading.lineItemsrequired | array of ReceiptLineItem | |
items[].reading.lineItems[].descriptionrequired | string | |
items[].reading.lineItems[].quantityrequired | stringnullable | |
items[].reading.lineItems[].amountPencerequired | integer | Integer pence (GBP). |
items[].reading.lineItems[].vatRateBpsrequired | integernullable | |
items[].reading.lineItems[].vatPence | integernullable | Integer pence (GBP). The VAT shown on this line. Absent on a reading from before lines were captured. |
items[].reading.lineItems[].accountCode | stringnullable | The category Tolvi AI suggests, from the chart of accounts. |
items[].reading.lineItems[].confidence | integer | How sure Tolvi AI is of this line, from 0 to 100. 0 to 100 |
items[].reading.lineItems[].reason | stringnullable | Why it is unsure, when it is; otherwise empty. |
items[].reading.moreLines | boolean | The document has more lines than Tolvi AI kept. |
items[].reading.paymentMethodrequired | string (ReceiptPaymentMethod) | One of: |
items[].reading.cardLast4required | stringnullable | The last four digits of the card only. |
items[].reading.fieldsrequired | object (ReceiptFieldNotes) | |
items[].reading.fields.supplierNamerequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
items[].reading.fields.supplierName.confidencerequired | string (ReceiptConfidence) | One of: |
items[].reading.fields.supplierName.reasonrequired | string | |
items[].reading.fields.supplierName.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
items[].reading.fields.daterequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
items[].reading.fields.date.confidencerequired | string (ReceiptConfidence) | One of: |
items[].reading.fields.date.reasonrequired | string | |
items[].reading.fields.date.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
items[].reading.fields.totalrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
items[].reading.fields.total.confidencerequired | string (ReceiptConfidence) | One of: |
items[].reading.fields.total.reasonrequired | string | |
items[].reading.fields.total.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
items[].reading.fields.vatrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
items[].reading.fields.vat.confidencerequired | string (ReceiptConfidence) | One of: |
items[].reading.fields.vat.reasonrequired | string | |
items[].reading.fields.vat.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
items[].reading.fields.vatNumberrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
items[].reading.fields.vatNumber.confidencerequired | string (ReceiptConfidence) | One of: |
items[].reading.fields.vatNumber.reasonrequired | string | |
items[].reading.fields.vatNumber.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
items[].reading.notesrequired | string | |
items[].reading.modelrequired | string | The model that read it. |
items[].reading.promptVersionrequired | string | example "extract_receipt@1" |
items[].reading.readAtrequired | string (date-time) | |
items[].suggestedLinesrequired | array of ReceiptSuggestedLine | |
items[].suggestedLines[].descriptionrequired | string | |
items[].suggestedLines[].accountCoderequired | stringnullable | The suggested category, from the chart of accounts. |
items[].suggestedLines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. |
items[].suggestedLines[].vatPencerequired | integer | Integer pence (GBP). The VAT in that cost. |
items[].suggestedLines[].vatCoderequired | string (VatCode) | One of: |
items[].suggestedLines[].checkThisrequired | boolean | Tolvi AI was barely sure of this line: highlight it for the person to check. It never stops them. |
items[].suggestedLines[].checkReasonrequired | stringnullable | |
items[].checksrequired | object (ReceiptChecks)nullable | |
items[].checks.ofrequired | string (ReceiptChecksOf) | Whether these checks are on what Tolvi AI read or on what the person confirmed. One of: |
items[].checks.problemsrequired | array of ReceiptProblem | |
items[].checks.problems[].coderequired | string | example "no_vat_number" |
items[].checks.problems[].severityrequired | string (ReceiptProblemSeverity) | One of: |
items[].checks.problems[].fieldrequired | stringnullable | example "vatNumber" |
items[].checks.problems[].messagerequired | string | |
items[].checks.problems[].sourcerequired | stringnullable | The rule behind it. |
items[].checks.vatClaimablerequired | boolean | Whether the VAT on it can be reclaimed: the business is VAT registered and the receipt shows VAT and a valid UK VAT number. |
items[].checks.suggestedVatCoderequired | string (VatCode)nullable | One of: |
items[].checks.vatNumberrequired | stringnullable | The VAT number without GB, when it passes the check digits. |
items[].duplicateOfIdrequired | string (uuid)nullable | An earlier receipt this looks like a copy of. |
items[].matchesrequired | array of ReceiptMatch | |
items[].matches[].bankLineIdrequired | string (uuid) | |
items[].matches[].bankAccountIdrequired | string (uuid) | |
items[].matches[].bookedAtrequired | string (date) | example "2026-03-31" |
items[].matches[].amountPencerequired | integer | Integer pence (GBP). As on the bank line: money out is negative. |
items[].matches[].descriptionrequired | string | |
items[].matches[].scorerequired | integer | 0 to 100 |
items[].matches[].bandrequired | string (ReceiptMatchBand) | One of: |
items[].matches[].summaryrequired | string | |
items[].matches[].reasonsrequired | array of string | |
items[].reviewStatusrequired | string (ReceiptReviewStatus) | One of: |
items[].confirmedrequired | object (ReceiptConfirmed)nullable | What the person confirmed. Money in integer pence. |
items[].confirmed.supplierNamerequired | string | |
items[].confirmed.supplierVatNumberrequired | stringnullable | |
items[].confirmed.daterequired | string (date) | example "2026-03-31" |
items[].confirmed.currencyrequired | string | example "GBP" |
items[].confirmed.totalPencerequired | integer | Integer pence (GBP). |
items[].confirmed.netPencerequired | integernullable | Integer pence (GBP). |
items[].confirmed.vatPencerequired | integernullable | Integer pence (GBP). |
items[].confirmed.vatCoderequired | string (VatCode)nullable | One of: |
items[].confirmed.accountCoderequired | stringnullable | |
items[].confirmed.paymentMethodrequired | string (ReceiptPaymentMethod)nullable | One of: |
items[].confirmed.cardLast4required | stringnullable | |
items[].confirmed.paidPersonallyrequired | boolean | |
items[].confirmed.bankLineIdrequired | string (uuid)nullable | |
items[].confirmed.noterequired | stringnullable | |
items[].confirmed.correctedFieldsrequired | array of string | Fields the person changed from what Tolvi AI read; line1.category is the first line’s category. |
items[].confirmed.linesrequired | array of ReceiptConfirmedLine | The lines it went into the books as. Empty when it was confirmed as one amount. |
items[].confirmed.lines[].descriptionrequired | string | |
items[].confirmed.lines[].accountCoderequired | stringnullable | |
items[].confirmed.lines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. |
items[].confirmed.lines[].vatPencerequired | integer | Integer pence (GBP). |
items[].confirmed.confirmedByrequired | string (uuid)nullable | |
items[].confirmed.confirmedAtrequired | string (date-time) | |
items[].postingStatusrequired | string (ReceiptPostingStatus) | none, proposed (confirmed; waiting for the books), or posted. One of: |
items[].postingProposalrequired | objectnullable | What confirming proposes for the books (version 1, or version 2 with lines). |
items[].ledgerEntryIdrequired | string (uuid)nullable | |
items[].emailFromrequired | stringnullable | For an emailed receipt: who sent it. |
items[].emailSubjectrequired | stringnullable | |
items[].previewablerequired | boolean | Whether a browser can show the image (a HEIC photo has a JPEG preview once read). |
items[].uploadedByrequired | string (uuid)nullable | |
items[].capturedAtrequired | string (date-time)nullable | |
items[].createdAtrequired | string (date-time) | |
items[].updatedAtrequired | string (date-time) | |
toReviewrequired | integer | Receipts waiting for the person to check. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Add a receipt
POST/api/v1/books/receipts
- Scopes
books:write- Retries
- Send an
Idempotency-Keyto make retries safe.
Records a photo or file of a receipt (JPEG, PNG, WebP, HEIC or PDF, up to 20 MB) and returns a short-lived presigned URL to PUT it to storage with exactly the headers given; then call upload-complete. Send the app’s own clientReference for a capture so sending it again (an offline retry) returns the same receipt. Needs a person, not an API client. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
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 (CreateReceipt).
Register a photo or file of a receipt before uploading it.
| Field | Type | Description |
|---|---|---|
fileNamerequired | string | up to 200 characters; example "IMG_2041.HEIC" |
contentType | string | The type the browser reports; the file name’s extension decides. example "image/heic" |
sizeBytesrequired | integer | 1 to 20971520 |
source | string (ReceiptCaptureSource) | One of: |
clientReference | string (uuid) | The app’s own id for this capture: sending it again returns the same receipt (offline retries). |
capturedAt | string (date-time) |
Responses
201 Created, application/json
Body: object (ReceiptUpload).
| Field | Type | Description |
|---|---|---|
receiptrequired | object (Receipt) | A receipt or bill: the file (kept as the record), what Tolvi AI read from it, and what the person confirmed. |
receipt.idrequired | string (uuid) | |
receipt.sourcerequired | string (ReceiptSource) | One of: |
receipt.fileNamerequired | string | |
receipt.contentTyperequired | string | |
receipt.sizeBytesrequired | integer | |
receipt.statusrequired | string (ReceiptFileStatus) | pending_upload, pending_scan (being checked for viruses), available, or rejected (with the reason). One of: |
receipt.rejectedReasonrequired | stringnullable | |
receipt.readingStatusrequired | string (ReceiptReadingStatus) | waiting (until the virus check passes), queued, reading, done or failed. One of: |
receipt.readingErrorrequired | stringnullable | Why reading failed, in plain English. |
receipt.readingrequired | object (ReceiptReading)nullable | What Tolvi AI read from the receipt: a suggestion for the person to confirm or correct. Money in integer pence. |
receipt.reading.documentTyperequired | string (ReceiptDocumentType) | One of: |
receipt.reading.supplierNamerequired | stringnullable | |
receipt.reading.supplierVatNumberrequired | stringnullable | As printed on the receipt. |
receipt.reading.documentNumberrequired | stringnullable | |
receipt.reading.daterequired | string (date)nullable | example "2026-03-31" |
receipt.reading.currencyrequired | stringnullable | example "GBP" |
receipt.reading.totalPencerequired | integernullable | Integer pence (GBP). |
receipt.reading.netPencerequired | integernullable | Integer pence (GBP). |
receipt.reading.vatPencerequired | integernullable | Integer pence (GBP). |
receipt.reading.vatBreakdownrequired | array of ReceiptVatRow | |
receipt.reading.vatBreakdown[].rateBpsrequired | integer | VAT rate in basis points (2000 = 20%). |
receipt.reading.vatBreakdown[].netPencerequired | integernullable | Integer pence (GBP). |
receipt.reading.vatBreakdown[].vatPencerequired | integer | Integer pence (GBP). |
receipt.reading.lineItemsrequired | array of ReceiptLineItem | |
receipt.reading.lineItems[].descriptionrequired | string | |
receipt.reading.lineItems[].quantityrequired | stringnullable | |
receipt.reading.lineItems[].amountPencerequired | integer | Integer pence (GBP). |
receipt.reading.lineItems[].vatRateBpsrequired | integernullable | |
receipt.reading.lineItems[].vatPence | integernullable | Integer pence (GBP). The VAT shown on this line. Absent on a reading from before lines were captured. |
receipt.reading.lineItems[].accountCode | stringnullable | The category Tolvi AI suggests, from the chart of accounts. |
receipt.reading.lineItems[].confidence | integer | How sure Tolvi AI is of this line, from 0 to 100. 0 to 100 |
receipt.reading.lineItems[].reason | stringnullable | Why it is unsure, when it is; otherwise empty. |
receipt.reading.moreLines | boolean | The document has more lines than Tolvi AI kept. |
receipt.reading.paymentMethodrequired | string (ReceiptPaymentMethod) | One of: |
receipt.reading.cardLast4required | stringnullable | The last four digits of the card only. |
receipt.reading.fieldsrequired | object (ReceiptFieldNotes) | |
receipt.reading.fields.supplierNamerequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
receipt.reading.fields.supplierName.confidencerequired | string (ReceiptConfidence) | One of: |
receipt.reading.fields.supplierName.reasonrequired | string | |
receipt.reading.fields.supplierName.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
receipt.reading.fields.daterequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
receipt.reading.fields.date.confidencerequired | string (ReceiptConfidence) | One of: |
receipt.reading.fields.date.reasonrequired | string | |
receipt.reading.fields.date.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
receipt.reading.fields.totalrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
receipt.reading.fields.total.confidencerequired | string (ReceiptConfidence) | One of: |
receipt.reading.fields.total.reasonrequired | string | |
receipt.reading.fields.total.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
receipt.reading.fields.vatrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
receipt.reading.fields.vat.confidencerequired | string (ReceiptConfidence) | One of: |
receipt.reading.fields.vat.reasonrequired | string | |
receipt.reading.fields.vat.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
receipt.reading.fields.vatNumberrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
receipt.reading.fields.vatNumber.confidencerequired | string (ReceiptConfidence) | One of: |
receipt.reading.fields.vatNumber.reasonrequired | string | |
receipt.reading.fields.vatNumber.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
receipt.reading.notesrequired | string | |
receipt.reading.modelrequired | string | The model that read it. |
receipt.reading.promptVersionrequired | string | example "extract_receipt@1" |
receipt.reading.readAtrequired | string (date-time) | |
receipt.suggestedLinesrequired | array of ReceiptSuggestedLine | |
receipt.suggestedLines[].descriptionrequired | string | |
receipt.suggestedLines[].accountCoderequired | stringnullable | The suggested category, from the chart of accounts. |
receipt.suggestedLines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. |
receipt.suggestedLines[].vatPencerequired | integer | Integer pence (GBP). The VAT in that cost. |
receipt.suggestedLines[].vatCoderequired | string (VatCode) | One of: |
receipt.suggestedLines[].checkThisrequired | boolean | Tolvi AI was barely sure of this line: highlight it for the person to check. It never stops them. |
receipt.suggestedLines[].checkReasonrequired | stringnullable | |
receipt.checksrequired | object (ReceiptChecks)nullable | |
receipt.checks.ofrequired | string (ReceiptChecksOf) | Whether these checks are on what Tolvi AI read or on what the person confirmed. One of: |
receipt.checks.problemsrequired | array of ReceiptProblem | |
receipt.checks.problems[].coderequired | string | example "no_vat_number" |
receipt.checks.problems[].severityrequired | string (ReceiptProblemSeverity) | One of: |
receipt.checks.problems[].fieldrequired | stringnullable | example "vatNumber" |
receipt.checks.problems[].messagerequired | string | |
receipt.checks.problems[].sourcerequired | stringnullable | The rule behind it. |
receipt.checks.vatClaimablerequired | boolean | Whether the VAT on it can be reclaimed: the business is VAT registered and the receipt shows VAT and a valid UK VAT number. |
receipt.checks.suggestedVatCoderequired | string (VatCode)nullable | One of: |
receipt.checks.vatNumberrequired | stringnullable | The VAT number without GB, when it passes the check digits. |
receipt.duplicateOfIdrequired | string (uuid)nullable | An earlier receipt this looks like a copy of. |
receipt.matchesrequired | array of ReceiptMatch | |
receipt.matches[].bankLineIdrequired | string (uuid) | |
receipt.matches[].bankAccountIdrequired | string (uuid) | |
receipt.matches[].bookedAtrequired | string (date) | example "2026-03-31" |
receipt.matches[].amountPencerequired | integer | Integer pence (GBP). As on the bank line: money out is negative. |
receipt.matches[].descriptionrequired | string | |
receipt.matches[].scorerequired | integer | 0 to 100 |
receipt.matches[].bandrequired | string (ReceiptMatchBand) | One of: |
receipt.matches[].summaryrequired | string | |
receipt.matches[].reasonsrequired | array of string | |
receipt.reviewStatusrequired | string (ReceiptReviewStatus) | One of: |
receipt.confirmedrequired | object (ReceiptConfirmed)nullable | What the person confirmed. Money in integer pence. |
receipt.confirmed.supplierNamerequired | string | |
receipt.confirmed.supplierVatNumberrequired | stringnullable | |
receipt.confirmed.daterequired | string (date) | example "2026-03-31" |
receipt.confirmed.currencyrequired | string | example "GBP" |
receipt.confirmed.totalPencerequired | integer | Integer pence (GBP). |
receipt.confirmed.netPencerequired | integernullable | Integer pence (GBP). |
receipt.confirmed.vatPencerequired | integernullable | Integer pence (GBP). |
receipt.confirmed.vatCoderequired | string (VatCode)nullable | One of: |
receipt.confirmed.accountCoderequired | stringnullable | |
receipt.confirmed.paymentMethodrequired | string (ReceiptPaymentMethod)nullable | One of: |
receipt.confirmed.cardLast4required | stringnullable | |
receipt.confirmed.paidPersonallyrequired | boolean | |
receipt.confirmed.bankLineIdrequired | string (uuid)nullable | |
receipt.confirmed.noterequired | stringnullable | |
receipt.confirmed.correctedFieldsrequired | array of string | Fields the person changed from what Tolvi AI read; line1.category is the first line’s category. |
receipt.confirmed.linesrequired | array of ReceiptConfirmedLine | The lines it went into the books as. Empty when it was confirmed as one amount. |
receipt.confirmed.lines[].descriptionrequired | string | |
receipt.confirmed.lines[].accountCoderequired | stringnullable | |
receipt.confirmed.lines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. |
receipt.confirmed.lines[].vatPencerequired | integer | Integer pence (GBP). |
receipt.confirmed.confirmedByrequired | string (uuid)nullable | |
receipt.confirmed.confirmedAtrequired | string (date-time) | |
receipt.postingStatusrequired | string (ReceiptPostingStatus) | none, proposed (confirmed; waiting for the books), or posted. One of: |
receipt.postingProposalrequired | objectnullable | What confirming proposes for the books (version 1, or version 2 with lines). |
receipt.ledgerEntryIdrequired | string (uuid)nullable | |
receipt.emailFromrequired | stringnullable | For an emailed receipt: who sent it. |
receipt.emailSubjectrequired | stringnullable | |
receipt.previewablerequired | boolean | Whether a browser can show the image (a HEIC photo has a JPEG preview once read). |
receipt.uploadedByrequired | string (uuid)nullable | |
receipt.capturedAtrequired | string (date-time)nullable | |
receipt.createdAtrequired | string (date-time) | |
receipt.updatedAtrequired | string (date-time) | |
uploadrequired | object (PresignedUpload)nullable | PUT the file here with exactly these headers, then call upload-complete. Null when the file has already been uploaded. |
upload.urlrequired | string (uri) | |
upload.methodrequired | string (PresignedMethod) | One of: |
upload.headersrequired | object | Headers to send with the request. |
upload.expiresAtrequired | string (date-time) |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
Get the address for emailing receipts in
GET/api/v1/books/receipts/email-address
- Scopes
books:read
Null until one is created. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (ReceiptEmailAddress).
Where to email receipts in. Anything sent here is read as a receipt for this business.
| Field | Type | Description |
|---|---|---|
addressrequired | string (email)nullable | example "k3v9x2m8q4p7w1z6@receipts.tolvi.co.uk" |
createdAtrequired | string (date-time)nullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Create or replace the address for emailing receipts in
POST/api/v1/books/receipts/email-address
- Scopes
books:admin- Access
- A signed-in Tolvi session only, never an API token.
Anything emailed to it is read as a receipt for this business. Replacing it stops the old address working at once. The business’s owners and admins, signed in. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (ReceiptEmailAddress).
Where to email receipts in. Anything sent here is read as a receipt for this business.
| Field | Type | Description |
|---|---|---|
addressrequired | string (email)nullable | example "k3v9x2m8q4p7w1z6@receipts.tolvi.co.uk" |
createdAtrequired | string (date-time)nullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get a receipt
GET/api/v1/books/receipts/{id}
- Scopes
books:read
The file’s state, what Tolvi AI read (with how sure it is), the checks, possible bank payments and what was confirmed. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200 OK, application/json
Body: object (Receipt).
A receipt or bill: the file (kept as the record), what Tolvi AI read from it, and what the person confirmed.
| Field | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
sourcerequired | string (ReceiptSource) | One of: |
fileNamerequired | string | |
contentTyperequired | string | |
sizeBytesrequired | integer | |
statusrequired | string (ReceiptFileStatus) | pending_upload, pending_scan (being checked for viruses), available, or rejected (with the reason). One of: |
rejectedReasonrequired | stringnullable | |
readingStatusrequired | string (ReceiptReadingStatus) | waiting (until the virus check passes), queued, reading, done or failed. One of: |
readingErrorrequired | stringnullable | Why reading failed, in plain English. |
readingrequired | object (ReceiptReading)nullable | What Tolvi AI read from the receipt: a suggestion for the person to confirm or correct. Money in integer pence. |
reading.documentTyperequired | string (ReceiptDocumentType) | One of: |
reading.supplierNamerequired | stringnullable | |
reading.supplierVatNumberrequired | stringnullable | As printed on the receipt. |
reading.documentNumberrequired | stringnullable | |
reading.daterequired | string (date)nullable | example "2026-03-31" |
reading.currencyrequired | stringnullable | example "GBP" |
reading.totalPencerequired | integernullable | Integer pence (GBP). |
reading.netPencerequired | integernullable | Integer pence (GBP). |
reading.vatPencerequired | integernullable | Integer pence (GBP). |
reading.vatBreakdownrequired | array of ReceiptVatRow | |
reading.vatBreakdown[].rateBpsrequired | integer | VAT rate in basis points (2000 = 20%). |
reading.vatBreakdown[].netPencerequired | integernullable | Integer pence (GBP). |
reading.vatBreakdown[].vatPencerequired | integer | Integer pence (GBP). |
reading.lineItemsrequired | array of ReceiptLineItem | |
reading.lineItems[].descriptionrequired | string | |
reading.lineItems[].quantityrequired | stringnullable | |
reading.lineItems[].amountPencerequired | integer | Integer pence (GBP). |
reading.lineItems[].vatRateBpsrequired | integernullable | |
reading.lineItems[].vatPence | integernullable | Integer pence (GBP). The VAT shown on this line. Absent on a reading from before lines were captured. |
reading.lineItems[].accountCode | stringnullable | The category Tolvi AI suggests, from the chart of accounts. |
reading.lineItems[].confidence | integer | How sure Tolvi AI is of this line, from 0 to 100. 0 to 100 |
reading.lineItems[].reason | stringnullable | Why it is unsure, when it is; otherwise empty. |
reading.moreLines | boolean | The document has more lines than Tolvi AI kept. |
reading.paymentMethodrequired | string (ReceiptPaymentMethod) | One of: |
reading.cardLast4required | stringnullable | The last four digits of the card only. |
reading.fieldsrequired | object (ReceiptFieldNotes) | |
reading.fields.supplierNamerequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
reading.fields.supplierName.confidencerequired | string (ReceiptConfidence) | One of: |
reading.fields.supplierName.reasonrequired | string | |
reading.fields.supplierName.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
reading.fields.daterequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
reading.fields.date.confidencerequired | string (ReceiptConfidence) | One of: |
reading.fields.date.reasonrequired | string | |
reading.fields.date.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
reading.fields.totalrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
reading.fields.total.confidencerequired | string (ReceiptConfidence) | One of: |
reading.fields.total.reasonrequired | string | |
reading.fields.total.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
reading.fields.vatrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
reading.fields.vat.confidencerequired | string (ReceiptConfidence) | One of: |
reading.fields.vat.reasonrequired | string | |
reading.fields.vat.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
reading.fields.vatNumberrequired | object (ReceiptFieldNote) | How sure Tolvi AI is of one field, and why. |
reading.fields.vatNumber.confidencerequired | string (ReceiptConfidence) | One of: |
reading.fields.vatNumber.reasonrequired | string | |
reading.fields.vatNumber.alternativerequired | stringnullable | Another reading worth checking, e.g. "£36.20". |
reading.notesrequired | string | |
reading.modelrequired | string | The model that read it. |
reading.promptVersionrequired | string | example "extract_receipt@1" |
reading.readAtrequired | string (date-time) | |
suggestedLinesrequired | array of ReceiptSuggestedLine | |
suggestedLines[].descriptionrequired | string | |
suggestedLines[].accountCoderequired | stringnullable | The suggested category, from the chart of accounts. |
suggestedLines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. |
suggestedLines[].vatPencerequired | integer | Integer pence (GBP). The VAT in that cost. |
suggestedLines[].vatCoderequired | string (VatCode) | One of: |
suggestedLines[].checkThisrequired | boolean | Tolvi AI was barely sure of this line: highlight it for the person to check. It never stops them. |
suggestedLines[].checkReasonrequired | stringnullable | |
checksrequired | object (ReceiptChecks)nullable | |
checks.ofrequired | string (ReceiptChecksOf) | Whether these checks are on what Tolvi AI read or on what the person confirmed. One of: |
checks.problemsrequired | array of ReceiptProblem | |
checks.problems[].coderequired | string | example "no_vat_number" |
checks.problems[].severityrequired | string (ReceiptProblemSeverity) | One of: |
checks.problems[].fieldrequired | stringnullable | example "vatNumber" |
checks.problems[].messagerequired | string | |
checks.problems[].sourcerequired | stringnullable | The rule behind it. |
checks.vatClaimablerequired | boolean | Whether the VAT on it can be reclaimed: the business is VAT registered and the receipt shows VAT and a valid UK VAT number. |
checks.suggestedVatCoderequired | string (VatCode)nullable | One of: |
checks.vatNumberrequired | stringnullable | The VAT number without GB, when it passes the check digits. |
duplicateOfIdrequired | string (uuid)nullable | An earlier receipt this looks like a copy of. |
matchesrequired | array of ReceiptMatch | |
matches[].bankLineIdrequired | string (uuid) | |
matches[].bankAccountIdrequired | string (uuid) | |
matches[].bookedAtrequired | string (date) | example "2026-03-31" |
matches[].amountPencerequired | integer | Integer pence (GBP). As on the bank line: money out is negative. |
matches[].descriptionrequired | string | |
matches[].scorerequired | integer | 0 to 100 |
matches[].bandrequired | string (ReceiptMatchBand) | One of: |
matches[].summaryrequired | string | |
matches[].reasonsrequired | array of string | |
reviewStatusrequired | string (ReceiptReviewStatus) | One of: |
confirmedrequired | object (ReceiptConfirmed)nullable | What the person confirmed. Money in integer pence. |
confirmed.supplierNamerequired | string | |
confirmed.supplierVatNumberrequired | stringnullable | |
confirmed.daterequired | string (date) | example "2026-03-31" |
confirmed.currencyrequired | string | example "GBP" |
confirmed.totalPencerequired | integer | Integer pence (GBP). |
confirmed.netPencerequired | integernullable | Integer pence (GBP). |
confirmed.vatPencerequired | integernullable | Integer pence (GBP). |
confirmed.vatCoderequired | string (VatCode)nullable | One of: |
confirmed.accountCoderequired | stringnullable | |
confirmed.paymentMethodrequired | string (ReceiptPaymentMethod)nullable | One of: |
confirmed.cardLast4required | stringnullable | |
confirmed.paidPersonallyrequired | boolean | |
confirmed.bankLineIdrequired | string (uuid)nullable | |
confirmed.noterequired | stringnullable | |
confirmed.correctedFieldsrequired | array of string | Fields the person changed from what Tolvi AI read; line1.category is the first line’s category. |
confirmed.linesrequired | array of ReceiptConfirmedLine | The lines it went into the books as. Empty when it was confirmed as one amount. |
confirmed.lines[].descriptionrequired | string | |
confirmed.lines[].accountCoderequired | stringnullable | |
confirmed.lines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. |
confirmed.lines[].vatPencerequired | integer | Integer pence (GBP). |
confirmed.confirmedByrequired | string (uuid)nullable | |
confirmed.confirmedAtrequired | string (date-time) | |
postingStatusrequired | string (ReceiptPostingStatus) | none, proposed (confirmed; waiting for the books), or posted. One of: |
postingProposalrequired | objectnullable | What confirming proposes for the books (version 1, or version 2 with lines). |
ledgerEntryIdrequired | string (uuid)nullable | |
emailFromrequired | stringnullable | For an emailed receipt: who sent it. |
emailSubjectrequired | stringnullable | |
previewablerequired | boolean | Whether a browser can show the image (a HEIC photo has a JPEG preview once read). |
uploadedByrequired | string (uuid)nullable | |
capturedAtrequired | string (date-time)nullable | |
createdAtrequired | string (date-time) | |
updatedAtrequired | string (date-time) |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Delete a receipt for good
DELETE/api/v1/books/receipts/{id}
- Scopes
books:write- Access
- A signed-in Tolvi session only, never an API token.
Removes a receipt that isn’t in the books, with its files. It can’t be undone. 409, saying why, when it has been confirmed, is in the books, is matched to a bank payment or is attached to a bill: those are accounting records. Signed in people only; recorded in the audit log. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
204 No content
Deleted.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Get a short-lived link to a receipt’s image
GET/api/v1/books/receipts/{id}/image
- Scopes
books:read
Only once the file has passed the virus check. Recorded in the audit log. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
variant | query | string | preview (default): the JPEG made from a HEIC photo when there is one; original: the file as received. One of: |
Responses
200 OK, application/json
Body: object (ReceiptImage).
A short-lived link to the receipt’s image (or its JPEG preview for a HEIC photo).
| Field | Type | Description |
|---|---|---|
urlrequired | string (uri) | |
contentTyperequired | string | |
expiresAtrequired | string (date-time) |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Presign a receipt upload again
POST/api/v1/books/receipts/{id}/upload
- Scopes
books:write
A fresh PUT for a receipt still waiting for its file (the first PUT failed or expired). Only the person who added it; 409 once uploaded. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Finish a receipt upload
POST/api/v1/books/receipts/{id}/upload-complete
- Scopes
books:write
Call after the PUT. Checks the stored file’s size, type and contents, records its checksum and starts the virus check. Once it passes, Tolvi AI reads it (readingStatus). 422 if the file is refused or malware is found. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200 OK, application/json
Body: object (Receipt).
A receipt or bill: the file (kept as the record), what Tolvi AI read from it, and what the person confirmed.
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).
Read a receipt again
POST/api/v1/books/receipts/{id}/read-again
- Scopes
books:write
After a reading failed. Only for receipts still to review. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200 OK, application/json
Body: object (Receipt).
A receipt or bill: the file (kept as the record), what Tolvi AI read from it, and what the person confirmed.
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).
Confirm a receipt
POST/api/v1/books/receipts/{id}/confirm
- Scopes
books:write
The person’s figures (what Tolvi AI read, checked or corrected), optionally the bank payment it matches or that it was paid personally. Checks run again on these figures. Confirming records what it means for the books (postingProposal); the ledger posts it. Needs a person, not an API client. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Request body
Required. application/json: object (ConfirmReceipt).
The person’s figures. Money in integer pence. Net plus VAT must equal the total.
| Field | Type | Description |
|---|---|---|
supplierNamerequired | string | up to 200 characters |
supplierVatNumber | stringnullable | up to 20 characters |
daterequired | string (date) | example "2026-03-31" |
currency | string | example "GBP" |
totalPencerequired | integer | Integer pence (GBP). at least 1 |
poundsPence | integernullable | Integer pence (GBP). What a foreign receipt paid with the person’s own money cost in pounds. Required then; ignored otherwise. at least 1 |
netPence | integernullable | Integer pence (GBP). at least 0 |
vatPence | integernullable | Integer pence (GBP). at least 0 |
vatCode | string (VatCode)nullable | One of: |
accountCode | stringnullable | The account it goes to, from the chart of accounts (or chosen later, when the bank payment is confirmed). |
paymentMethod | string (ReceiptPaymentMethod)nullable | One of: |
cardLast4 | stringnullable | pattern ^\d{4}$ |
paidPersonally | boolean | Paid with the owner’s own money: the business owes it back. |
bankLineId | string (uuid)nullable | The bank payment it matches, from |
note | stringnullable | up to 1000 characters |
lines | array of ConfirmReceiptLine | The lines, each with its own category. They must add up to the total. Ignored for a receipt in another currency, where the amount in pounds comes from the bank payment. Left out, the whole amount is one line. |
lines[].descriptionrequired | string | up to 500 characters |
lines[].accountCode | stringnullable | The account this line goes to. Left out, it is chosen when the bank payment is confirmed, or goes to general costs. |
lines[].grossPencerequired | integer | Integer pence (GBP). What the line cost, including any VAT. A discount is negative. |
lines[].vatPence | integernullable | Integer pence (GBP). The VAT in that cost. Used only when the VAT can be reclaimed. |
lines[].vatCode | string (VatCode)nullable | One of: |
Responses
200 OK, application/json
Body: object (Receipt).
A receipt or bill: the file (kept as the record), what Tolvi AI read from it, and what the person confirmed.
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).
Discard a receipt
POST/api/v1/books/receipts/{id}/discard
- Scopes
books:write
Not a receipt, or a copy of one already added. The file is kept until the receipt is deleted. A confirmed receipt can’t be discarded. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200 OK, application/json
Body: object (Receipt).
A receipt or bill: the file (kept as the record), what Tolvi AI read from it, and what the person confirmed.
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).
What the business owes
GET/api/v1/books/purchases/summary
- Scopes
books:read
What is owed, overdue and due in the next 7 days as at today, by supplier (most overdue first), with drafts. Part of the books product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: object (BooksPurchasesSummary).
What the business owes as at today.
| Field | Type | Description |
|---|---|---|
asAtrequired | string (date) | example "2026-03-31" |
owedPencerequired | integer | Integer pence (GBP). |
overduePencerequired | integer | Integer pence (GBP). |
dueSoonPencerequired | integer | Integer pence (GBP). What falls due in the next 7 days. |
unpaidBillsrequired | integer | |
overdueBillsrequired | integer | |
draftsrequired | integer | |
creditorsrequired | array of BooksPurchasesCreditor | |
creditors[].supplierIdrequired | string (uuid) | |
creditors[].supplierNamerequired | string | |
creditors[].owedPencerequired | integer | Integer pence (GBP). |
creditors[].overduePencerequired | integer | Integer pence (GBP). |
creditors[].billsrequired | integer |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List suppliers
GET/api/v1/books/suppliers
- Scopes
books:read
By name, with what is still owed to each. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
q | query | string | |
archived | query | string | One of: |
Responses
200 OK, application/json
Body: object (BooksSupplierList).
| Field | Type | Description |
|---|---|---|
itemsrequired | array of BooksSupplier | |
items[].idrequired | string (uuid) | |
items[].namerequired | string | |
items[].emailrequired | stringnullable | |
items[].addressLinesrequired | array of string | |
items[].postcoderequired | stringnullable | |
items[].vatNumberrequired | stringnullable | |
items[].paymentTermsDaysrequired | integernullable | Days to pay; null means 30 days unless a bill gives a due date. |
items[].defaultAccountIdrequired | string (uuid)nullable | The account used on the last bill, to fill in the next one. |
items[].defaultVatCoderequired | stringnullable | |
items[].owedPencerequired | integer | What is still owed, in pence. |
items[].openBillsrequired | integer | |
items[].archivedrequired | boolean |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Add a supplier
POST/api/v1/books/suppliers
- Scopes
books:write- Retries
- Send an
Idempotency-Keyto make retries safe.
Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
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 (BooksSupplierInput).
| Field | Type | Description |
|---|---|---|
namerequired | string | up to 200 characters |
email | stringnullable | |
addressLines | array of string | |
postcode | stringnullable | |
vatNumber | stringnullable | |
paymentTermsDays | integernullable | 0 to 365 |
Responses
201 Created, application/json
Body: object (BooksSupplier).
A business this business buys from.
| Field | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
namerequired | string | |
emailrequired | stringnullable | |
addressLinesrequired | array of string | |
postcoderequired | stringnullable | |
vatNumberrequired | stringnullable | |
paymentTermsDaysrequired | integernullable | Days to pay; null means 30 days unless a bill gives a due date. |
defaultAccountIdrequired | string (uuid)nullable | The account used on the last bill, to fill in the next one. |
defaultVatCoderequired | stringnullable | |
owedPencerequired | integer | What is still owed, in pence. |
openBillsrequired | integer | |
archivedrequired | boolean |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
Get a supplier
GET/api/v1/books/suppliers/{id}
- Scopes
books:read
Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
200 OK, application/json
Body: object (BooksSupplier).
A business this business buys from.
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).
Change or archive a supplier
PATCH/api/v1/books/suppliers/{id}
- Scopes
books:write
A supplier who is still owed money can’t be archived. Posted bills keep the name they were posted with. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Request body
Required. application/json: object (BooksSupplierPatch).
| Field | Type | Description |
|---|---|---|
name | string | up to 200 characters |
email | stringnullable | |
addressLines | array of string | |
postcode | stringnullable | |
vatNumber | stringnullable | |
paymentTermsDays | integernullable | 0 to 365 |
archived | boolean |
Responses
200 OK, application/json
Body: object (BooksSupplier).
A business this business buys from.
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).
List bills
GET/api/v1/books/bills
- Scopes
books:read
Newest first. Status is worked out as at today: to_pay includes part-paid bills; overdue is past its due date with something still to pay. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | number | |
supplierId | query | string (uuid) | |
kind | query | string | One of: |
status | query | string | One of: |
Responses
200 OK, application/json
Body: object (BooksBillList).
| Field | Type | Description |
|---|---|---|
itemsrequired | array of BooksBill | |
items[].idrequired | string (uuid) | |
items[].kindrequired | string (BooksBillKind) | One of: |
items[].staterequired | string (BooksBillState) | One of: |
items[].statusrequired | string (BooksBillStatus) | One of: |
items[].supplierIdrequired | string (uuid) | |
items[].supplierNamerequired | string | |
items[].referencerequired | stringnullable | The supplier’s own invoice or credit note number. |
items[].billDaterequired | string (date)nullable | example "2026-03-31" |
items[].dueDaterequired | string (date)nullable | example "2026-03-31" |
items[].notesrequired | stringnullable | |
items[].reasonrequired | stringnullable | |
items[].creditsIdrequired | string (uuid)nullable | |
items[].openingrequired | boolean | Brought forward with the starting balances: dated before the books start, with no VAT breakdown. |
items[].receiptIdrequired | string (uuid)nullable | The photo or PDF this came from (GET /books/receipts/{id}/image). |
items[].netPencerequired | integer | Integer pence (GBP). |
items[].vatPencerequired | integer | Integer pence (GBP). |
items[].grossPencerequired | integer | Integer pence (GBP). |
items[].paidPencerequired | integer | Integer pence (GBP). |
items[].creditedPencerequired | integer | Integer pence (GBP). |
items[].outstandingPencerequired | integer | Integer pence (GBP). |
items[].overpaidPencerequired | integer | Integer pence (GBP). |
items[].daysOverduerequired | integer | |
items[].ledgerEntryIdrequired | string (uuid)nullable | |
items[].createdAtrequired | string (date-time) | |
items[].postedAtrequired | string (date-time)nullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Start or add a bill
POST/api/v1/books/bills
- Scopes
books:write- Retries
- Send an
Idempotency-Keyto make retries safe.
Give a supplierId, or a supplierName to find or add one. With post: true it goes into the books straight away (Dr costs and VAT, Cr creditors); otherwise it stays a draft. With a receiptId the photo or PDF stays with the bill; posting confirms the receipt with these figures, which is the person’s decision on what Tolvi AI read. 409 if the same supplier reference has already been added. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
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 (BooksCreateBill).
A supplierId, or a supplierName to find or add one.
| Field | Type | Description |
|---|---|---|
supplierId | string (uuid) | |
supplierName | string | up to 200 characters |
reference | stringnullable | The supplier’s own invoice number. |
billDate | string (date)nullable | example "2026-03-31" |
dueDate | string (date)nullable | Left out: the supplier’s terms, or 30 days. example "2026-03-31" |
notes | stringnullable | |
lines | array of BooksBillLineInput | |
lines[].descriptionrequired | string | up to 500 characters |
lines[].accountIdrequired | string (uuid) | A cost or asset account. |
lines[].netPencerequired | integer | Integer pence (GBP). |
lines[].vatCoderequired | string | example "S20" |
lines[].vatPence | integer | Integer pence (GBP). The VAT on the bill; worked from the rate at the bill’s date when left out. |
receiptId | string (uuid) | The receipt or bill photo this came from. |
post | boolean | Add it to the books straight away. |
Responses
201 Created, application/json
Body: object (BooksBillDetail).
| Field | Type | Description |
|---|---|---|
idrequired | string (uuid) | |
kindrequired | string (BooksBillKind) | One of: |
staterequired | string (BooksBillState) | One of: |
statusrequired | string (BooksBillStatus) | One of: |
supplierIdrequired | string (uuid) | |
supplierNamerequired | string | |
referencerequired | stringnullable | The supplier’s own invoice or credit note number. |
billDaterequired | string (date)nullable | example "2026-03-31" |
dueDaterequired | string (date)nullable | example "2026-03-31" |
notesrequired | stringnullable | |
reasonrequired | stringnullable | |
creditsIdrequired | string (uuid)nullable | |
openingrequired | boolean | Brought forward with the starting balances: dated before the books start, with no VAT breakdown. |
receiptIdrequired | string (uuid)nullable | The photo or PDF this came from (GET /books/receipts/{id}/image). |
netPencerequired | integer | Integer pence (GBP). |
vatPencerequired | integer | Integer pence (GBP). |
grossPencerequired | integer | Integer pence (GBP). |
paidPencerequired | integer | Integer pence (GBP). |
creditedPencerequired | integer | Integer pence (GBP). |
outstandingPencerequired | integer | Integer pence (GBP). |
overpaidPencerequired | integer | Integer pence (GBP). |
daysOverduerequired | integer | |
ledgerEntryIdrequired | string (uuid)nullable | |
createdAtrequired | string (date-time) | |
postedAtrequired | string (date-time)nullable | |
linesrequired | array of BooksBillLine | |
lines[].idrequired | string (uuid) | |
lines[].descriptionrequired | string | |
lines[].accountIdrequired | string (uuid) | |
lines[].accountNamerequired | string | |
lines[].netPencerequired | integer | Integer pence (GBP). |
lines[].vatCoderequired | string | example "S20" |
lines[].vatPencerequired | integer | Integer pence (GBP). |
paymentsrequired | array of BooksBillPayment | |
payments[].idrequired | string (uuid) | |
payments[].paidOnrequired | string (date) | example "2026-03-31" |
payments[].amountPencerequired | integer | Integer pence (GBP). |
payments[].methodrequired | string (BooksBillPaymentMethod) | One of: |
payments[].accountIdrequired | string (uuid) | |
payments[].accountNamerequired | string | |
payments[].referencerequired | stringnullable | |
payments[].bankLineIdrequired | string (uuid)nullable | Set when it was matched from a bank line: undo it in Bank. |
payments[].undonerequired | boolean | |
creditsrequired | array of BooksBillCredit | |
credits[].idrequired | string (uuid) | |
credits[].referencerequired | stringnullable | |
credits[].daterequired | string (date)nullable | example "2026-03-31" |
credits[].amountPencerequired | integer | Integer pence (GBP). |
credits[].reasonrequired | stringnullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
A bill as Tolvi AI read it
GET/api/v1/books/bills/from-receipt/{receiptId}
- Scopes
books:read
A suggestion to check, from a receipt or bill that has been read. Nothing is saved or posted. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
receiptIdrequired | path | string (uuid) |
Responses
200 OK, application/json
Body: object (BooksBillProposal).
A bill as Tolvi AI read it from a receipt or bill: a suggestion. Nothing is saved until the person checks it and creates the bill.
| Field | Type | Description |
|---|---|---|
receiptIdrequired | string (uuid) | |
existingBillIdrequired | string (uuid)nullable | A draft already started from this file: open that instead. |
supplierIdrequired | string (uuid)nullable | The supplier if the name read matches one. |
supplierNamerequired | stringnullable | |
referencerequired | stringnullable | |
billDaterequired | string (date)nullable | example "2026-03-31" |
dueDaterequired | string (date)nullable | example "2026-03-31" |
linesrequired | array of BooksBillProposalLine | |
lines[].descriptionrequired | string | |
lines[].accountIdrequired | string (uuid)nullable | |
lines[].netPencerequired | integer | Integer pence (GBP). |
lines[].vatCoderequired | string | |
lines[].vatPencerequired | integer | Integer pence (GBP). |
lines[].checkThisrequired | boolean | Tolvi AI was barely sure of this line: highlight it for the person to check. It never stops them. |
lines[].checkReasonrequired | stringnullable | |
warningsrequired | array of string | What to check, in plain English. |
documentTotalPencerequired | integernullable | Integer pence (GBP). The total printed on the document, to compare the lines with. Null when it couldn’t be read or isn’t in pounds. |
linesWarningrequired | stringnullable | Which of the warnings says the lines don’t add up, so it can go once they do. |
readingStatusrequired | string (BooksBillProposalReading) | One of: |
foreignCurrencyrequired | stringnullable | The currency, when the document isn’t in pounds. Bills are in pounds, so no amount is suggested. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Get a bill
GET/api/v1/books/bills/{id}
- Scopes
books:read
With its lines, what is owed, payments and credits. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Change a draft bill
PATCH/api/v1/books/bills/{id}
- Scopes
books:write
409 once it is in the books: a posted bill is corrected with a credit. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Request body
Required. application/json: object (BooksUpdateBill).
| Field | Type | Description |
|---|---|---|
supplierId | string (uuid) | |
supplierName | string | up to 200 characters |
reference | stringnullable | |
billDate | string (date)nullable | example "2026-03-31" |
dueDate | string (date)nullable | example "2026-03-31" |
notes | stringnullable | |
lines | array of BooksBillLineInput | |
lines[].descriptionrequired | string | up to 500 characters |
lines[].accountIdrequired | string (uuid) | A cost or asset account. |
lines[].netPencerequired | integer | Integer pence (GBP). |
lines[].vatCoderequired | string | example "S20" |
lines[].vatPence | integer | Integer pence (GBP). The VAT on the bill; worked from the rate at the bill’s date when left out. |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Delete a draft bill
DELETE/api/v1/books/bills/{id}
- Scopes
books:write
409 once it is in the books. The file it came from is kept. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
204 No content
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Add a draft bill to the books
POST/api/v1/books/bills/{id}/post
- Scopes
books:write
Posts it through the ledger with VAT on every line, in one step. 422 with the reasons when it can’t post (for example a locked period). Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).
Record a supplier credit note
POST/api/v1/books/bills/{id}/credit
- Scopes
books:write- Retries
- Send an
Idempotency-Keyto make retries safe.
All of what hasn’t been credited, or amountPence gross shared across the bill’s lines, at the VAT rates of the original supply (VAT Notice 700 section 19). Posts straight away. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
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 (BooksCreditBill).
| Field | Type | Description |
|---|---|---|
reasonrequired | string | up to 200 characters |
reference | stringnullable | The supplier’s credit note number. |
date | string (date) | example "2026-03-31" |
amountPence | integer | Integer pence (GBP). Gross. Left out, everything not credited yet. at least 1 |
Responses
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).
Cancel a bill
POST/api/v1/books/bills/{id}/void
- Scopes
books:write- Retries
- Send an
Idempotency-Keyto make retries safe.
Posts a credit for the whole bill, so the record stays and the books balance. 409 if anything has been paid or credited. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
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 (BooksVoidBill).
| Field | Type | Description |
|---|---|---|
reasonrequired | string | up to 200 characters |
date | string (date) | example "2026-03-31" |
Responses
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).
Record a payment made
POST/api/v1/books/bills/{id}/payments
- Scopes
books:write- Retries
- Send an
Idempotency-Keyto make retries safe.
For money paid outside the bank feed. Part payments are fine; no more than is owed. Dr creditors, Cr the account it came from, and the share of the bill’s VAT settled for cash accounting. Tolvi doesn’t send the money. A bank line is matched in Bank instead. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
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 (BooksPayBill).
| Field | Type | Description |
|---|---|---|
paidOnrequired | string (date) | example "2026-03-31" |
amountPencerequired | integer | Integer pence (GBP). at least 1 |
accountIdrequired | string (uuid) | The bank, cash or card account it came from, or the owner’s own account (director’s loan, capital introduced, partner’s current account). |
method | string (BooksBillPayMethod) | One of: |
reference | stringnullable |
Responses
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).
Undo a payment recorded by hand
POST/api/v1/books/bills/{id}/payments/{paymentId}/undo
- Scopes
books:write
Posts the reversal and reverses its VAT settlement. 409 for one matched from a bank line: undo that in Bank. Part of the books product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | |
paymentIdrequired | path | string (uuid) |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).