Every month

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

Sell Translation by the month on the buyer's site. A schedule and an event order it, four a month are included, the fifth is offered at its price, and renewal resets the count.

Some buyers want the work kept up, not ordered one job at a time. A subscription charges once a cycle and orders the jobs itself: on a schedule, and whenever the buyer's systems say something changed. This is Grow 7: the four-line Translation stays as it is, and a plan beside it sells it by the month.

check counts 7 lines:

grow/subscription.ts
import { Service } from 'services.dev'
import translation from '../services/translation.ts'

// Grow 7, every month: the four-line Translation, kept up on the buyer's site. The plan is a subscription on the site,
// $149 a month. On the 1st it translates the site's document, and each page.changed the buyer's systems send translates
// that page. Four translations a month are included, at $0; past four, the next is offered at Translation's own $49.
export default Service({
  name: 'Translation Plan',
  description: 'A site kept translated, every month and on every change.',
  input: { site: 'text: your site, such as acme.example', document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
  output: { report: 'text' },
  subject: { kind: 'site', key: 'site' },
  on: { schedule: { monthly: { at: 'every month', order: translation } }, event: { 'page.changed': translation } },
  price: { usd: 149, every: 'month', includes: { translation: 4 } },
})

subject is what the plan keeps up, the buyer's site. on orders Translation on the 1st and on every page.changed, and price makes an order a subscription.

KeyWhat it doesDefault
subjectWhat the work is kept on: kind, in your words, and key, the input (or the output a receipt fills) whose value names it. It outlives every job; Subjects adds keeps, period and of_recordnone
on.scheduleDates, by name, each ordering this Service, or with { at, order } the one it names: 'every month' is the 1st, and 'every monday', 'every quarter', 'Feb 1' and '2027-02-01' work too. Each mints at the calendar's rings_atnone
on.eventEvents, by name, each ordering a Service: from your other Services' jobs on the subject, or from the buyer's systems through POST /events as { type, id, data }. data names the subject and fills the order; the same id again mints nothing morenone
price.every'day', 'week', 'month', 'quarter' or 'year': an order is a subscription, sbn_…, charged usd at the start of each cycle, counted from the day it's orderedone job
price.includesAllowances per cycle, by Service id: inside one a job is $0 and counted; past it, the order answers 402 OFFER at the job's own price. Renewal resets themnone

Schedules and triggers covers routing, each and catch_up, and Subjects what a subject keeps.

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.
  who     AI delivers every job.
  steps   draft · ai → review · ai · adversarial
  subject site · keyed by site
  agent   order_plan · 29 of 30 words · bought sbn_7Hq2 · $149.00 · no person
  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
  apps    customer portal · owner console

services/translation.ts · 4 lines · $49 per job, within 48 hours, charged on delivery.
  who     AI delivers every job.
  steps   draft · ai → review · ai · adversarial
  agent   order_translation · 24 of 30 words · bought job_7Hq2 · $49.00 · no person
  ledger  +$49.00 · AI $0.84 (example) · you $48.16
  margin  at least $39.20 a job (80%) · a job that stops costs you at most $9.80
  apps    customer portal · owner console

mcp     14 buyer tools · 5,166 of 8,000 tokens · 9 more with ?toolset=sandbox
✓ ready to sell · 2 Services · AI delivers both alone · each keeps at least 80% of every job
  next    npx services.dev dev

The plan prices a cycle, not a job. Translation still reads $49 a job.

The order answers a subscription, its allowance at 0 of 4:

npx services.dev plan site=acme.example document=https://example.com/acme/index.html to=fr-CA --max 149 --key plan --json
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "active",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,
    "start": "2026-10-25T14:02:11.000Z",
    "end": "2026-11-25T14:02:11.000Z"
  },
  "allowances": {
    "translation": {
      "used": 0,
      "included": 4
    }
  },
curl -X POST http://localhost:8787/plan -d '{"site":"acme.example","document":{"url":"https://example.com/acme/index.html","name":"index.html"},"to":"fr-CA","max_usd":149,"key":"plan"}'
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "active",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,
    "start": "2026-10-25T14:02:11.000Z",
    "end": "2026-11-25T14:02:11.000Z"
  },
  "allowances": {
    "translation": {
      "used": 0,
      "included": 4
    }
  },
→ tools/call order_plan {"site":"acme.example","document":{"url":"https://example.com/acme/index.html","name":"index.html"},"to":"fr-CA","max_usd":149,"key":"plan"}
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "active",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,
    "start": "2026-10-25T14:02:11.000Z",
    "end": "2026-11-25T14:02:11.000Z"
  },
  "allowances": {
    "translation": {
      "used": 0,
      "included": 4
    }
  },
await lingua.plan.order({"site":"acme.example","document":{"url":"https://example.com/acme/index.html","name":"index.html"},"to":"fr-CA","max_usd":149,"key":"plan"})
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "active",
  "subject": "sbj_7Hq2",
  "policy": {},
  "price": {
    "usd": 149,
    "every": "month"
  },
  "cycle": {
    "number": 1,
    "start": "2026-10-25T14:02:11.000Z",
    "end": "2026-11-25T14:02:11.000Z"
  },
  "allowances": {
    "translation": {
      "used": 0,
      "included": 4
    }
  },

A test clock runs the cycle. Three pages change, then the 1st comes, and each mints Translation at $0:

Call
→ POST /plan {"site":"acme.example","document":"lease.pdf","to":"fr-CA","max_usd":149,"key":"plan","test_clock":"tc_7Hq3"}
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

# three pages change this month: each mints Translation on the plan, inside its four
→ POST /events {"type":"page.changed","id":"page-1","data":{"site":"acme.example","document":"page-1.html"}}
← subscription.minted · event page.changed → job_7Hq2 translation · $0.00
→ POST /events {"type":"page.changed","id":"page-2","data":{"site":"acme.example","document":"page-2.html"}}
← 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"}}
← subscription.minted · event page.changed → job_7Hq4 translation · $0.00

# the 1st mints the fourth
→ POST /test_clocks/tc_7Hq3/advance {"to":"2026-11-02T00:00:00Z"}
  2026-11-01  sbn_7Hq2  schedule monthly → job_7Hv5 translation · $0.00 · allowance 4 of 4

A fifth change is past the four, so Translation is offered at its own $49, a 402 OFFER whose next is the order to place. Placing it holds $49 on a job from the plan:

Call
→ POST /events {"type":"page.changed","id":"page-5","data":{"site":"acme.example","document":"page-5.html"}}
magic values: none
← subscription.minted · event page.changed → translation offered at $49.00
  next    order translation {"document":"page-5.html","to":"fr-CA","max_usd":49,"key":"sbn_7Hq2:event page.changed:page-5","test_clock":"tc_7Hq3","subscription":"sbn_7Hq2"}

→ POST /translation {"document":"page-5.html","to":"fr-CA","max_usd":49,"key":"sbn_7Hq2:event page.changed:page-5","test_clock":"tc_7Hq3","subscription":"sbn_7Hq2"}
← job_7Hq6 · working · $49.00 held · from sbn_7Hq2

On the 25th the plan renews: it charges $149 again and resets the allowance, so the 1 December job is 1 of 4 at $0.

Call
→ POST /test_clocks/tc_7Hq3/advance {"to":"2026-12-02T00:00:00Z"}
magic values: none
  2026-11-25  sbn_7Hq2  renews · cycle 2 · $149.00 charged · allowances reset · ends 2026-12-25
  2026-12-01  sbn_7Hq2  schedule monthly → job_7Hq7 translation · $0.00 · allowance 1 of 4

← sbn_7Hq2 · plan · active · cycle 2 · 2026-11-25 → 2026-12-25 · $149.00 per month
  allowances  translation 1 of 4
  schedule    monthly → translation · next 2027-01-01
  event       page.changed → translation
  minted      2026-10-25  event page.changed → job_7Hq2 translation · $0.00
  minted      2026-10-25  event page.changed → job_7Hq3 translation · $0.00
  minted      2026-10-25  event page.changed → job_7Hq4 translation · $0.00
  minted      2026-11-01  schedule monthly → job_7Hv5 translation · $0.00
  minted      2026-11-02  event page.changed → translation offered at $49.00
  minted      2026-12-01  schedule monthly → job_7Hq7 translation · $0.00
Run
npx services.dev diff . with-plan
magic values: none
. (4 lines) → with-plan (2 Services)
Translation Plan · new
Translation · changed nothing

API   ~ operation service.order        POST /jobs  item.requestBody.content.application/json.schema.properties.service.enum
      + operation service.order.plan   POST /plan
      + operation service.quote.plan   GET /plan.json
      + operation subject.list         GET /subjects
      + operation subject.get          GET /subjects/{id}
      + operation member.subjects      GET /me/subjects
      + operation subject.withdraw     POST /subjects/{id}/withdraw
      + operation subscription.list    GET /subscriptions
      + operation subscription.get     GET /subscriptions/{id}
      + operation subscription.cancel  POST /subscriptions/{id}/cancel
      ~ operation brand.manifest       GET /manifest.json  answers.services, answers.tools
      + object sbj_                    subject
      + object sbn_                    subscription
      + 6 events                       subscription.created, subscription.renewed, subscription.minted, allowance.used, allowance.exceeded, subscription.canceled
CLI   + operation service.order.plan   services.dev plan site=… document=… to=…
      + operation subject.list         services.dev subjects list

The plan brings the subject and subscription objects, their calls, cancel among them, and six events. The customer portal gains subscriptions and allowances used, and Translation's MCP tool takes a subscription, so an agent orders past an allowance on the plan. Subscriptions and allowances covers what each cycle charges and pays.

In a brand, the tax example routes one plan's schedule and events to several small Services, and the legal example orders a job on each event it watches.

All four, in one filing puts Grow 1 to 4 in one regulated Service.