Billing
21 operations in the Tolvi API, version 1. Paths are relative to https://api.tolvi.co.uk. Every amount is integer pence.
Get the firm’s billing account, plan and this period’s engagements
GET/api/v1/billing/account
- Scopes
settings:admin- Role
- Firm admin
Responses
200 OK, application/json
Body: object (BillingAccount).
| Field | Type | Description |
|---|---|---|
statusrequired | string | trialing, trial_ended, or the Stripe subscription status. |
stripeCustomerIdrequired | stringnullable | |
hasSubscriptionrequired | boolean | |
planIdrequired | stringnullable | |
planIntervalrequired | stringnullable | |
trialEndsAtrequired | string (date-time) | |
freeEngagementsrequired | integer | |
freeEngagementsUsedrequired | integer | |
dunningSincerequired | string (date-time)nullable | When an invoice first failed; starting engagements is blocked 14 days after. |
startBlockedrequired | boolean | |
startBlockedReasonrequired | stringnullable | |
billingEmailrequired | stringnullable | |
vatNumberrequired | stringnullable | |
planrequired | object (BillingPlan) | |
plan.versionrequired | string | |
plan.statusrequired | string (PriceTableStatus) | One of: |
plan.currencyrequired | string | |
plan.tiersrequired | array of PriceTier | |
plan.tiers[].upTorequired | integernullable | Inclusive upper bound (engagements per month); null for the last tier. |
plan.tiers[].unitPencerequired | integer | Pence per engagement, net of VAT. |
plan.trialDaysrequired | integer | |
plan.freeEngagementsrequired | integer | |
plan.cancellationDaysrequired | integer | |
plan.dunningBlockDaysrequired | integer | |
plan.example100required | integer | What 100 engagements in a month cost, in pence. |
periodrequired | object (BillingPeriod) | |
period.startedrequired | integer | |
period.creditedrequired | integer | |
period.freerequired | integer | |
period.billablerequired | integer | |
period.estimaterequired | object (BillingEstimate) | |
period.estimate.totalPencerequired | integer | Integer pence (GBP). |
period.estimate.linesrequired | array of TierLine | |
period.estimate.lines[].fromrequired | integer | |
period.estimate.lines[].torequired | integer | |
period.estimate.lines[].countrequired | integer | |
period.estimate.lines[].unitPencerequired | integer | Integer pence (GBP). |
period.estimate.lines[].amountPencerequired | integer | Integer pence (GBP). |
period.fromrequired | string (date-time) | |
period.torequired | string (date-time) | |
period.nextUnitPencerequired | integer | Integer pence (GBP). Price of the next engagement this month. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the per-engagement tiers the plans are priced from
GET/api/v1/billing/plan
- Scopes
settings:admin- Role
- Firm admin
Graduated monthly tiers, as data.
Responses
200 OK, application/json
Body: object (BillingPlan).
| Field | Type | Description |
|---|---|---|
versionrequired | string | |
statusrequired | string (PriceTableStatus) | One of: |
currencyrequired | string | |
tiersrequired | array of PriceTier | |
tiers[].upTorequired | integernullable | Inclusive upper bound (engagements per month); null for the last tier. |
tiers[].unitPencerequired | integer | Pence per engagement, net of VAT. |
trialDaysrequired | integer | |
freeEngagementsrequired | integer | |
cancellationDaysrequired | integer | |
dunningBlockDaysrequired | integer | |
example100required | integer | What 100 engagements in a month cost, in pence. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List the plans with monthly and annual prices, allowances, AI tokens, overage rates and entitlements
GET/api/v1/billing/plans
- Scopes
settings:admin- Role
- Firm admin
Responses
200 OK, application/json
Body: object (PlanCatalogue).
| Field | Type | Description |
|---|---|---|
versionrequired | string | |
statusrequired | string (PlanCatalogueStatus) | One of: |
currencyrequired | string | |
annualDiscountBpsrequired | integer | |
vatRegisteredrequired | boolean | Whether Tolvi charges VAT on these prices. False: no VAT is charged; true: VAT is added at checkout and on invoices. |
tiersrequired | array of PriceTier | |
tiers[].upTorequired | integernullable | Inclusive upper bound (engagements per month); null for the last tier. |
tiers[].unitPencerequired | integer | Pence per engagement, net of VAT. |
plansrequired | array of PlanOption | |
plans[].idrequired | string (PlanId) | One of: |
plans[].namerequired | string | |
plans[].customrequired | boolean | Priced per firm (Enterprise). |
plans[].engagementsPerYearrequired | integernullable | |
plans[].monthlyPencerequired | integernullable | Pence a month, billed monthly (list price), net of VAT. |
plans[].annualPencerequired | integernullable | Pence a year, billed annually upfront (15% off). |
plans[].aiTokensPerMonthrequired | integernullable | |
plans[].overageMonthlyPencerequired | integernullable | Integer pence (GBP). |
plans[].overageAnnualPencerequired | integernullable | Integer pence (GBP). |
plans[].entitlementsrequired | object (PlanEntitlements) | |
plans[].entitlements.ssorequired | boolean | |
plans[].entitlements.prioritySupportrequired | boolean | |
plans[].entitlements.assistedOnboardingrequired | boolean | |
plans[].entitlements.accountManagerrequired | boolean | |
plans[].entitlements.supportSlarequired | boolean | |
plans[].entitlements.multiOfficeReportingrequired | boolean | |
plans[].entitlements.apiRateLimitTierrequired | string (ApiRateLimitTier) | One of: |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the subscription: plan, interval, allowance used this plan year, AI tokens this month, next invoice, entitlements
GET/api/v1/billing/subscription
- Scopes
settings:admin- Role
- Firm admin
Responses
200 OK, application/json
Body: object (Subscription).
| Field | Type | Description |
|---|---|---|
statusrequired | string | trialing, no_plan, or the Stripe subscription status. |
planIdrequired | string (PlanId)nullable | One of: |
planNamerequired | stringnullable | |
intervalrequired | string (PlanInterval)nullable | One of: |
pricePence | integer | Integer pence (GBP). |
mrrPence | integer | Integer pence (GBP). |
currentPeriodStartsAt | string (date-time)nullable | |
currentPeriodEndsAt | string (date-time)nullable | |
trialrequired | object (TrialStatus) | |
trial.endsAtrequired | string (date-time) | |
trial.freeEngagementsrequired | integer | |
trial.freeEngagementsUsedrequired | integer | |
trial.activerequired | boolean | |
allowancerequired | object (PlanAllowance)nullable | |
allowance.planYearStartsAtrequired | string (date-time) | |
allowance.planYearEndsAtrequired | string (date-time) | |
allowance.totalrequired | integer | |
allowance.usedrequired | integer | |
allowance.remainingrequired | integer | |
allowance.overagerequired | integer | |
allowance.overagePencerequired | integer | Integer pence (GBP). |
allowance.overageRatePencerequired | integer | Integer pence (GBP). |
allowance.thresholdsReachedrequired | array of integer | 80 and/or 100 once reached. |
aiTokensrequired | object (PlanAiTokens) | |
aiTokens.monthrequired | string | example "2026-10" |
aiTokens.includedrequired | integer | Included with the plan this month (no rollover). |
aiTokens.usedrequired | integer | |
nextInvoicerequired | object (NextInvoice)nullable | |
nextInvoice.daterequired | string (date-time)nullable | |
nextInvoice.planPencerequired | integer | Integer pence (GBP). |
nextInvoice.overagePencerequired | integer | Integer pence (GBP). Overage not yet invoiced. |
entitlementsrequired | object (PlanEntitlements) | |
entitlements.ssorequired | boolean | |
entitlements.prioritySupportrequired | boolean | |
entitlements.assistedOnboardingrequired | boolean | |
entitlements.accountManagerrequired | boolean | |
entitlements.supportSlarequired | boolean | |
entitlements.multiOfficeReportingrequired | boolean | |
entitlements.apiRateLimitTierrequired | string (ApiRateLimitTier) | One of: |
cancelAtrequired | string (date-time)nullable | |
scheduledChangerequired | object (ScheduledPlanChange)nullable | |
scheduledChange.planIdrequired | string (PlanId) | One of: |
scheduledChange.intervalrequired | string (PlanInterval)nullable | One of: |
scheduledChange.effectiveAtrequired | string (date-time)nullable | |
pendingChange | object (PendingPlanChange)nullable | A plan chosen but not yet paid for: nothing is granted until Stripe reports the invoice paid. |
pendingChange.planIdrequired | string (PlanId) | One of: |
pendingChange.intervalrequired | string (PlanInterval)nullable | One of: |
pendingChange.paymentUrlrequired | stringnullable | Stripe’s hosted page for paying the invoice. |
recommendationrequired | string (PlanId)nullable | The plan Tolvi recommends (allowance used up, or overage reaching the next plan’s price). One of: |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Choose or change plan
POST/api/v1/billing/subscription
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
A first plan starts the subscription and plan year. Upgrades apply at once, prorated; downgrades are scheduled for renewal. Clears a pending cancellation.
Request body
Required. application/json: object (ChangePlan).
| Field | Type | Description |
|---|---|---|
planIdrequired | string (PlanId) | One of: |
intervalrequired | string (PlanInterval) | One of: |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Preview a plan change
POST/api/v1/billing/subscription/preview
- Scopes
settings:admin- Role
- Firm admin
Upgrades apply now, prorated; downgrades at renewal.
Request body
Required. application/json: object (ChangePlan).
| Field | Type | Description |
|---|---|---|
planIdrequired | string (PlanId) | One of: |
intervalrequired | string (PlanInterval) | One of: |
Responses
200 OK, application/json
Body: object (PlanChangePreview).
| Field | Type | Description |
|---|---|---|
kindrequired | string (PlanChangeKind) | One of: |
effectiverequired | string (PlanChangeEffective) | One of: |
effectiveAtrequired | string (date-time) | |
planIdrequired | string (PlanId) | One of: |
intervalrequired | string (PlanInterval) | One of: |
pricePencerequired | integer | Integer pence (GBP). |
prorationPencerequired | integer | Integer pence (GBP). Charged now: the prorated difference for an upgrade (or the first period for a new plan). |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the Meetings tiers, the firm’s Meetings plan and this month’s minutes
GET/api/v1/billing/meetings
- Scopes
settings:admin- Role
- Firm admin
Responses
200 OK, application/json
Body: object (MeetingsBilling).
The Meetings tiers, the firm’s Meetings plan and this month’s minutes.
| Field | Type | Description |
|---|---|---|
catalogueVersionrequired | string | |
vatRegisteredrequired | boolean | |
overagePencePerMinuterequired | integer | at least 0 |
trialMinutesrequired | integer | at least 0 |
plansrequired | array of MeetingsPlan | |
plans[].idrequired | string (MeetingsPlanId) | One of: |
plans[].namerequired | string | |
plans[].minutesPerMonthrequired | integer | at least 0 |
plans[].monthlyPencerequired | integer | Pence, excluding VAT. at least 0 |
plans[].annualPencerequired | integer | Pence, excluding VAT (15% off 12 months). at least 0 |
subscribedrequired | boolean | The firm has a Tolvi subscription that Meetings is (or can be) part of. If not, Meetings is bought on its own through Checkout. |
standalonerequired | boolean | Meetings is the firm’s only Tolvi subscription. |
annualDiscountBpsrequired | integer | Annual discount in basis points (1500 = 15%). at least 0 |
pendingCheckoutUrlrequired | string (uri)nullable | A Meetings Checkout started but not finished: send the browser here. |
intervalrequired | string (PlanInterval)nullable | Meetings bills at the subscription’s interval. One of: |
statusrequired | string (FirmProductStatus)nullable | Null: the firm doesn’t have Meetings. One of: |
planIdrequired | string (MeetingsPlanId)nullable | Null on a trial. One of: |
planNamerequired | stringnullable | |
scheduledChangerequired | object (MeetingsScheduledChange)nullable | |
scheduledChange.planIdrequired | string (MeetingsPlanId)nullable | Null: Meetings ends. One of: |
scheduledChange.effectiveAtrequired | string (date-time)nullable | The end of the current billing period. |
usagerequired | object (MeetingsUsageThisMonth) | |
usage.monthrequired | string | example "2026-10" |
usage.minutesUsedrequired | integer | at least 0 |
usage.allowancerequired | integer | at least 0 |
usage.remainingrequired | integer | at least 0 |
usage.overageMinutesrequired | integer | at least 0 |
usage.overagePencerequired | integer | Pence, excluding VAT; billed in arrears on a paid plan. at least 0 |
usage.thresholdsReachedrequired | array of number | |
recommendationrequired | string (MeetingsPlanId)nullable | The overage has reached the next tier’s price. One of: |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Choose, change or cancel the Meetings plan
PUT/api/v1/billing/meetings
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
With a Tolvi subscription, Meetings is added to it at its interval: a first plan and upgrades apply at once, prorated, once the invoice is paid; downgrades and cancelling (planId null) apply at the end of the billing period. Without one, Meetings is bought on its own through Stripe Checkout (interval month or year; see pendingCheckoutUrl) and starts once the first invoice is paid.
Request body
Required. application/json: object (ChangeMeetingsPlan).
| Field | Type | Description |
|---|---|---|
planIdrequired | string (MeetingsPlanId)nullable | Null cancels Meetings at the end of the billing period. One of: |
interval | string (PlanInterval) | Only when buying Meetings on its own (no Tolvi subscription yet). Default month. One of: |
Responses
200 OK, application/json
Body: object (MeetingsBilling).
The Meetings tiers, the firm’s Meetings plan and this month’s minutes.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Cancel the subscription
POST/api/v1/billing/subscription/cancel
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
Monthly plans: one month’s notice. Annual plans: at renewal (no refund).
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Keep a subscription that is set to cancel
POST/api/v1/billing/subscription/resume
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List billable engagements (started and credited) in a period (default: this month)
GET/api/v1/billing/usage
- Scopes
settings:admin- Role
- Firm admin
Parameters
| Name | In | Type | Description |
|---|---|---|---|
from | query | string (date) | |
to | query | string (date) |
Responses
200 OK, application/json
Body: object (BillingUsage).
| Field | Type | Description |
|---|---|---|
fromrequired | string (date-time) | |
torequired | string (date-time) | |
summaryrequired | object (BillingPeriodSummary) | |
summary.startedrequired | integer | |
summary.creditedrequired | integer | |
summary.freerequired | integer | |
summary.billablerequired | integer | |
summary.estimaterequired | object (BillingEstimate) | |
summary.estimate.totalPencerequired | integer | Integer pence (GBP). |
summary.estimate.linesrequired | array of TierLine | |
summary.estimate.lines[].fromrequired | integer | |
summary.estimate.lines[].torequired | integer | |
summary.estimate.lines[].countrequired | integer | |
summary.estimate.lines[].unitPencerequired | integer | Integer pence (GBP). |
summary.estimate.lines[].amountPencerequired | integer | Integer pence (GBP). |
eventsrequired | array of BillableEvent | |
events[].idrequired | string (uuid) | |
events[].kindrequired | string (BillableKind) | One of: |
events[].engagementIdrequired | string (uuid) | |
events[].clientIdrequired | string (uuid) | |
events[].clientNamerequired | string | |
events[].periodEndrequired | string (date) | example "2026-03-31" |
events[].sourcerequired | string (BillableSource) | One of: |
events[].startedByrequired | string (uuid)nullable | |
events[].freerequired | boolean | One of the trial’s free engagements. |
events[].atrequired | string (date-time) | |
events[].reportableAtrequired | string (date-time)nullable | When the cancellation window closes and it is reported to Stripe. |
events[].reportedAtrequired | string (date-time)nullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List invoices (Stripe-hosted)
GET/api/v1/billing/invoices
- Scopes
settings:admin- Role
- Firm admin
Responses
200 OK, application/json
Body: array of Invoice.
| Field | Type | Description |
|---|---|---|
[].idrequired | string (uuid) | |
[].stripeInvoiceIdrequired | string | |
[].numberrequired | stringnullable | |
[].statusrequired | string | |
[].amountDuePencerequired | integer | Integer pence (GBP). |
[].amountPaidPencerequired | integer | Integer pence (GBP). |
[].currencyrequired | string | |
[].hostedInvoiceUrlrequired | stringnullable | |
[].invoicePdfrequired | stringnullable | |
[].periodStartrequired | string (date-time)nullable | |
[].periodEndrequired | string (date-time)nullable | |
[].createdAtrequired | string (date-time) |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Set billing details (contact, VAT number) and create the Stripe customer
POST/api/v1/billing/setup
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
Choose a plan with POST /billing/subscription; add a payment method in the Customer Portal.
Request body
Required. application/json: object (BillingSetup).
| Field | Type | Description |
|---|---|---|
billingEmail | string (email) | |
vatNumber | string | example "GB123456789" |
Responses
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Open the Stripe Customer Portal (payment method, billing details, invoices)
POST/api/v1/billing/portal
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
Responses
200 OK, application/json
Body: object (PortalSession).
| Field | Type | Description |
|---|---|---|
urlrequired | string (uri) |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Check whether new engagements can start
GET/api/v1/billing/start-status
- Scopes
engagements:write
The same gate as starting (402 when blocked): trial’s free engagements used or the trial over without a plan, or an invoice unpaid for 14 days. Viewing and exports are never blocked.
Responses
200 OK, application/json
Body: object (EngagementStartStatus).
Whether the firm can start engagements now. Readable by anyone who starts engagements; no prices, plan or invoices.
| Field | Type | Description |
|---|---|---|
canStartrequired | boolean | False when starting an engagement would fail with 402. |
reasonrequired | string (StartBlockReason)nullable | Why starting is blocked: an invoice unpaid for 14 days, the trial’s free engagements used, or the trial over without a plan. Null when it isn’t. One of: |
messagerequired | stringnullable | The same message a blocked start returns with its 402. |
startsLeftrequired | integernullable | How many more engagements can start before the gate closes (the trial’s free ones left); null when there is no limit. at least 0 |
allowance | object (StartAllowance)nullable | The plan year’s engagement allowance, as counts: how many have been used and how many the plan includes. Null on the trial, an Enterprise plan or no plan. Going over never stops work. |
allowance.usedrequired | integer | at least 0 |
allowance.totalrequired | integer | at least 0 |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Get the business’s Tolvi Books plan and the tiers
GET/api/v1/books/billing
- Scopes
books:admin
How Tolvi Books is paid for (a trial, the business, or its accountant), the tier the business needs, the tiers with monthly and annual prices, and this month’s AI fair use. Owners and admins.
Responses
200 OK, application/json
Body: object (BooksBilling).
A business’s Tolvi Books plan, how it is paid for, and the tiers.
| Field | Type | Description |
|---|---|---|
catalogueVersionrequired | string | |
priceStatusrequired | string (BooksPriceStatus) | placeholder: prices still to be confirmed. One of: |
vatRegisteredrequired | boolean | Tolvi charges VAT on top of these prices. |
annualDiscountBpsrequired | integer | at least 0 |
trialDaysrequired | integer | at least 0 |
plansrequired | array of BooksPlan | |
plans[].idrequired | string (BooksPlanId) | One of: |
plans[].namerequired | string | |
plans[].whorequired | string | Who the plan is for, in plain English. |
plans[].monthlyPencerequired | integer | Integer pence (GBP). at least 0 |
plans[].annualPencerequired | integer | Integer pence (GBP). 12 × monthly less the annual discount, in whole pounds. at least 0 |
plans[].wholesaleMonthlyPencerequired | integer | Integer pence (GBP). What a practice pays per client a month. at least 0 |
plans[].aiFairUsePerMonthrequired | integer | AI suggestions a month within fair use. Past it, nothing stops and nothing is charged. at least 0 |
plans[].vatReturnsrequired | boolean | MTD VAT returns are included. |
plans[].includesrequired | array of string | |
requiredPlanIdrequired | string (BooksPlanId) | The tier this business needs (from its type and VAT registration); it may choose a higher one. One of: |
statusrequired | string (FirmProductStatus) | suspended: read-only (an ended trial, or no plan). Reading and exporting are never blocked. One of: |
paidByrequired | string (BooksPaidBy) | One of: |
practicerequired | object (BooksPayingPractice)nullable | |
practice.linkIdrequired | string (uuid) | |
practice.firmNamerequired | string | |
planIdrequired | string (BooksPlanId)nullable | One of: |
planNamerequired | stringnullable | |
intervalrequired | string (PlanInterval)nullable | One of: |
trialEndsAtrequired | string (date-time)nullable | |
currentPeriodEndsAtrequired | string (date-time)nullable | |
cancelAtrequired | string (date-time)nullable | |
scheduledChangerequired | object (BooksScheduledChange)nullable | |
scheduledChange.planIdrequired | string (BooksPlanId)nullable | Null: Tolvi Books ends. One of: |
scheduledChange.intervalrequired | string (PlanInterval)nullable | Moves to this billing period; null keeps the current one. One of: |
scheduledChange.effectiveAtrequired | string (date-time)nullable | |
pendingCheckoutUrlrequired | string (uri)nullable | A Checkout started but not finished: send the browser here. |
fairUserequired | object (BooksFairUse) | AI suggestions this calendar month (UTC) against the fair-use allowance. |
fairUse.monthrequired | string | example "2026-10" |
fairUse.allowancerequired | integer | at least 0 |
fairUse.usedrequired | integer | at least 0 |
fairUse.remainingrequired | integer | at least 0 |
fairUse.overrequired | boolean | |
endedAtrequired | string (date-time)nullable | When the plan or trial ended. Set while read-only; cleared on choosing a plan. |
dataDeletesOnrequired | string (date-time)nullable | When the books are due to be deleted: 6 years after endedAt. |
dataDeletedAtrequired | string (date-time)nullable | When the books were deleted, 6 years after the plan ended. |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Choose, change or cancel the business’s Tolvi Books plan
PUT/api/v1/books/billing/plan
- Scopes
books:admin- Access
- A signed-in Tolvi session only, never an API token.
Without a plan: Stripe Checkout (interval month or year; see pendingCheckoutUrl), and Tolvi Books is paid for once the first invoice is. With one: upgrades apply at once, prorated, once paid; downgrades and cancelling (planId null) at the end of the billing period. The current plan with another interval switches how it is billed: to year at once, prorated, once paid; to month at the end of the billing period. The tier must be at least requiredPlanId. Refused (409) while the business’s accountant pays. Signed-in owners and admins only.
Request body
Required. application/json: object (ChangeBooksPlan).
| Field | Type | Description |
|---|---|---|
planIdrequired | string (BooksPlanId)nullable | Null cancels at the end of the billing period. One of: |
interval | string (PlanInterval) | Buying a first plan (default month), or moving the current plan: to year applies now, prorated; to month at the end of the billing period. One of: |
Responses
200 OK, application/json
Body: object (BooksBilling).
A business’s Tolvi Books plan, how it is paid for, and the tiers.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Open the Stripe Customer Portal for the business
POST/api/v1/books/billing/portal
- Scopes
books:admin- Access
- A signed-in Tolvi session only, never an API token.
Payment method, billing details and invoices. Signed-in owners and admins only.
Responses
200 OK, application/json
Body: object (BooksPortalSession).
| Field | Type | Description |
|---|---|---|
urlrequired | string (uri) |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
List the firm’s Tolvi Books clients and whose Books it pays for
GET/api/v1/practice/books/billing
- Scopes
settings:admin- Role
- Firm admin
Every business with a live link, the tier it needs, the wholesale price and whether the firm pays.
Responses
200 OK, application/json
Body: object (PracticeBooksBilling).
The firm’s linked clients and whose Tolvi Books it pays for, at the wholesale price.
| Field | Type | Description |
|---|---|---|
catalogueVersionrequired | string | |
priceStatusrequired | string (BooksPriceStatus) | placeholder: prices still to be confirmed. One of: |
vatRegisteredrequired | boolean | |
consentVersionrequired | string | |
plansrequired | array of PracticeBooksPlan | |
plans[].idrequired | string (BooksPlanId) | One of: |
plans[].namerequired | string | |
plans[].whorequired | string | |
plans[].wholesaleMonthlyPencerequired | integer | Integer pence (GBP). at least 0 |
subscribedrequired | boolean | The firm has a Tolvi subscription to add clients to. Without one, paying for a client starts a Stripe Checkout for Tolvi Books alone. |
pendingCheckoutUrlrequired | string (uri)nullable | A Checkout for clients’ Tolvi Books started but not finished: send the browser here. |
monthlyTotalPencerequired | integer | Integer pence (GBP). at least 0 |
clientsrequired | array of PracticeBooksClient | |
clients[].linkIdrequired | string (uuid) | |
clients[].businessIdrequired | string (uuid) | |
clients[].businessNamerequired | string | |
clients[].clientPrefersrequired | string (LinkBilledBy) | Who the business said should pay, on its link. One of: |
clients[].planIdrequired | string (BooksPlanId)nullable | The tier the business needs. One of: |
clients[].planNamerequired | stringnullable | |
clients[].wholesaleMonthlyPencerequired | integernullable | Integer pence (GBP). at least 0 |
clients[].booksStatusrequired | string (FirmProductStatus)nullable | Null: the firm doesn’t have Meetings. One of: |
clients[].trialEndsAtrequired | string (date-time)nullable | |
clients[].paysItselfrequired | boolean | The business pays for its own plan. |
clients[].consentTextrequired | stringnullable | What a firm admin agrees to before paying for this client. |
clients[].billingrequired | object (PracticeBooksClientBilling)nullable | |
clients[].billing.statusrequired | string (PracticeBooksBillingStatus) | pending: waiting for the prorated invoice to be paid. One of: |
clients[].billing.sincerequired | string (date-time) | |
clients[].billing.paymentUrlrequired | string (uri)nullable |
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 429 Too many requests, as problem details (application/problem+json).
Pay for a client’s Tolvi Books
POST/api/v1/practice/books/billing/clients/{linkId}
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
- Retries
- Send an
Idempotency-Keyto make retries safe.
Adds the client to the firm’s Tolvi invoice at the wholesale price for the tier it needs, prorated, from today (agree: true records the firm admin’s agreement to the client’s consentText). Paid for once that invoice is paid. A firm with no Tolvi subscription gets a Stripe Checkout for Tolvi Books alone (see pendingCheckoutUrl); the client starts once its first invoice is paid. Needs a live link; refused (409) if the business already pays for itself, or while another Checkout is unfinished.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
linkIdrequired | 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 (PayForClientBooks).
| Field | Type | Description |
|---|---|---|
agreerequired | boolean | Must be true: the firm admin agrees to the client’s consentText. |
Responses
200 OK, application/json
Body: object (PracticeBooksBilling).
The firm’s linked clients and whose Tolvi Books it pays for, at the wholesale price.
The fields are as shown above.
Errors: 400 Bad request, 401 Unauthorised, 403 Forbidden, 404 Not found, 409 Conflict, 422 Unprocessable content, 429 Too many requests, as problem details (application/problem+json).
Stop paying for a client’s Tolvi Books
POST/api/v1/practice/books/billing/clients/{linkId}/stop
- Scopes
settings:admin- Role
- Firm admin
- Access
- A signed-in Tolvi session only, never an API token.
Takes the client off the firm’s invoice now (credited, prorated). The business keeps its books and gets a grace period before it must choose a plan of its own.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
linkIdrequired | path | string (uuid) |
Request body
Optional. application/json: object (StopPayingForClientBooks).
| Field | Type | Description |
|---|---|---|
reason | string | up to 500 characters |
Responses
200 OK, application/json
Body: object (PracticeBooksBilling).
The firm’s linked clients and whose Tolvi Books it pays for, at the wholesale price.
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).