Subscriptions
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:
grep price services/plan.ts price: { usd: 149, every: 'month', includes: { translation: 4 } },check reads the cycle back, with what it leaves you:
npx services.dev checkservices/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.80The order opens sbn_7Hq2 and charges the first cycle at once:
→ POST /plan {"site":"acme.example","document":"lease.pdf","to":"fr-CA","max_usd":149,"key":"plan"}← 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→ GET /events?type=subscription.created 2026-10-25 sbn_7Hq2 created · cycle 1 · $149.00 charged · ends 2026-11-25Each 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:
→ POST /events {"type":"page.changed","id":"page-1","data":{"site":"acme.example","document":"page-1.html"}}→ 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.84A 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:
→ POST /translation {"document":"page-5.html","to":"fr-CA","subscription":"sbn_7Hq2"}← 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.16A 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:
→ POST /test_clocks/default/advance {"to":"2026-11-26T00:00:00Z"} 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→ GET /subscriptions/sbn_7Hr3 # another buyer's plan: the renewal's card was declined← 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| Status | Allowances | New orders |
|---|---|---|
past_due, while Stripe retries and an ask asks for a new card | usable | priced as usual |
unpaid, once Brand.dunning runs out | frozen | 402, with the balance and the exact next call |
active, once a retry collects | as they were | priced as usual |
a schedule's date, while past_due or unpaid | its job mints, held | runs once a retry collects: no date skipped, none worked unpaid |
PATCH /subscriptions/:id, subscriptions update, update_subscription, subscriptions.update():
| An update | |
|---|---|
| changes | the subscription's input: a new state, a new officer, a new books link |
| takes effect | at the next mint, recording subscription.updated |
| leaves | jobs already minted on their input, and the cycle's charge unchanged |
npx services.dev subscriptions update sbn_7Hq2 jurisdictions=US,US-CA,US-NY,US-NJ,US-TX --test-clock tc_7Hq4sbn_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-01A 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_7Hq2sbn_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 → translationcurl -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:
→ POST /test_clocks/default/advance {"to":"2027-01-02T00:00:00Z"} 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:
| Event | The buyer's card |
|---|---|
subscription.created | The first cycle charged, with when it ends |
allowance.used | $0 on the job, counted against the cycle |
allowance.exceeded | Nothing yet: the extra job is offered at its price, 402 OFFER, with the exact next call |
subscription.minted | A date or an event: the job minted, the job offered, or the refusal recorded |
subscription.renewed | Charged again; every allowance reset |
charge.failed | The renewal declined: update the card; the plan reads past_due |
subscription.unpaid | Still declined when dunning ran out: allowances frozen until it's paid |
subscription.updated | The new input, and the next mint that takes it |
subscription.canceled | Nothing more: it runs to the end of the cycle paid for |
| Key | Written | Left out | Does |
|---|---|---|---|
price.every | 'day', 'week', 'month', 'quarter' or 'year' | one job | An order is a subscription, sbn_…, charged usd at the start of each cycle |
price.includes | { <service id>: <jobs> } | none | Allowances 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 order | sbn_… | none | Orders the job on the plan: inside an allowance at $0, past it at its own price |
| Surface | What every and includes add |
|---|---|
| API | The order answers an sbn_ object; GET /subscriptions/:id, PATCH /subscriptions/:id and POST /subscriptions/:id/cancel |
| CLI | subscriptions list, get, update and cancel |
| MCP | The order tool; get_subscription, update_subscription and cancel_subscription in the sandbox toolset |
| SDK | subscriptions.list(), get(), update() and cancel() |
| Site | The price sentence, with what each cycle includes |
| Docs | The plan's order and its operations in /llms.txt |
| App | Subscriptions and allowances used in the customer portal; the cycle's price in the owner console |
| Code | When |
|---|---|
subscription_unpriced | Service(): includes or on without price.every |
every_unknown | Service(): price.every isn't a cycle |
allowance_uncounted | Service(): 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.
→ 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.00Two 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:
→ 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.
→ 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"}}}← 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.