Work
8 operations in the Tolvi API, version 1. Paths are relative to https://api.tolvi.co.uk. Every amount is integer pence.
List the work queue
GET/api/v1/work
- Scopes
work:read
Saved views are relative to the caller ("mine" = engagements where you act next). Needs a person, not an API client. Part of the working_papers product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer | 1 to 100; default 50 |
cursor | query | string | |
q | query | string | Part of the client name. up to 100 characters |
partner | query | string (uuid) | |
manager | query | string (uuid) | |
actor | query | string (uuid) | Whose turn it is: the person assigned to the current stage. |
entityType | query | string (EntityType) | One of: |
yearEndMonth | query | integer | 1 to 12 |
stage | query | string (EngagementStatus) | One of: |
software | query | string (Software) | One of: |
dueFrom | query | string (date) | Deadline on or after this date. |
dueTo | query | string (date) | Deadline on or before this date. |
scope | query | string (WorkScope) | mine: engagements where you are preparer, reviewer, manager or partner. Default everyone. One of: |
sort | query | string (WorkSort) | Default: due. One of: |
view | query | string (WorkView) | Default: mine. todo/later: your turn, due within 120 days / after; stuck: in review over 14 days; at_risk: not yet overdue but likely to miss the deadline at its current stage (Due within 45 days and not started, 30 days preparing, 21 days returned or 14 days in review.); unassigned: open, no preparer (use scope=everyone); changed: open work only. One of: |
Responses
200 OK, application/json
Body: object (WorkPage).
| Field | Type | Description |
|---|---|---|
rowsrequired | array of WorkRow | |
rows[].idrequired | string (uuid) | Engagement id. |
rows[].clientIdrequired | string (uuid) | |
rows[].clientNamerequired | string | |
rows[].entityTyperequired | string (EntityType) | One of: |
rows[].periodStartrequired | string (date) | example "2026-03-31" |
rows[].periodEndrequired | string (date) | example "2026-03-31" |
rows[].dueDaterequired | string (date) | Filing deadline. example "2026-03-31" |
rows[].daysLeftrequired | integer | Days from today (Europe/London) to the deadline; negative when overdue. |
rows[].provisionalrequired | boolean | The deadline is for a tax year and the return may need provisional figures. |
rows[].statusrequired | string (EngagementStatus) | One of: |
rows[].stageIndexrequired | integer | at least 0 |
rows[].stageNamerequired | string | |
rows[].stageCountrequired | integer | at least 1 |
rows[].workflowNamerequired | string | |
rows[].currentActorIdrequired | string (uuid)nullable | Who acts next. |
rows[].preparerIdrequired | string (uuid)nullable | |
rows[].reviewerIdrequired | string (uuid)nullable | |
rows[].managerIdrequired | string (uuid)nullable | |
rows[].partnerIdrequired | string (uuid)nullable | |
rows[].roundsrequired | integer | Review rounds (returns to the preparer). at least 0 |
rows[].openPointsrequired | integer | Open review points. at least 0 |
rows[].syncStaterequired | string (SyncState) | One of: |
rows[].softwarerequired | string (Software) | One of: |
rows[].lastSyncedAtrequired | string (date-time)nullable | |
rows[].riskrequired | string (RiskLevel) | One of: |
rows[].filedOnrequired | string (date)nullable | When the accounts were filed (recorded by a person). example "2026-03-31" |
nextCursorrequired | stringnullable | Opaque cursor for the next page (pass as |
pageSizerequired | integer | The page size that was used. 1 to 100 |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Count each saved view, with the KPI cards
GET/api/v1/work/counts
- Scopes
work:read
Part of the working_papers product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
q | query | string | Part of the client name. up to 100 characters |
partner | query | string (uuid) | |
manager | query | string (uuid) | |
actor | query | string (uuid) | Whose turn it is: the person assigned to the current stage. |
entityType | query | string (EntityType) | One of: |
yearEndMonth | query | integer | 1 to 12 |
stage | query | string (EngagementStatus) | One of: |
software | query | string (Software) | One of: |
dueFrom | query | string (date) | Deadline on or after this date. |
dueTo | query | string (date) | Deadline on or before this date. |
scope | query | string (WorkScope) | mine: engagements where you are preparer, reviewer, manager or partner. Default everyone. One of: |
Responses
200 OK, application/json
Body: object (WorkCounts).
| Field | Type | Description |
|---|---|---|
viewsrequired | object (WorkViewCounts) | Engagements in each saved view. |
views.minerequired | integer | |
views.reviewrequired | integer | |
views.returnedrequired | integer | |
views.due30required | integer | |
views.overduerequired | integer | |
views.changedrequired | integer | |
views.openrequired | integer | |
views.allrequired | integer | |
views.todorequired | integer | Your turn, due within 120 days. |
views.laterrequired | integer | Your turn, due in more than 120 days. |
views.stuckrequired | integer | In review for more than 14 days. |
views.at_riskrequired | integer | At risk: not yet overdue, but likely to miss the deadline at its current stage. Due within 45 days and not started, 30 days preparing, 21 days returned or 14 days in review. |
views.unassignedrequired | integer | Open engagements with no preparer (nobody’s To do until assigned). |
kpisrequired | object (WorkKpis) | |
kpis.due30required | integer | |
kpis.overduerequired | integer | |
kpis.awaitingReviewrequired | integer | |
kpis.awaitingMyReviewrequired | integer | |
kpis.changedrequired | integer | |
kpis.avgRoundsrequired | number | Average review rounds on complete engagements, to 2 dp. |
kpis.firstTimeRightPctrequired | integer | 0 to 100 |
totalrequired | integer | All engagements in the firm, ignoring filters. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Count a view grouped by status, deadline day or whose turn it is
GET/api/v1/work/groups
- Scopes
work:read
For the Board, Calendar and Workload. Same view, scope and filters as the list. Part of the working_papers product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
q | query | string | Part of the client name. up to 100 characters |
partner | query | string (uuid) | |
manager | query | string (uuid) | |
actor | query | string (uuid) | Whose turn it is: the person assigned to the current stage. |
entityType | query | string (EntityType) | One of: |
yearEndMonth | query | integer | 1 to 12 |
stage | query | string (EngagementStatus) | One of: |
software | query | string (Software) | One of: |
dueFrom | query | string (date) | Deadline on or after this date. |
dueTo | query | string (date) | Deadline on or before this date. |
scope | query | string (WorkScope) | mine: engagements where you are preparer, reviewer, manager or partner. Default everyone. One of: |
view | query | string (WorkView) | One of: |
groupByrequired | query | string (WorkGroupBy) | One of: |
Responses
200 OK, application/json
Body: object (WorkGroups).
| Field | Type | Description |
|---|---|---|
groupByrequired | string (WorkGroupBy) | One of: |
groupsrequired | array of WorkGroup | |
groups[].keyrequired | stringnullable | A status, a deadline (YYYY-MM-DD) or a person id (null: nobody). |
groups[].countrequired | integer | |
totalrequired | integer |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Start engagements (one or in bulk)
POST/api/v1/work/start
- Scopes
engagements:writeandwork:read- Role
- Preparer or above
- Retries
- Send an
Idempotency-Keyto make retries safe.
Each start is billable (engagement.started, source web/api/mcp, or bulk for several). AI tokens come from the plan’s monthly allowance, not from each start. 402 when billing blocks starting (trial over without billing, or an invoice unpaid for 14 days).
Needs the Preparer role or above. Part of the working_papers 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 (StartEngagements).
| Field | Type | Description |
|---|---|---|
engagementIdsrequired | array of string (uuid) | 1 to 500 items |
workflowId | string | Workflow template id; default: the client’s, else the firm default for the entity type. |
preparerId | string (uuid) | |
reviewerId | string (uuid) | |
managerId | string (uuid) | |
partnerId | string (uuid) | |
pullTrialBalance | boolean | Pull each started engagement’s TB from its client’s bookkeeping connection (reported back in tbPulls). |
bringForward | object (BringForward) | What to carry over from the client’s previous engagement (the year-end roll forward). Each defaults to true. |
bringForward.priorYearFigures | boolean | Last year’s final TB (draft + posted journals) as this year’s comparatives, kept when this year’s TB is imported. |
bringForward.mappings | boolean | Last year’s account mappings, applied when the trial balance is imported. |
bringForward.fixedAssets | boolean | The fixed asset register: closing cost and depreciation as opening balances, disposed assets left out. |
bringForward.taxBroughtForward | boolean | Main and special rate pool WDVs and trading losses carried forward. |
bringForward.notes | boolean | Last year’s variance notes, as hints on the lead schedules (never as this year’s explanations). |
bringForward.clientSteps | boolean | The client’s own checklist steps, added to the firm’s checklists. |
Responses
200 OK, application/json
Body: object (StartResult).
| Field | Type | Description |
|---|---|---|
startedrequired | integer | |
alreadyStartedrequired | array of string (uuid) | |
notFoundrequired | array of string (uuid) | |
blockedrequired | stringnullable | Why the rest of a bulk start stopped (billing). |
trialBalancePulledrequired | boolean | pullTrialBalance was asked for and every started engagement with a connection was pulled (or queued) without failure. |
tbPullsrequired | array of TbPullOutcome | Per started engagement, when pullTrialBalance was asked for. |
tbPulls[].engagementIdrequired | string (uuid) | |
tbPulls[].outcomerequired | string (TbPullOutcomeKind) | pulled: a new TB version from the books; queued: being pulled in the background (large batches); not_connected: no usable connection; failed: see message. One of: |
tbPulls[].messagerequired | stringnullable | |
broughtForwardrequired | object (BroughtForward) | What was carried over, totalled across the engagements started. |
broughtForward.mappingsrequired | integer | |
broughtForward.notesrequired | integer | Variance notes brought forward as hints. |
broughtForward.clientStepsrequired | integer | |
broughtForward.priorYearAccountsrequired | integer | Accounts in last year’s final TB brought forward as comparatives. |
broughtForward.fixedAssetsrequired | integer | Fixed assets brought forward with their closing balances. |
broughtForward.taxBroughtForwardrequired | integer | Engagements whose pools and losses carried forward were brought forward. |
notifiedrequired | integer | Team members told in-app that they were added (the person starting is never told). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
Record the dates accounts were filed
POST/api/v1/work/filed
- Scopes
engagements:writeandwork:read
Part of the working_papers product: the firm must have it and you must have access to it.
Request body
Required. application/json: object (MarkFiled).
| Field | Type | Description |
|---|---|---|
filedrequired | array of FiledEntry | 1 to 500 items |
filed[].engagementIdrequired | string (uuid) | |
filed[].filedOnrequired | string (date) | example "2026-03-31" |
Responses
200 OK, application/json
Body: object (MarkFiledResult).
| Field | Type | Description |
|---|---|---|
updatedrequired | integer |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List the firm’s people, for filters and assignment
GET/api/v1/work/people
- Scopes
work:read
Part of the working_papers product: the firm must have it and you must have access to it.
Responses
200 OK, application/json
Body: array of Person.
| Field | Type | Description |
|---|---|---|
[].idrequired | string (uuid) | |
[].namerequired | string | |
[].initialsrequired | string | example "SP" |
[].rolerequired | string (StaffRole)nullable | One of: |
[].openInRole | integernullable | Open engagements this person holds in their own role (as preparer, reviewer, manager or partner). Null for practice admin staff. at least 0 |
[].capacity | integernullable | Their role’s capacity (open jobs at once, from the firm’s settings); null for no limit. at least 0 |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Assign engagements in bulk
POST/api/v1/work/assign
- Scopes
engagements:writeandwork:read- Role
- Manager or above
- Retries
- Send an
Idempotency-Keyto make retries safe.
All or nothing: if any engagement id is not found in this firm, nothing is changed and the response is a 404 problem listing those ids.
Needs the Manager role or above. Part of the working_papers 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 (AssignRequest).
Give at least one of preparerId, reviewerId, managerId or partnerId.
| Field | Type | Description |
|---|---|---|
engagementIdsrequired | array of string (uuid) | 1 to 500 items |
preparerId | string (uuid) | |
reviewerId | string (uuid) | |
managerId | string (uuid) | |
partnerId | string (uuid) |
Responses
200 OK, application/json
Body: object (AssignResult).
| Field | Type | Description |
|---|---|---|
updatedrequired | integer | Engagements updated (ids from other firms are ignored). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
Get one engagement with its workflow timeline
GET/api/v1/work/{id}
- Scopes
work:read
Part of the working_papers product: the firm must have it and you must have access to it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
idrequired | path | string (uuid) | Engagement id. |
Responses
200 OK, application/json
Body: object (WorkDetail).
| Field | Type | Description |
|---|---|---|
idrequired | string (uuid) | Engagement id. |
clientIdrequired | string (uuid) | |
clientNamerequired | string | |
entityTyperequired | string (EntityType) | One of: |
periodStartrequired | string (date) | example "2026-03-31" |
periodEndrequired | string (date) | example "2026-03-31" |
dueDaterequired | string (date) | Filing deadline. example "2026-03-31" |
daysLeftrequired | integer | Days from today (Europe/London) to the deadline; negative when overdue. |
provisionalrequired | boolean | The deadline is for a tax year and the return may need provisional figures. |
statusrequired | string (EngagementStatus) | One of: |
stageIndexrequired | integer | at least 0 |
stageNamerequired | string | |
stageCountrequired | integer | at least 1 |
workflowNamerequired | string | |
currentActorIdrequired | string (uuid)nullable | Who acts next. |
preparerIdrequired | string (uuid)nullable | |
reviewerIdrequired | string (uuid)nullable | |
managerIdrequired | string (uuid)nullable | |
partnerIdrequired | string (uuid)nullable | |
roundsrequired | integer | Review rounds (returns to the preparer). at least 0 |
openPointsrequired | integer | Open review points. at least 0 |
syncStaterequired | string (SyncState) | One of: |
softwarerequired | string (Software) | One of: |
lastSyncedAtrequired | string (date-time)nullable | |
riskrequired | string (RiskLevel) | One of: |
filedOnrequired | string (date)nullable | When the accounts were filed (recorded by a person). example "2026-03-31" |
workflowTemplateIdrequired | string | |
timelinerequired | array of WorkTimelineStep | |
timeline[].namerequired | string | |
timeline[].rolerequired | string (StaffRole) | One of: |
timeline[].assigneeIdrequired | string (uuid)nullable | |
timeline[].staterequired | string (TimelineStepState) | One of: |
timeline[].mustDifferrequired | boolean | The assignee must differ from the previous stage’s. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 429 Too many requests, as problem details (application/problem+json).