---
title: "File it somewhere"
url: https://services.dev/docs/grow/submit
captured: [grow-submit-reject-once.txt, grow-submit-tabs.txt, grow-submit.txt]
---
# File it somewhere

URL: https://services.dev/docs/grow/submit

> 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.

## Add the filing

`check` counts 22 lines. The Pool is Grow 3's, and the table gains one row:

```ts title="grow/submit.ts"
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',
      to: 'the registry',
      receipt: 'filing',
      rejected: { missing_certificate: 'sign', '*': 'translate' },
      perfect: '2d',
      fallback: 'hand_off',
    },
    hand_off: { ask: 'buyer', to: 'answer', routed: true },
  },
  price: 89,
})

// lingua's Pool, as Grow 3 left it: sworn translators apply with their name, credential and languages.
function translators() {
  return Pool({
    name: 'sworn-translators',
    roles: { translator: { credential: 'sworn translator' } },
    join: { name: 'text: your name as sworn', credential: ['sworn translator'], languages: 'language[]: each language you are sworn in' },
    verify: 'https://verify.example.test/sworn-translators',
  })
}

// lingua's rules table, two illustrative rows: signing is reserved to a sworn translator, and filing is open.
function rules() {
  return Rules({
    version: '2026-10',
    services: {
      translation: {
        sign: { reserved: 'sworn translator', position: 'A registry accepts a translation a sworn translator signs.' },
        file: { open: true, position: 'Sending a signed translation to a registry needs no credential.' },
      },
    },
  })
}
```

`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`](https://services.dev/docs/reference/check/act_unruled.md).

| 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](https://services.dev/docs/service/submit.md) covers every key.

## Check it

```bash
npx services.dev check
```

```txt title="magic values: metadata.sandbox.outside = { reject: [missing_certificate] }"
services/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.

## Run one job

The magic value `metadata.sandbox.outside = { reject: [missing_certificate] }` makes the simulated registry reject the first filing:

**CLI**

```bash title="CLI"
npx services.dev translation document=https://example.com/lease.pdf to=fr-CA --max 89 --key grow --metadata 'sandbox.outside={"reject":["missing_certificate"]}'
```

```txt title="magic values: 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

# mbr_7Hq2, the sworn translator, claims tsk_7Hq3 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq3/claim · POST /tasks/tsk_7Hq3/clear · POST /tasks/tsk_7Hq3/attest

$ npx services.dev jobs wait job_7Hq2
job_7Hq2 · delivered · lease.fr-CA.pdf · $89.00 charged
```

**API**

```bash title="API"
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"]}}}}'
```

```txt title="202 · job_7Hq2 · working · $89.00 held · magic values: 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"}

# mbr_7Hq2, the sworn translator, claims tsk_7Hq3 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq3/claim · POST /tasks/tsk_7Hq3/clear · POST /tasks/tsk_7Hq3/attest

$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
← 200 · job_7Hq2 · delivered · lease.fr-CA.pdf · $89.00 charged
```

**MCP**

```txt title="MCP"
→ 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"]}}}}
```

```txt title="magic values: 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"}

# mbr_7Hq2, the sworn translator, claims tsk_7Hq3 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq3/claim · POST /tasks/tsk_7Hq3/clear · POST /tasks/tsk_7Hq3/attest

→ resources/read job://job_7Hq2?wait=50
← job_7Hq2 · delivered · lease.fr-CA.pdf · $89.00 charged
```

**SDK**

```ts title="SDK"
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"]}}}})
```

```txt title="magic values: 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"}

# mbr_7Hq2, the sworn translator, claims tsk_7Hq3 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq3/claim · POST /tasks/tsk_7Hq3/clear · POST /tasks/tsk_7Hq3/attest

await lingua.jobs.wait("job_7Hq2")
← job_7Hq2 · delivered · lease.fr-CA.pdf · $89.00 charged
```

The record:

```txt
→ POST /translation {"document":"lease.pdf","to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":{"reject":["missing_certificate"]}}}}
```

```txt title="magic values: 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"}
# 20 minutes later, mbr_7Hq2, the sworn translator, claims tsk_7Hq3 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq3/claim · POST /tasks/tsk_7Hq3/clear · POST /tasks/tsk_7Hq3/attest

← job_7Hq2 · delivered · lease.fr-CA.pdf · $89.00 charged
  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 | sign · person · sworn translator · * · mbr_7Hq2 | file · the registry · accepted · rcp_7Hq3
  ledger  +$89.00 · AI $1.26 · translator $60.00 · you $27.74
  signed  {"member":"mbr_7Hq2","credential":"sworn translator","region":"*","rules":"2026-10","fields":["document"],"hash":"sha256:e6fe1e8aa69adec4e929764e2e52b2685f51174f15155672b9dfcfc2c7de2d3a"}
  stamp   Signed by mbr_7Hq2, sworn translator, 2026-10-25.
  receipt rcp_7Hq2 · rejected · missing_certificate · at 2026-10-25T16:22:11.000Z
  receipt rcp_7Hq3 · accepted · at 2026-10-25T16:22:11.000Z · sent 2026-10-25T16:42:11.000Z
```

`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`.

## Any other code

`metadata.sandbox.outside = reject_once` rejects with a code the file doesn't name, so the work goes back to `translate`:

```txt
→ POST /translation {"document":"lease.pdf","to":"fr-CA","key":"grow","max_usd":89,"metadata":{"sandbox":{"outside":"reject_once"}}}
```

```txt title="magic values: 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"}
# 20 minutes later, mbr_7Hq2, the sworn translator, claims tsk_7Hq3 in the delivery portal and signs, in their own session
→ POST /tasks/tsk_7Hq3/claim · POST /tasks/tsk_7Hq3/clear · POST /tasks/tsk_7Hq3/attest

← job_7Hq2 · delivered · lease.fr-CA.pdf · $89.00 charged
  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 · reject_once · repairing at translate | translate · ai · redo | sign · translator · refused · unattested · document changed after the translator signed it; they sign the new version before it is filed. | harden · ai · adversarial · 2 lenses · pass | sign · person · sworn translator · * · mbr_7Hq2 | file · the registry · accepted · rcp_7Hq3
  ledger  +$89.00 · AI $2.52 · translator $60.00 · you $26.48
  signed  {"member":"mbr_7Hq2","credential":"sworn translator","region":"*","rules":"2026-10","fields":["document"],"hash":"sha256:e1acf2486220648ec94aecf4a4d8e6b67cf423d348ccaa75e89be1afb18a2e9b"}
  stamp   Signed by mbr_7Hq2, sworn translator, 2026-10-25.
  receipt rcp_7Hq2 · rejected · reject_once · at 2026-10-25T16:22:11.000Z
  receipt rcp_7Hq3 · accepted · at 2026-10-25T16:22:11.000Z · sent 2026-10-25T16:42:11.000Z
```

The redo changed the document, so the gate refused the old signature (`unattested`) and posted a second Task. You kept $26.48.

## What changed

```bash
npx services.dev diff grow/role.ts grow/submit.ts
```

```txt title="magic values: metadata.sandbox.outside = { reject: [missing_certificate] }"
grow/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
      + object rcp_                          /jobs/:id · receipts
```

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](https://services.dev/docs/examples/tax/efile.md) files once per jurisdiction with `each` and routes each rejection to the signature it needs.

## Next

[A date the world sets](https://services.dev/docs/grow/clock.md) gives the filing a date. [All four, in one filing](https://services.dev/docs/grow/together.md) adds a signature. [How a job runs](https://services.dev/docs/start/how-it-runs.md) puts every step on one map.

---

- Up: [Your first paid job](https://services.dev/docs.md)
- Previous: [Add a signer](https://services.dev/docs/grow/role.md)
- Next: [A date the world sets](https://services.dev/docs/grow/clock.md)
