File it somewhere
Send the signed translation to a registry, keep its receipt, route a coded rejection back to the signer, and charge on the receipt.
Often the job is done only when someone else's system accepts it. A submit step sends the work there, keeps every receipt, routes each rejection by its code, and holds the charge until the system accepts. This is Grow 4: Grow 3's sworn translator still signs, and what they signed is what's filed.
check counts 22 lines. The Pool is Grow 3's, and the table gains one row:
import { Pool, Rules, Service } from 'services.dev'
// Grow 4, file it somewhere: once the sworn translator signs, the translation goes to a registry, and its
// receipt fills `filing` and releases the charge. A rejection coded missing_certificate goes back to the
// signer; any other code, to the AI, and the changed file is signed again. Two rejections, or silence, hand
// the filing to the buyer.
export default Service({
name: 'Translation',
description: 'A document, translated, signed and filed.',
input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
output: { document: 'file', filing: 'text' },
rules: rules(),
roles: { translator: { pool: translators(), pay: 30 } },
deliver: {
glossary: { ask: 'buyer', to: 'answer', items: () => [{ id: 'terms', label: 'Which terms stay in English?' }] },
translate: 'ai',
harden: { review: 'adversarial', lenses: ['meaning', 'terms'], rounds: 2 },
sign: { by: 'translator', stamp: true, est: '20min' },
file: {
submit: 'https://registry.example.test/filings',file sends the signed document to the registry and writes its receipt into filing. rejected routes by code: missing_certificate back to sign, anything else back to translate, so a changed file is reviewed and signed again. Filing is an act, so it needs a row; without one, check refuses with act_unruled.
| Key | What it does | Default |
|---|---|---|
submit | The outside system: a URL, or your function that sends one submission and returns the answer | required |
to | Its name on the record | required |
receipt | The output field the receipt fills | required |
act | The act this step performs, as your rules table names it | the step's name |
rejected | Where a rejection goes: one step, a map from codes to steps with '*' for the rest (the order you write the codes in is their priority), or a function of the codes | the fallback |
perfect | A repair resubmitted inside this window keeps the first submission's time | off |
wait | How long an answer may take; silence past it goes to the fallback | 2d |
fallback | What runs after two rejections, or silence | none: the job stops at $0 |
each | One submission and receipt per element of a list; lead goes first | off |
On localhost a simulated registry answers example.test. Sending to outside systems covers every key.
npx services.dev checkservices/translation.ts · 22 lines · $89 per job, within 48 hours, charged on file's receipt.
who A translator signs every document before it is filed. (rules 2026-10) The buyer answers at glossary.
roles translator · sworn translator · from sworn-translators · $30 an act
steps glossary · buyer · answer → translate · ai → harden · ai · adversarial · 2 lenses → sign · translator · * · stamped → file · the registry → hand_off · buyer · answer · routed
bounds reserved sign to sworn translator · open file
agent order_translation · 27 of 30 words · job_7Hq2 waits on party
ledger +$89.00 · AI $1.26 (example) · translator $30.00 · you $57.74
margin at least $41.20 when the first translator signs ($89.00 − $30.00 translator − $17.80 AI cap) · $26.20 at the ladder's cap, the lower of 50% and what the floor leaves · $11.20 after a decline and a second translator
a job that stops costs you at most $47.80
apps customer portal · delivery portal · owner console
mcp 13 buyer tools · 4,506 of 8,000 tokens · 9 more with ?toolset=sandbox
✓ ready to sell · 1 Service · it keeps at least 46% of every job
next npx services.dev dev
AI and money are simulated until you wire them. $0.42 a call is an example.The price now ends charged on file's receipt: the buyer's money stays held until the registry accepts. price.release names another step.
The magic value metadata.sandbox.outside = { reject: [missing_certificate] } makes the simulated registry reject the first filing:
npx services.dev translation document=https://example.com/lease.pdf to=fr-CA --max 89 --key grow --metadata 'sandbox.outside={"reject":["missing_certificate"]}'job_7Hq2 · working · $89.00 held
job_7Hq2 · waiting · on party · $89.00 held
record
next hand http://localhost:8787/a/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ to the person it asks
# 2 hours later, the buyer answers through the link
→ POST /asks/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ {"items":{"terms":{"answer":"Lessee, Lessor, Premises"}}}
$ npx services.dev jobs wait job_7Hq2
job_7Hq2 · waiting · on claim · $89.00 held
record glossary.terms · answered | glossary · buyer · answer · clock paused 2h | translate · ai | harden · ai · adversarial · 2 lenses · pass
next a member claims tsk_7Hq2: npx services.dev tasks sheet tsk_7Hq2, in their session
# mbr_7Hq2, the sworn translator, claims tsk_7Hq2 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq2/claim · POST /tasks/tsk_7Hq2/clear · POST /tasks/tsk_7Hq2/attest
$ npx services.dev jobs wait job_7Hq2
job_7Hq2 · waiting · on claim · $89.00 held
record glossary.terms · answered | glossary · buyer · answer · clock paused 2h | translate · ai | harden · ai · adversarial · 2 lenses · pass | sign · person · sworn translator · * · mbr_7Hq2 | file · the registry · rejected · missing_certificate · repairing at sign
next a member claims tsk_7Hq3: npx services.dev tasks sheet tsk_7Hq3, in their session
curl -X POST http://localhost:8787/jobs -H 'content-type: application/json' -d '{"service":"translation","document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":{"reject":["missing_certificate"]}}}}' next get_job {"id":"job_7Hq2"}
$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
← 200 · job_7Hq2 · waiting · on party · $89.00 held
next hand_to_person {"ask":"ask_7Hq2"}
# 2 hours later, the buyer answers through the link
→ POST /asks/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ {"items":{"terms":{"answer":"Lessee, Lessor, Premises"}}}
$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
← 200 · job_7Hq2 · waiting · on claim · $89.00 held
next get_job {"id":"job_7Hq2"}
# mbr_7Hq2, the sworn translator, claims tsk_7Hq2 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq2/claim · POST /tasks/tsk_7Hq2/clear · POST /tasks/tsk_7Hq2/attest
$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
← 200 · job_7Hq2 · waiting · on claim · $89.00 held
next get_job {"id":"job_7Hq2"}
→ tools/call order_translation {"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":{"reject":["missing_certificate"]}}}}← job_7Hq2 · working · $89.00 held
next get_job {"id":"job_7Hq2"}
→ resources/read job://job_7Hq2?wait=50
← job_7Hq2 · waiting · on party · $89.00 held
next hand_to_person {"ask":"ask_7Hq2"}
# 2 hours later, the buyer answers through the link
→ POST /asks/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ {"items":{"terms":{"answer":"Lessee, Lessor, Premises"}}}
→ resources/read job://job_7Hq2?wait=50
← job_7Hq2 · waiting · on claim · $89.00 held
next get_job {"id":"job_7Hq2"}
# mbr_7Hq2, the sworn translator, claims tsk_7Hq2 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq2/claim · POST /tasks/tsk_7Hq2/clear · POST /tasks/tsk_7Hq2/attest
→ resources/read job://job_7Hq2?wait=50
← job_7Hq2 · waiting · on claim · $89.00 held
next get_job {"id":"job_7Hq2"}
await lingua.translation.order({"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":{"reject":["missing_certificate"]}}}})← job_7Hq2 · working · $89.00 held
next get_job {"id":"job_7Hq2"}
await lingua.jobs.wait("job_7Hq2")
← job_7Hq2 · waiting · on party · $89.00 held
next hand_to_person {"ask":"ask_7Hq2"}
# 2 hours later, the buyer answers through the link
→ POST /asks/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ {"items":{"terms":{"answer":"Lessee, Lessor, Premises"}}}
await lingua.jobs.wait("job_7Hq2")
← job_7Hq2 · waiting · on claim · $89.00 held
next get_job {"id":"job_7Hq2"}
# mbr_7Hq2, the sworn translator, claims tsk_7Hq2 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq2/claim · POST /tasks/tsk_7Hq2/clear · POST /tasks/tsk_7Hq2/attest
await lingua.jobs.wait("job_7Hq2")
← job_7Hq2 · waiting · on claim · $89.00 held
next get_job {"id":"job_7Hq2"}
The record:
→ POST /translation {"document":"lease.pdf","to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":{"reject":["missing_certificate"]}}}}← job_7Hq2 · waiting · on party · $89.00 held
waiting {"on":"party","since_at":"2026-10-25T14:02:11.000Z","step":"glossary","clock":"paused"}
# 2 hours later, the buyer answers through the link
→ POST /asks/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ {"items":{"terms":{"answer":"Lessee, Lessor, Premises"}}}
← job_7Hq2 · waiting · on claim · $89.00 held
waiting {"on":"claim","since_at":"2026-10-25T16:02:11.000Z","step":"sign","task":"tsk_7Hq2"}
# on localhost you are the owner, and the owner holds no credential
→ POST /sworn-translators/jobs/job_7Hq2/approve
← 403 needs_a_person: sign is tsk_7Hq2, a Task for a member of sworn-translators: the member who claims and clears it attests, in their own session.
# 20 minutes later, mbr_7Hq2, the sworn translator, claims tsk_7Hq2 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq2/claim
← tsk_7Hq2 · claimed · translator · sign · * · $30.00 · 20 min · $90.00 an hour · due 2026-10-27 16:02
→ POST /tasks/tsk_7Hq2/clear · GET /tasks/tsk_7Hq2/packet
→ POST /tasks/tsk_7Hq2/attest
← tsk_7Hq2 · done · translator · sign · * · $30.00 · attested · 20 min · $90.00 an hour · due 2026-10-27 16:02
← job_7Hq2 · waiting · on claim · $89.00 held
waiting {"on":"claim","since_at":"2026-10-25T16:22:11.000Z","step":"sign","task":"tsk_7Hq3"}
missing_certificate sent the job back to sign: two signatures at $30, so you kept $27.74, above the $11.20 worst case check printed. The charge released on rcp_7Hq3, sent at 16:42 and dated 16:22, the first filing's time, because the repair landed inside perfect.
metadata.sandbox.outside = reject_once rejects with a code the file doesn't name, so the work goes back to translate:
→ POST /translation {"document":"lease.pdf","to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":"reject_once"}}}← job_7Hq2 · waiting · on party · $89.00 held
waiting {"on":"party","since_at":"2026-10-25T14:02:11.000Z","step":"glossary","clock":"paused"}
# 2 hours later, the buyer answers through the link
→ POST /asks/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_ {"items":{"terms":{"answer":"Lessee, Lessor, Premises"}}}
← job_7Hq2 · waiting · on claim · $89.00 held
waiting {"on":"claim","since_at":"2026-10-25T16:02:11.000Z","step":"sign","task":"tsk_7Hq2"}
# on localhost you are the owner, and the owner holds no credential
→ POST /sworn-translators/jobs/job_7Hq2/approve
← 403 needs_a_person: sign is tsk_7Hq2, a Task for a member of sworn-translators: the member who claims and clears it attests, in their own session.
# 20 minutes later, mbr_7Hq2, the sworn translator, claims tsk_7Hq2 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq2/claim
← tsk_7Hq2 · claimed · translator · sign · * · $30.00 · 20 min · $90.00 an hour · due 2026-10-27 16:02
→ POST /tasks/tsk_7Hq2/clear · GET /tasks/tsk_7Hq2/packet
→ POST /tasks/tsk_7Hq2/attest
← tsk_7Hq2 · done · translator · sign · * · $30.00 · attested · 20 min · $90.00 an hour · due 2026-10-27 16:02
← job_7Hq2 · waiting · on claim · $89.00 held
waiting {"on":"claim","since_at":"2026-10-25T16:22:11.000Z","step":"sign","task":"tsk_7Hq3"}
The redo changed the document, so the gate refused the old signature (unattested) and posted a second Task. You kept $26.48.
npx services.dev diff grow/role.ts grow/submit.tsgrow/role.ts (13 lines) → grow/submit.ts (22 lines)
Translation · changed description, output, deliver, rules, price · adds a submit step: an outside system and its receipts
API ~ operation service.order.translation POST /translation item.description, item.responses.200.content.application/json.schema.properties.output.properties.filing, item.responses.200.content.application/json.schema.properties.output.required · breaking
~ operation service.quote.translation GET /translation.json answers.description, answers.output.properties.filing, answers.output.required · breaking
~ operation brand.manifest GET /manifest.json answers.services
+ object rcp_ receipt
+ 4 events submission.sent, receipt.accepted, receipt.rejected, receipt.timed_out
CLI ~ operation service.order.translation services.dev translation document=… to=… price
+ 4 events submission.sent, receipt.accepted, receipt.rejected, receipt.timed_out
MCP ~ operation order_translation order_translation description, outputSchema.oneOf
~ prompt buy_translation prompts/get buy_translation description
SDK ~ object job_ (translation) Job<"translation"> + output: { document: File; filing: string }, + stage: "glossary" | "translate" | "harden" | "sign" | "file" | "hand_off"
+ object rcp_ receipt
+ 4 events submission.sent, receipt.accepted, receipt.rejected, receipt.timed_out
Site ~ operation service.order.translation /translation + A document, translated, signed and filed., + $89 per job, within 48 hours, charged on file's receipt. A translator signs at sign, stamp
~ operation service.quote.translation /translation.json description, output.properties.filing, output.required · breaking
Docs ~ operation service.order.translation /llms.txt · Translation + A document, translated, signed and filed. $89 per job, within 48 hours, charged on file's
App ~ operation ask.complete /a/:id · hosted page + translation: hand_off · buyer answer · routed
~ operation job.ledger /console · owner console + translation: $89 per job, within 48 hours, charged on file's receipt. A translator signs a
The output gains filing, breaking for clients that type it, and the receipt object and four events arrive. Grow 3's Pool and Tasks carry over.
In a brand, the tax example files once per jurisdiction with each and routes each rejection to the signature it needs.
A date the world sets gives the filing a date. All four, in one filing adds a signature. How a job runs puts every step on one map.