Skip to main content

Integrations

21 operations in the Tolvi API, version 1. Paths are relative to https://api.tolvi.co.uk. Every amount is integer pence.

List bookkeeping providers

GET/api/v1/integrations/providers

Scopes
clients:read

Which providers this Tolvi deployment is set up for (developer app credentials present), and this firm’s connections to each by status.

Responses

200 OK, application/json

Body: object (IntegrationProviders).

200 OK response for GET /api/v1/integrations/providers
FieldTypeDescription
providersrequiredarray of IntegrationProviderInfo
providers[].​idrequiredstring (IntegrationProvider)

One of: xero, quickbooks, sage, freeagent

providers[].​labelrequiredstring

example "Xero"

providers[].​configuredrequiredboolean

Set up for this Tolvi deployment (developer app credentials present). Only configured providers can be connected.

providers[].​connectionsrequiredobject (ConnectionCounts)

This firm’s client connections to the provider, by status.

providers[].​connections.​activerequiredinteger

at least 0

providers[].​connections.​expiredrequiredinteger

Refresh token lapsed or access removed: needs reconnecting.

at least 0

providers[].​connections.​revokedrequiredinteger

at least 0

providers[].​connections.​errorrequiredinteger

at least 0

providers[].​connections.​needsOrganisationrequiredinteger

Connected, but the organisation has not been chosen yet.

at least 0

fakerequiredboolean

Development only: the fixture-driven fake provider stands in for every provider.

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).

Finish connecting a client’s books

POST/api/v1/integrations/callback

Scopes
integrations:manage
Access
A signed-in Tolvi session only, never an API token.

Called by the web app’s /integrations/callback/{provider} page with the code and state from the provider’s redirect, as the signed-in user who started the connect. The state is single-use and expires after 10 minutes. With one organisation it is bound at once; with several, choose one with PUT /clients/{clientId}/connection/organisation. A practice connect (POST /integrations/practice/authorize) is refused with 409 and its state left unused: finish it with POST /integrations/practice/callback.

Request body

Required. application/json: object (IntegrationCallbackRequest).

Request body for POST /api/v1/integrations/callback
FieldTypeDescription
providerrequiredstring (IntegrationProvider)

One of: xero, quickbooks, sage, freeagent

coderequiredstring

The code from the provider’s redirect.

1 to 4096 characters

staterequiredstring

The state from the provider’s redirect.

1 to 512 characters

paramsobject

Any other query parameters from the redirect (QuickBooks sends realmId).

Responses

200 OK, application/json

Body: object (IntegrationCallbackResult).

200 OK response for POST /api/v1/integrations/callback
FieldTypeDescription
clientIdrequiredstring (uuid)

The client connected.

grantIdrequiredstring (uuid)nullable

Always null. Kept for compatibility: a practice connect finishes with POST /integrations/practice/callback, which returns the grantId.

outcomerequiredstring (IntegrationCallbackOutcome)

One of: connected, choose_organisation

connectionrequiredobject (ClientConnection)
connection.​clientIdrequiredstring (uuid)
connection.​connectedrequiredboolean
connection.​providerrequiredstring (IntegrationProvider)nullable

One of: xero, quickbooks, sage, freeagent

connection.​providerLabelrequiredstringnullable

example "Xero"

connection.​statusrequiredstring (ConnectionStatus)nullable

One of: active, expired, revoked, error

connection.​orgIdrequiredstringnullable
connection.​orgNamerequiredstringnullable
connection.​needsOrganisationrequiredboolean

Several organisations were authorised: choose one with PUT …/connection/organisation.

connection.​organisationsrequiredarray of ConnectionOrganisation

The organisations to choose from (only while needsOrganisation).

connection.​organisations[].​idrequiredstring

The provider’s organisation (tenant, realm, business or company) id.

connection.​organisations[].​namerequiredstring
connection.​viaPracticerequiredboolean

Connected by importing clients from the practice’s Xero: the tokens are the practice authorisation’s, shared with the other clients imported with it.

connection.​lastErrorrequiredstringnullable

The last failure, in words. Never contains a token.

connection.​lastSyncedAtrequiredstring (date-time)nullable
connection.​connectedAtrequiredstring (date-time)nullable
connection.​connectedByrequiredstring (uuid)nullable

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).

Start importing clients from Xero

POST/api/v1/integrations/practice/authorize

Scopes
integrations:manage and clients:write
Access
A signed-in Tolvi session only, never an API token.

Returns Xero’s sign-in URL. The adviser ticks every client organisation to bring in (Xero lets one sign-in cover several). Xero returns them to /integrations/callback/xero, and POST /integrations/practice/callback stores one authorisation for them all.

Request body

Required. application/json: object (AuthorizePracticeRequest).

Request body for POST /api/v1/integrations/practice/authorize
FieldTypeDescription
providerrequiredstring (PracticeProvider)

Only Xero offers the organisation details the import needs.

One of: xero

Responses

200 OK, application/json

Body: object (AuthorizeConnectionResult).

200 OK response for POST /api/v1/integrations/practice/authorize
FieldTypeDescription
authorizeUrlrequiredstring (uri)

Send the user here. The provider returns them to /integrations/callback/{provider}; valid for 10 minutes, once.

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).

Finish importing clients from Xero

POST/api/v1/integrations/practice/callback

Scopes
integrations:manage and clients:write
Access
A signed-in Tolvi session only, never an API token.

Called by the web app’s /integrations/callback/{provider} page with the code and state from Xero’s redirect, as the signed-in user who started the import (POST /integrations/practice/authorize). The state is single-use and expires after 10 minutes. Stores one authorisation for every organisation the adviser ticked and returns its grantId; nothing is imported until POST /integrations/practice/grants/{grantId}/import. A client’s connect is refused with 409 and its state left unused: finish it with POST /integrations/callback.

Request body

Required. application/json: object (PracticeCallbackRequest).

Request body for POST /api/v1/integrations/practice/callback
FieldTypeDescription
providerrequiredstring (PracticeProvider)

Only Xero offers the organisation details the import needs.

One of: xero

coderequiredstring

The code from the provider’s redirect.

1 to 4096 characters

staterequiredstring

The state from the provider’s redirect.

1 to 512 characters

paramsobject

Any other query parameters from the redirect.

Responses

200 OK, application/json

Body: object (PracticeCallbackResult).

200 OK response for POST /api/v1/integrations/practice/callback
FieldTypeDescription
grantIdrequiredstring (uuid)

The authorisation to import clients from (GET /integrations/practice/grants/{grantId}/organisations).

outcomerequiredstring (PracticeCallbackOutcome)

One of: practice_authorised

organisationCountrequiredinteger

Organisations the adviser authorised.

at least 1

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).

List practice authorisations

GET/api/v1/integrations/practice/grants

Scopes
integrations:manage

This firm’s Xero authorisations for importing clients, newest first. Tokens are never returned.

Responses

200 OK, application/json

Body: object (PracticeGrantList).

200 OK response for GET /api/v1/integrations/practice/grants
FieldTypeDescription
rowsrequiredarray of PracticeGrant

Newest first.

rows[].​idrequiredstring (uuid)
rows[].​providerrequiredstring (IntegrationProvider)

One of: xero, quickbooks, sage, freeagent

rows[].​providerLabelrequiredstring

example "Xero"

rows[].​statusrequiredstring (ConnectionStatus)

One of: active, expired, revoked, error

rows[].​organisationCountrequiredinteger

Organisations the adviser authorised.

at least 0

rows[].​clientsConnectedrequiredinteger

Clients connected through this authorisation.

at least 0

rows[].​lastErrorrequiredstringnullable
rows[].​connectedAtrequiredstring (date-time)
rows[].​connectedByrequiredstring (uuid)nullable

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).

List the organisations to import

GET/api/v1/integrations/practice/grants/{grantId}/organisations

Scopes
integrations:manage

Each organisation the adviser authorised, as Xero describes it (name, legal form, Companies House and VAT numbers, financial year end), with the suggested entity type, the next year end, and whether it is already in Tolvi (matched by company number, then name) or already connected.

Parameters

Parameters for GET /api/v1/integrations/practice/grants/{grantId}/organisations
NameInTypeDescription
grantIdrequiredpathstring (uuid)

Practice authorisation id.

Responses

200 OK, application/json

Body: object (PracticeOrganisationList).

200 OK response for GET /api/v1/integrations/practice/grants/{grantId}/organisations
FieldTypeDescription
grantrequiredobject (PracticeGrant)
grant.​idrequiredstring (uuid)
grant.​providerrequiredstring (IntegrationProvider)

One of: xero, quickbooks, sage, freeagent

grant.​providerLabelrequiredstring

example "Xero"

grant.​statusrequiredstring (ConnectionStatus)

One of: active, expired, revoked, error

grant.​organisationCountrequiredinteger

Organisations the adviser authorised.

at least 0

grant.​clientsConnectedrequiredinteger

Clients connected through this authorisation.

at least 0

grant.​lastErrorrequiredstringnullable
grant.​connectedAtrequiredstring (date-time)
grant.​connectedByrequiredstring (uuid)nullable
organisationsrequiredarray of PracticeOrganisation
organisations[].​orgIdrequiredstring

The provider’s organisation (tenant) id.

organisations[].​namerequiredstring

The client name to use (the legal name when Xero has one).

organisations[].​legalNamerequiredstringnullable
organisations[].​organisationTyperequiredstringnullable

The provider’s own word for the legal form, e.g. COMPANY, PARTNERSHIP, SOLE_TRADER, CHARITY.

organisations[].​entityTyperequiredstring (EntityType)nullable

Suggested Tolvi entity type; null when Tolvi doesn’t prepare this kind of entity (choose one to import it).

One of: LTD_FRS102_1A, LTD_FRS105_MICRO, LLP, SOLE_TRADER, INDIVIDUAL, PARTNERSHIP, TRUST, ESTATE, CHARITY_UNINCORPORATED, CHARITY_TRUST, CIO, SCIO, CHARITABLE_COMPANY

organisations[].​entityTypeReasonrequiredstring
organisations[].​companyNumberrequiredstringnullable

Companies House number, normalised (companies and LLPs only).

organisations[].​vatNumberrequiredstringnullable

VAT number as held in Xero.

organisations[].​yearEndDayrequiredintegernullable

1 to 31

organisations[].​yearEndMonthrequiredintegernullable

1 to 12

organisations[].​nextPeriodEndrequiredstring (date)nullable

The first year end on or after today (29 February becomes 28 February outside leap years).

example "2026-03-31"

organisations[].​baseCurrencyrequiredstringnullable
organisations[].​isDemoCompanyrequiredboolean
organisations[].​warningsrequiredarray of string

Reasons it isn’t ticked by default.

organisations[].​profileErrorrequiredstringnullable

Why the organisation’s details couldn’t be read (not importable until reconnected).

organisations[].​connectedClientrequiredobject (PracticeClientRef)nullable

Already connected to this Tolvi client.

organisations[].​connectedClient.​clientIdrequiredstring (uuid)
organisations[].​connectedClient.​clientNamerequiredstring
organisations[].​matchrequiredobject (PracticeClientMatch)nullable

Already in Tolvi: importing links to this client instead of creating one.

organisations[].​match.​clientIdrequiredstring (uuid)
organisations[].​match.​clientNamerequiredstring
organisations[].​match.​matchedByrequiredstring (PracticeMatchBasis)

One of: company_number, name

organisations[].​match.​connectedElsewhererequiredstringnullable

The client is already connected to other books (it is then left as it is).

organisations[].​suggestedrequiredboolean

Pre-tick it: new to Tolvi, a type Tolvi prepares and nothing to check.

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).

Import clients from Xero

POST/api/v1/integrations/practice/grants/{grantId}/import

Scopes
integrations:manage and clients:write
Retries
Send an Idempotency-Key to make retries safe.

Creates a client for each chosen organisation, or links it to the client it matches (company number, then name; never a duplicate), sets the year end, and connects the client’s books through this authorisation so the trial balance can be pulled without connecting again. Organisations already connected, or matching a client connected to other books, are reported and left alone.

Parameters

Parameters for POST /api/v1/integrations/practice/grants/{grantId}/import
NameInTypeDescription
grantIdrequiredpathstring (uuid)

Practice authorisation id.

Idempotency-Keyheaderstring

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 (PracticeImportRequest).

Request body for POST /api/v1/integrations/practice/grants/{grantId}/import
FieldTypeDescription
organisationsrequiredarray of PracticeImportItem

1 to 1000 items

organisations[].​orgIdrequiredstring

1 to 200 characters

organisations[].​entityTypestring (EntityType)

For a new client; defaults to the suggestion. Ignored when linking.

One of: LTD_FRS102_1A, LTD_FRS105_MICRO, LLP, SOLE_TRADER, INDIVIDUAL, PARTNERSHIP, TRUST, ESTATE, CHARITY_UNINCORPORATED, CHARITY_TRUST, CIO, SCIO, CHARITABLE_COMPANY

organisations[].​yearEndDayintegernullable

Year end day (with yearEndMonth); defaults to none.

1 to 31

organisations[].​yearEndMonthintegernullable

1 to 12

organisations[].​clientIdstring (uuid)

Link to this existing client instead of matching.

organisations[].​createNewboolean

Create a new client even if one matches.

Responses

200 OK, application/json

Body: object (PracticeImportResult).

200 OK response for POST /api/v1/integrations/practice/grants/{grantId}/import
FieldTypeDescription
createdrequiredinteger

at least 0

linkedrequiredinteger

at least 0

alreadyConnectedrequiredinteger

at least 0

skippedrequiredinteger

at least 0

resultsrequiredarray of PracticeImportOutcome
results[].​orgIdrequiredstring
results[].​orgNamerequiredstring
results[].​outcomerequiredstring (PracticeImportOutcomeKind)

One of: created, linked, already_connected, skipped

results[].​clientIdrequiredstring (uuid)nullable
results[].​clientNamerequiredstringnullable
results[].​reasonrequiredstringnullable

Why it was skipped.

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).

Remove a practice authorisation

DELETE/api/v1/integrations/practice/grants/{grantId}

Scopes
integrations:manage

Revokes it at Xero and disconnects every client connected through it (their open engagements go back to manual). Clients and imported trial balances are kept.

Parameters

Parameters for DELETE /api/v1/integrations/practice/grants/{grantId}
NameInTypeDescription
grantIdrequiredpathstring (uuid)

Practice authorisation id.

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).

Get a client’s bookkeeping connection

GET/api/v1/clients/{clientId}/connection

Scopes
clients:read

Status, organisation and when the books were last read. Tokens are never returned.

Parameters

Parameters for GET /api/v1/clients/{clientId}/connection
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

Responses

200 OK, application/json

Body: object (ClientConnection).

200 OK response for GET /api/v1/clients/{clientId}/connection
FieldTypeDescription
clientIdrequiredstring (uuid)
connectedrequiredboolean
providerrequiredstring (IntegrationProvider)nullable

One of: xero, quickbooks, sage, freeagent

providerLabelrequiredstringnullable

example "Xero"

statusrequiredstring (ConnectionStatus)nullable

One of: active, expired, revoked, error

orgIdrequiredstringnullable
orgNamerequiredstringnullable
needsOrganisationrequiredboolean

Several organisations were authorised: choose one with PUT …/connection/organisation.

organisationsrequiredarray of ConnectionOrganisation

The organisations to choose from (only while needsOrganisation).

organisations[].​idrequiredstring

The provider’s organisation (tenant, realm, business or company) id.

organisations[].​namerequiredstring
viaPracticerequiredboolean

Connected by importing clients from the practice’s Xero: the tokens are the practice authorisation’s, shared with the other clients imported with it.

lastErrorrequiredstringnullable

The last failure, in words. Never contains a token.

lastSyncedAtrequiredstring (date-time)nullable
connectedAtrequiredstring (date-time)nullable
connectedByrequiredstring (uuid)nullable

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).

Disconnect a client’s books

DELETE/api/v1/clients/{clientId}/connection

Scopes
integrations:manage

Revokes Tolvi’s access at the provider where it supports that (Xero, QuickBooks, Sage; for FreeAgent remove the app there), then forgets the connection. Imported trial balances are kept.

Parameters

Parameters for DELETE /api/v1/clients/{clientId}/connection
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

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).

Start connecting a client’s books

POST/api/v1/clients/{clientId}/connection/authorize

Scopes
integrations:manage
Access
A signed-in Tolvi session only, never an API token.

Returns the provider’s sign-in URL to send the user to. Reconnecting replaces the current connection once the user completes it.

Parameters

Parameters for POST /api/v1/clients/{clientId}/connection/authorize
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

Request body

Required. application/json: object (AuthorizeConnectionRequest).

Request body for POST /api/v1/clients/{clientId}/connection/authorize
FieldTypeDescription
providerrequiredstring (IntegrationProvider)

One of: xero, quickbooks, sage, freeagent

Responses

200 OK, application/json

Body: object (AuthorizeConnectionResult).

200 OK response for POST /api/v1/clients/{clientId}/connection/authorize
FieldTypeDescription
authorizeUrlrequiredstring (uri)

Send the user here. The provider returns them to /integrations/callback/{provider}; valid for 10 minutes, once.

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).

Choose the client’s organisation

PUT/api/v1/clients/{clientId}/connection/organisation

Scopes
integrations:manage
Access
A signed-in Tolvi session only, never an API token.

One of the organisations the connection was authorised for.

Parameters

Parameters for PUT /api/v1/clients/{clientId}/connection/organisation
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

Request body

Required. application/json: object (ChooseOrganisationRequest).

Request body for PUT /api/v1/clients/{clientId}/connection/organisation
FieldTypeDescription
orgIdrequiredstring

1 to 200 characters

Responses

200 OK, application/json

Body: object (ClientConnection).

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 a client’s chart of accounts

GET/api/v1/clients/{clientId}/chart-of-accounts

Scopes
clients:read

Every account in the client’s books, with or without a balance (the trial balance lists only accounts with balances), as last read from their bookkeeping software or imported from a CSV, with the lead schedule line each would map to. Section C and journals offer these real nominal codes.

Parameters

Parameters for GET /api/v1/clients/{clientId}/chart-of-accounts
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

Responses

200 OK, application/json

Body: object (ChartOfAccounts).

The client’s chart of accounts, read from their bookkeeping software (on connect, on every TB pull, and on refresh) or imported from a CSV.

200 OK response for GET /api/v1/clients/{clientId}/chart-of-accounts
FieldTypeDescription
clientIdrequiredstring (uuid)
sourcerequiredstring (ChartOfAccountsSource)nullable

Null until a chart has been read or imported.

One of: xero, quickbooks, sage, freeagent, tolvi_books, csv

sourceLabelrequiredstringnullable

example "Xero"

syncedAtrequiredstring (date-time)nullable

When the chart was last read or imported.

canRefreshrequiredboolean

The client’s books are connected (or linked to Tolvi Books), so POST …/chart-of-accounts/refresh can read the chart again.

accountsrequiredarray of ChartAccount
accounts[].​coderequiredstring
accounts[].​namerequiredstring
accounts[].​typerequiredstring (ChartAccountType)nullable

One of: asset, liability, equity, revenue, expense

accounts[].​providerTyperequiredstringnullable

The software’s own type, e.g. Xero’s FIXED or DEPRECIATN, or the CSV’s Type column.

accounts[].​activerequiredboolean

False for archived or inactive accounts.

accounts[].​lineIdrequiredstring (LeadLineId)nullable

The lead schedule line the mapping order suggests (client memory, firm rules, built-in).

One of: C.cost, C.dep, D.stk, E.td, E.pre, F.bank, G.tc, G.acc, H.vat, I.paye, J.dla, J.loanst, J.loan, K.cap, K.res, K.div, L.sales, L.intrec, M.cos, M.adm, M.dis, M.dep, M.int, M.disp, T.chg, T.liab, T.dtc, P.dt, J.cap, J.draw

Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).

Import a client’s chart of accounts from a CSV

PUT/api/v1/clients/{clientId}/chart-of-accounts

Scopes
clients:write

For a client whose books are not connected: replaces the stored chart with the file’s accounts. The file needs Code and Name columns; Type and Status are read where present (Xero’s chart of accounts export works as it is). 422 with the reason when the file can’t be read.

Parameters

Parameters for PUT /api/v1/clients/{clientId}/chart-of-accounts
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

Request body

Required. application/json: object (ImportChartOfAccountsRequest).

Request body for PUT /api/v1/clients/{clientId}/chart-of-accounts
FieldTypeDescription
formatrequiredstring (ChartOfAccountsFormat)

One of: csv

contentrequiredstring

The CSV text: Code and Name columns, with Type and Status where the file has them (Xero’s chart of accounts export works as it is).

up to 2000000 characters

Responses

200 OK, application/json

Body: object (ChartOfAccounts).

The client’s chart of accounts, read from their bookkeeping software (on connect, on every TB pull, and on refresh) or imported from a CSV.

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 client’s chart of accounts again

POST/api/v1/clients/{clientId}/chart-of-accounts/refresh

Scopes
clients:write

Reads the chart from the client’s connected books and replaces the stored one. Tolvi also does this when the books are connected and on every trial balance pull. 409 when the books are not connected or the connection needs reconnecting.

Parameters

Parameters for POST /api/v1/clients/{clientId}/chart-of-accounts/refresh
NameInTypeDescription
clientIdrequiredpathstring (uuid)

Client id.

Responses

200 OK, application/json

Body: object (ChartOfAccounts).

The client’s chart of accounts, read from their bookkeeping software (on connect, on every TB pull, and on refresh) or imported from a CSV.

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).