---
title: "Ask the buyer"
url: https://services.dev/docs/grow/ask
captured: [grow-ask-tabs.txt, grow-ask.txt]
codes: [needs_the_party]
---
# Ask the buyer

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

> Add an ask step. The buyer answers one question through a link that needs no account, and the job's clock pauses until they do.

Some work needs something only a person on the buyer's side can give: an answer, a file, a signature, consent or a decision. An ask step puts it to them through a hosted link that needs no account, and the job's deadline pauses until they act. Here the buyer says which terms stay in English. This is Grow 2.

## Add the ask

`check` counts 9 lines:

```ts title="grow/ask.ts"
import { Service } from 'services.dev'

// Grow 2, ask the buyer: before the AI starts, the buyer says which terms stay in English, through a
// link that needs no account. The job waits on them, and its clock pauses until they answer.
export default Service({
  name: 'Translation',
  input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
  output: { document: 'file' },
  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 },
  },
  price: 49,
})
```

`glossary` is an [ask](https://services.dev/docs/service/ask.md). It's written first, so it runs first, and `translate` waits for it.

| Key | What it does | Default |
|---|---|---|
| `ask` | Who acts: `'buyer'`, or a `party` input such as a co-signer (`party?` when optional, `party[]` for several) | required |
| `to` | What they do: `answer`, `upload`, `sign`, `consent`, `decide`, `direct`, or `grant`, a scoped and expiring permission a later step `needs` ([Grants](https://services.dev/docs/service/grants.md)) | required |
| `items` | A request list, a function of the job, so the questions can follow the input. Each item is answered, waived with a reason, or substituted; a step can start on one with `after: 'glossary.terms'`. [Request lists](https://services.dev/docs/service/intake.md) derives one from the job and its subject `deliver.items` | none |
| `expires` | When the link stops working. The ask then opens once more, with a new link; a second expiry lapses the job, which charges only the acts already committed | `7d` |
| `remind` | Reminders, each this long before the link expires | `['1d']` |
| `choices` | For `decide`: the options the party picks from. A decision is theirs alone | none |
| `how` | How the act is proven: `'click'`, `'esign'`, `'id-verified'` or `'notarized'`, kept on the record | `'click'` |

[Asks](https://services.dev/docs/service/ask.md) lists the rest: `what` a signature binds, `all` for several parties, `capacity` and `void`.

## Check it

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

```txt title="magic values: none"
services/translation.ts · 9 lines · $49 per job, within 48 hours, charged on delivery.
  who     AI delivers every job. The buyer answers at glossary.
  steps   glossary · buyer · answer → translate · ai → harden · ai · adversarial · 2 lenses
  agent   order_translation · 29 of 30 words · job_7Hq2 waits on party
  ledger  +$49.00 · AI $1.26 (example) · you $47.74
  margin  at least $39.20 a job (80%) · a job that stops costs you at most $9.80
  apps    customer portal · owner console

mcp     13 buyer tools · 4,494 of 8,000 tokens · 9 more with ?toolset=sandbox
✓ ready to sell · 1 Service · it keeps at least 80% 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 `who` line names the step where the buyer answers, and the `agent` line says an order now waits on a party. The margin doesn't move: asking costs nothing.

## Run one job

No magic value is needed. In this capture the buyer answers two hours after the order; on localhost, `dev` serves the link, so open the one the CLI prints and answer it yourself.

**CLI**

```bash title="CLI"
npx services.dev translation document=https://example.com/lease.pdf to=fr-CA --max 49 --key grow
```

```txt title="magic values: none"
job_7Hq2 · working · $49.00 held
job_7Hq2 · waiting · on party · $49.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 · delivered · lease.fr-CA.pdf · $49.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":49}'
```

```txt title="202 · job_7Hq2 · working · $49.00 held · magic values: none"
  next    get_job {"id":"job_7Hq2"}

$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
← 200 · job_7Hq2 · waiting · on party · $49.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 · delivered · lease.fr-CA.pdf · $49.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":49}
```

```txt title="magic values: none"
← job_7Hq2 · working · $49.00 held
  next    get_job {"id":"job_7Hq2"}

→ resources/read job://job_7Hq2?wait=50
← job_7Hq2 · waiting · on party · $49.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 · delivered · lease.fr-CA.pdf · $49.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":49})
```

```txt title="magic values: none"
← job_7Hq2 · working · $49.00 held
  next    get_job {"id":"job_7Hq2"}

await lingua.jobs.wait("job_7Hq2")
← job_7Hq2 · waiting · on party · $49.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 · delivered · lease.fr-CA.pdf · $49.00 charged
```

Every surface answers the same way: the job waits `on: party`, and its next step is `hand_to_person`, with the link. An `answer` or `upload` may also come from an agent holding the party's delegated key; `sign`, `consent` and `decide` need the party's own link, and any other caller is refused [`needs_the_party`](https://services.dev/docs/reference/errors.md#needs_the_party). The record:

```txt
→ POST /translation {"document":"lease.pdf","to":"fr-CA","key":"grow","max_usd":49}
```

```txt title="magic values: none"
← job_7Hq2 · waiting · on party · $49.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 · delivered · lease.fr-CA.pdf · $49.00 charged
  record  glossary.terms · answered | glossary · buyer · answer · clock paused 2h | translate · ai | harden · ai · adversarial · 2 lenses · pass
  ledger  +$49.00 · AI $1.26 · you $47.74
```

`waiting` reads `"clock":"paused"`, and the record keeps the two hours as `clock paused 2h`, so the wait doesn't count against the 48-hour deadline. [Deadlines](https://services.dev/docs/service/within.md) lists everything that pauses it.

## What changed

```bash
npx services.dev diff grow/review.ts grow/ask.ts
```

```txt title="magic values: none"
grow/review.ts (8 lines) → grow/ask.ts (9 lines)
Translation · changed deliver, human · adds an ask step: a party acts through a hosted link

API   ~ operation service.order.translation  POST /translation  item.description
      ~ operation service.quote.translation  GET /translation.json  answers.deliver, answers.human.sentence, answers.surfaces.description
      + operation ask.get                    GET /asks/{id}
      + operation ask.complete               POST /asks/{id}
      + operation ask.reissue                POST /asks/{id}/reissue
      ~ operation brand.manifest             GET /manifest.json  answers.services
      + object ask_                          ask
      + 10 events                            job.paused, job.ended, ask.opened, item.done, ask.reminded, ask.done, ask.expired, ask.voided, ask.relinked, consent.given
      + 4 errors                             needs_the_party, ask_expired, ask_voided, signature_void
CLI   + operation ask.get                    services.dev asks get <ask>
      + operation ask.reissue                services.dev asks reissue <ask>
      + 10 events                            job.paused, job.ended, ask.opened, item.done, ask.reminded, ask.done, ask.expired, ask.voided, ask.relinked, consent.given
      + 4 errors                             needs_the_party, ask_expired, ask_voided, signature_void
MCP   ~ operation order_translation          order_translation  description
      + operation reissue_ask                reissue_ask
SDK   + operation ask.reissue                asks.reissue()
      ~ object job_ (translation)            Job<"translation">  + stage: "glossary" | "translate" | "harden"
      + object ask_                          ask
      + 10 events                            job.paused, job.ended, ask.opened, item.done, ask.reminded, ask.done, ask.expired, ask.voided, ask.relinked, consent.given
      + 4 errors                             needs_the_party, ask_expired, ask_voided, signature_void
Site  ~ operation service.order.translation  /translation  + $49 per job, within 48 hours, charged on delivery. AI delivers every job. The buyer answer
      ~ operation service.quote.translation  /translation.json  deliver, human.sentence, surfaces.description
Docs  ~ operation service.order.translation  /llms.txt · Translation  + Translation: send a document and a language; get a document back. $49 per job, within 48 h
      + operation ask.reissue                /llms.txt · Operations
      + 4 errors                             needs_the_party, ask_expired, ask_voided, signature_void
App   ~ operation job.list                   /portal · customer portal  + translation: AI delivers every job. The buyer answers at glossary.
      + operation ask.complete               /a/:id · hosted page
      ~ operation job.ledger                 /console · owner console  + translation: $49 per job, within 48 hours, charged on delivery. AI delivers every job. The
      + object ask_                          /a/:id
```

An ask is the first key that adds calls: `ask.get`, `ask.complete` and `ask.reissue`, the hosted page at `/a/:id`, ten events and four errors, each on every surface that reaches it.

In a brand, [the legal example](https://services.dev/docs/examples/legal/demand-letter.md) asks the client for consent before any work starts, and for a decision when a reply comes back.

## Next

[Add a signer](https://services.dev/docs/grow/role.md): a sworn translator signs every job.

---

- Up: [Your first paid job](https://services.dev/docs.md)
- Previous: [Harden it](https://services.dev/docs/grow/review.md)
- Next: [Add a signer](https://services.dev/docs/grow/role.md)
