Subscriptions

The running jobjob_7Hq2 · accepted · document.fr-CA.pdf · $49.00 final

price.every makes an order a subscription, sbn_, charged once per cycle, and price.includes says how many jobs of each Service the cycle covers. Past an allowance, the next job is offered at its own price. Renewal charges again and resets every allowance, an update takes effect at the next mint, and a cancel runs to the end of the cycle paid for.

A subscription answers one money question: what does a buyer pay for work that keeps coming? One charge a cycle, a count of jobs it covers, and each job past the count at its own price. What orders the jobs, on, is Schedules and triggers.

Lingua's Translation Plan keeps a buyer's site translated for $149 a month, with four translations included:

Run
grep price services/plan.ts
magic values: none
  price: { usd: 149, every: 'month', includes: { translation: 4 } },

check reads the cycle back, with what it leaves you:

Run
npx services.dev check
magic values: none
services/plan.ts · 7 lines · $149 per month, charged at the start of each month, 4 translation included each month.
  ledger  +$149.00 · AI $0.84 (example) · you $148.16
  margin  at least $119.20 a job (80%) · a job that stops costs you at most $29.80

The order opens sbn_7Hq2 and charges the first cycle at once:

Call
→ POST /plan {"site":"acme.example","document":"lease.pdf","to":"fr-CA","max_usd":149,"key":"plan"}
magic values: none
← sbn_7Hq2 · plan · active · cycle 1 · 2026-10-25 → 2026-11-25 · $149.00 per month
  allowances  translation 0 of 4
  schedule    monthly → translation · next 2026-11-01
  event       page.changed → translation
Call
→ GET /events?type=subscription.created
magic values: none
  2026-10-25  sbn_7Hq2  created · cycle 1 · $149.00 charged · ends 2026-11-25

Each job inside the four charges $0 and counts against the cycle, so its ledger carries only what it cost you: the AI here, and any person's pay:

Call
→ POST /events {"type":"page.changed","id":"page-1","data":{"site":"acme.example","document":"page-1.html"}}
200 · subscription.minted · event page.changed → job_7Hq2 translation · $0.00 · magic values: none
→ POST /events {"type":"page.changed","id":"page-2","data":{"site":"acme.example","document":"page-2.html"}}
← 200 · subscription.minted · event page.changed → job_7Hq3 translation · $0.00
→ POST /events {"type":"page.changed","id":"page-3","data":{"site":"acme.example","document":"page-3.html"}}
← 200 · subscription.minted · event page.changed → job_7Hq4 translation · $0.00
→ POST /events {"type":"page.changed","id":"page-4","data":{"site":"acme.example","document":"page-4.html"}}
← 200 · subscription.minted · event page.changed → job_7Hv5 translation · $0.00

→ GET /owner/jobs/job_7Hq2
← job_7Hq2 · delivered · page-1.fr-CA.html · $0.00 held
  ledger  +$0.00 · AI $0.84 · you −$0.84

A fifth order answers 402 OFFER at Translation's own $49 and charges nothing until the buyer sends its next. The extra job then holds $49, from the plan, with its own ledger:

Call
→ POST /translation {"document":"page-5.html","to":"fr-CA","subscription":"sbn_7Hq2"}
magic values: none
← 402 OFFER · $49 per job, within 48 hours, charged on delivery.
  next    order translation {"document":"page-5.html","to":"fr-CA","max_usd":49,"key":"order-f402daf0","subscription":"sbn_7Hq2"}
→ POST /translation {"document":"page-5.html","to":"fr-CA","max_usd":49,"key":"order-f402daf0","subscription":"sbn_7Hq2"}
← job_7Hq6 · working · $49.00 held · from sbn_7Hq2
→ GET /owner/jobs/job_7Hq6
← job_7Hq6 · delivered · page-5.fr-CA.html · $49.00 charged
  ledger  +$49.00 · AI $0.84 · you $48.16

A month earns you $149, less its included jobs' costs, plus each extra job's ledger. check's margin covers the plan's charge, not the jobs it includes, so price each allowance against its Service's ledger: four Translations spend at most four AI caps, $39.20.

At the cycle's end the plan charges $149 again and resets every allowance. Each cycle counts from the first, so it never drifts: a monthly plan ordered 31 January renews 28 February, then 31 March. A date that falls past the count before then is offered too, never charged on its own:

Call
→ POST /test_clocks/default/advance {"to":"2026-11-26T00:00:00Z"}
magic values: none
  2026-11-01  sbn_7Hq2  allowance translation past 4 of 4 · the extra job offered at $49.00
  2026-11-01  sbn_7Hq2  schedule monthly → translation offered at $49.00
  2026-11-25  sbn_7Hq2  renews · cycle 2 · $149.00 charged · allowances reset · ends 2026-12-25
Call
→ GET /subscriptions/sbn_7Hr3   # another buyer's plan: the renewal's card was declined
magic values: none
← sbn_7Hr3 · plan · past_due · cycle 2 · 2026-11-25 → 2026-12-25 · $149.00 per month
  renewal     25 Nov charge.failed, card expired · ask_7Hr3 to update the card · Stripe retries 27 Nov, 1 Dec and 5 Dec
  allowances  translation 1 of 4 · still usable while past_due
  then        9 Dec, if still unpaid: unpaid · allowances frozen · new orders answer 402 with the balance
StatusAllowancesNew orders
past_due, while Stripe retries and an ask asks for a new cardusablepriced as usual
unpaid, once Brand.dunning runs outfrozen402, with the balance and the exact next call
active, once a retry collectsas they werepriced as usual
a schedule's date, while past_due or unpaidits job mints, heldruns once a retry collects: no date skipped, none worked unpaid

PATCH /subscriptions/:id, subscriptions update, update_subscription, subscriptions.update():

An update
changesthe subscription's input: a new state, a new officer, a new books link
takes effectat the next mint, recording subscription.updated
leavesjobs already minted on their input, and the cycle's charge unchanged
Run
npx services.dev subscriptions update sbn_7Hq2 jurisdictions=US,US-CA,US-NY,US-NJ,US-TX --test-clock tc_7Hq4
magic values: owner.email = signs@example.test, metadata.sandbox.outside = {"reject":{"file.US-NY":["ein_mismatch"]}}
sbn_7Hq2 · tax-plan · active · updated 2027-03-08 · jurisdictions + US-TX
  next mint   annual 2028-02-01 · period 2027 · US, US-CA, US-NY, US-NJ, US-TX · Texas due 05-15, its own extension on 05-164
  keeps       job_4Tx3 · period 2026 · US, US-CA, US-NY, US-NJ, as minted 2027-02-01

A cancel stops the renewal, not the cycle the buyer paid for. The subscription reads canceling until the cycle ends, then canceled, and nothing after that mints or charges:

npx services.dev subscriptions cancel sbn_7Hq2
sbn_7Hq2 · plan · canceling · cycle 1 · 2026-10-25 → 2026-11-25 · $149.00 per month · ends 2026-11-25
  allowances  translation 0 of 4
  schedule    monthly → translation · next 2026-11-01
  event       page.changed → translation
curl -X POST http://localhost:8787/subscriptions/sbn_7Hq2/cancel
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "canceling",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,
→ tools/call cancel_subscription {"id":"sbn_7Hq2"}
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "canceling",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,
await lingua.subscriptions.cancel("sbn_7Hq2")
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "canceling",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,

On the clock, the date inside the paid cycle still mints at $0, and the next one never comes:

Call
→ POST /test_clocks/default/advance {"to":"2027-01-02T00:00:00Z"}
magic values: none
  2026-12-01  sbn_7Hq2  schedule monthly → job_7Hq7 translation · $0.00 · allowance 1 of 4

→ GET /subscriptions/sbn_7Hq2
← sbn_7Hq2 · plan · canceled · cycle 2 · 2026-11-25 → 2026-12-25 · $149.00 per month · ends 2026-12-25
  allowances  translation 1 of 4
  schedule    monthly → translation · next none

→ POST /events {"type":"page.changed","id":"page-9","data":{"site":"acme.example","document":"page-9.html"}}
← 422 input_invalid: page.changed isn't an event of the catalog. No subscription listens for it, and to raise a job's inbound signal, name the job.

Each cycle's money arrives on the feed, for the buyer and for you:

EventThe buyer's card
subscription.createdThe first cycle charged, with when it ends
allowance.used$0 on the job, counted against the cycle
allowance.exceededNothing yet: the extra job is offered at its price, 402 OFFER, with the exact next call
subscription.mintedA date or an event: the job minted, the job offered, or the refusal recorded
subscription.renewedCharged again; every allowance reset
charge.failedThe renewal declined: update the card; the plan reads past_due
subscription.unpaidStill declined when dunning ran out: allowances frozen until it's paid
subscription.updatedThe new input, and the next mint that takes it
subscription.canceledNothing more: it runs to the end of the cycle paid for
KeyWrittenLeft outDoes
price.every'day', 'week', 'month', 'quarter' or 'year'one jobAn order is a subscription, sbn_…, charged usd at the start of each cycle
price.includes{ <service id>: <jobs> }noneAllowances per cycle: inside one a job is $0 and counted; past it, the order answers 402 OFFER at the job's price. Renewal resets them
subscription on an ordersbn_…noneOrders the job on the plan: inside an allowance at $0, past it at its own price
SurfaceWhat every and includes add
APIThe order answers an sbn_ object; GET /subscriptions/:id, PATCH /subscriptions/:id and POST /subscriptions/:id/cancel
CLIsubscriptions list, get, update and cancel
MCPThe order tool; get_subscription, update_subscription and cancel_subscription in the sandbox toolset
SDKsubscriptions.list(), get(), update() and cancel()
SiteThe price sentence, with what each cycle includes
DocsThe plan's order and its operations in /llms.txt
AppSubscriptions and allowances used in the customer portal; the cycle's price in the owner console
CodeWhen
subscription_unpricedService(): includes or on without price.every
every_unknownService(): price.every isn't a cycle
allowance_uncountedService(): an allowance isn't a whole number of jobs

Tax. A year for $2,400, routed to six Services, five of them inside an allowance. The third notice is past two and is offered at $299. The Tax Plan.

captured/tax-plan.txt · magic values: none
→ POST /events {"type":"notice.received","id":"CP161-0003","data":{"ein":"12-3456789","notice":"cp161.pdf","reply_by":"2027-05-01","tax_years":["2025"]}}
← subscription.minted · event notice.received → notice-response offered at $299.00

Two years of the Tax Plan: each cycle's charge, then each job it drew on at $0 with its own costs. A return inside the allowance still paid its signer:

captured/tax-money.txt · magic values: owner.email = signs@example.test, metadata.sandbox.asks = {"organizer.books":"after 160d","organizer.assets":"waive","organizer #2":"after 2d","w9":"after 19d"}, metadata.sandbox.outside = {"reject":{"file.US-NY":["ein_mismatch"]}}, metadata.sandbox.events = { notice.received: { at: 2027-11-14T15:00:00Z, data: { notice, reply_by, tax_years } } }
→ GET /subscriptions/sbn_7Hq2
← sbn_7Hq2 · tax-plan · active · cycle 2 · 2028-01-20 → 2029-01-20 · $2,400.00 per year
  charge      cycle 1 · $2,400.00 · 2027-01-20
  charge      cycle 2 · $2,400.00 · 2028-01-20
# each job the plan drew on, as the owner reads its ledger
  job_4Tx1  information-returns  2026  +$0.00 · AI $0.84 · you −$0.84 · 5 payees
  job_4Tx3  annual-return        2026  +$0.00 · AI $3.36 · signer $210.00 · you −$213.36
  job_4Tx4  estimate                   +$0.00 · AI $0.42 · you −$0.42
  job_4Tx5  estimate                   +$0.00 · AI $0.42 · you −$0.42

Legal. A docket watched for $12 a month, with no allowance. Each weekly check is $0 out of the month, and an office action is offered at its own response's price. Docket watch.

Call
→ POST /events {"type":"office_action.issued","id":"OA-97123456-1","data":{"matter":"63/000,001","serial":"97123456","action":"office-action.pdf","issued":"2026-10-30","client":{"name":"Ada Lovelace","email":"ada@example.com"}}}
magic values: none
← subscription.minted · event office_action.issued → trademark-response offered at $349.00
  next    order trademark-response {"serial":"97123456","action":"office-action.pdf","issued":"2026-10-30","client":{"name":"Ada Lovelace","email":"ada@example.com"},"max_usd":349,"key":"sbn_7Hq2:event office_action.issued:OA-97123456-1","subscription":"sbn_7Hq2"}

Sales. Paid per outcome, on the close, with no cycle.

Subscriptions run live on Launch.