---
title: "Operations"
url: https://services.dev/docs/reference/operations
captured: [api-checkout.txt, api-empty.txt, api-events-list.txt, api-manifest.txt, api-not-found.txt, api-ok.txt, api-openapi-item.txt, api-order-body.txt, api-pricing.txt, api-suite-json.txt, api-watch.txt, ask-the-client.txt, ask.txt, brand-smallest.txt, clock-calls.txt, clock-live.txt, clock-run-held.txt, clock-vacant.txt, ends.txt, export.txt, grow-role.txt, grow-subscription-tabs.txt, grow-subscription.txt, lapse.txt, legal-region.txt, member-ledger.txt, member-wire.txt, money-subscriptions-tabs.txt, pool-register.txt, service-grant.txt, service-policy.txt, service-signals.txt, service-subject.txt, submit.txt, tab-relink.txt, talk-default.txt]
codes: [act_refused, api_error, ask_expired, ask_voided, body_invalid, brand_unverified, clock_advancing, clock_behind, clock_closed, clock_moved_on, clock_not_due, defects_required, end_not_allowed, event_id_required, evidence_missing, evidence_required, evidence_used, guard_refused, ineligible, input_invalid, job_ended, key_invalid, key_missing, key_reused, memo_required, merits_not_yours, needs_a_person, needs_direction, needs_grant, needs_the_owner, needs_the_party, not_cancelable, not_cleared, not_contestable, not_delivered, not_found, not_live, not_on_site, not_terminal, not_waiting, nothing_to_redo, period_taken, policy_invalid, question_open, region_not_ratified, reserved, signature_void, talk_off, task_taken, version_unknown]
---
# Operations

URL: https://services.dev/docs/reference/operations

> Every call a brand serves, one row each: its HTTP route, CLI command, MCP tool and SDK method, who may make it, what it does, what it emits and every answer it can give.

90 operations. Each is one row of [the contract](https://services.dev/docs/surfaces/contract.md): its HTTP route, CLI command, MCP tool and SDK method are renderings of the row, and [`surface_drift`](https://services.dev/docs/reference/check/surface_drift.md) fails `check` when a surface strays from it.

Beyond the codes its row names, every call can answer the ones its kind meets, by one rule: a call a key makes, [`key_missing`](https://services.dev/docs/reference/errors.md#key_missing) and [`key_invalid`](https://services.dev/docs/reference/errors.md#key_invalid); a POST or PUT, [`body_invalid`](https://services.dev/docs/reference/errors.md#body_invalid); a call idempotent with a key, [`key_reused`](https://services.dev/docs/reference/errors.md#key_reused); a person's call, when a key makes it, [`needs_a_person`](https://services.dev/docs/reference/errors.md#needs_a_person); a party's call, when a key makes it, [`needs_the_party`](https://services.dev/docs/reference/errors.md#needs_the_party); and every call, [`version_unknown`](https://services.dev/docs/reference/errors.md#version_unknown) and [`api_error`](https://services.dev/docs/reference/errors.md#api_error).

Each credential, code, event and object links to its reference the first time this page names it.

## Takes calls share

What several calls take word for word, said once: each of them names it under its own row.

| Takes | In | Type | Says | Taken by |
|---|---|---|---|---|
| `limit` | query | `integer` | Default `10`. At most this many, up to 100. | `job.list`, `subject.list`, `clock.list`, `subscription.list`, `watch.list` |
| `starting_after` | query | `string` | The id of the last object on the page before. | `job.list`, `subject.list`, `clock.list`, `subscription.list`, `watch.list` |
| `key` | body | `string` | Any string you choose: the same key replays the first answer and never acts twice. The Idempotency-Key header does the same. | `job.send_back`, `job.signal`, `job.message`, `task.message`, `clock.ack`, `ask.reissue`, `test_clock.create`, `watch.create`, `watch.replay`, `event.inbound` |
| `due` | body | `string` | The occurrence you mean, by its due_on (2027-04-15) or due_at: a clock that moved or recurred since answers clock_moved_on with the call for the one it is at now. The portals and the CLI always send it. | `clock.ack`, `clock.satisfy`, `clock.choose` |

## Ordering

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`service.order`](https://services.dev/docs/reference/operations.md#serviceorder) | `POST /jobs` | `<service> k=v …` | `order_<service>` | `<service>.order()` |
| [`service.quote`](https://services.dev/docs/reference/operations.md#servicequote) | `GET /:service.json` | — | — | — |
| [`service.waitlist`](https://services.dev/docs/reference/operations.md#servicewaitlist) | `POST /:service/waitlist` | — | — | — |
| [`service.list`](https://services.dev/docs/reference/operations.md#servicelist) | `GET /catalog` | — | — | — |
| [`service.get`](https://services.dev/docs/reference/operations.md#serviceget) | `GET /catalog/:id` | — | — | — |

### `service.order`

Order a job, or get the offer: the price and the exact next call. A Service whose price has every answers a subscription, sbn_….

Also served at `POST /:service`.

Who may call: [`sk_test_`](https://services.dev/docs/reference/credentials.md#sk_test_), [`sk_live_`](https://services.dev/docs/reference/credentials.md#sk_live_), [`dlg_`](https://services.dev/docs/reference/credentials.md#dlg_), [`tk_`](https://services.dev/docs/reference/credentials.md#tk_) · effect: act · gate: access · idempotent: with a key · returns: [`job`](https://services.dev/docs/reference/objects.md#job)

**Emits** [`job.ordered`](https://services.dev/docs/reference/events.md#job.ordered), [`charge.held`](https://services.dev/docs/reference/events.md#charge.held) · **Can answer** [OFFER](https://services.dev/docs/api/answers.md), [`input_invalid`](https://services.dev/docs/reference/errors.md#input_invalid), [`policy_invalid`](https://services.dev/docs/reference/errors.md#policy_invalid), [`key_reused`](https://services.dev/docs/reference/errors.md#key_reused), [`not_found`](https://services.dev/docs/reference/errors.md#not_found), [`not_live`](https://services.dev/docs/reference/errors.md#not_live), [`act_refused`](https://services.dev/docs/reference/errors.md#act_refused), [`region_not_ratified`](https://services.dev/docs/reference/errors.md#region_not_ratified), [`brand_unverified`](https://services.dev/docs/reference/errors.md#brand_unverified), [`period_taken`](https://services.dev/docs/reference/errors.md#period_taken)

| Takes | In | Type | Says |
|---|---|---|---|
| `service` | body | `string` | The Service to order. POST `/<service>` names it in the path instead. |
| `max_usd` | body | `number` | The most you will pay, in dollars. Missing or below the price, you get an offer instead of a job. |
| `key` | body | `string` | Any string you choose. Retrying with the same key returns the same job. |
| `metadata` | body | `object` | Your own keys, echoed on the job and its events. |
| `test_clock` | body | `string` | A test clock the job runs on: tc_…. |

```bash
curl -X POST http://localhost:8787/jobs -H 'content-type: application/json' -d '{"service":"certified-translation","document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr","country":"FR","max_usd":189,"key":"lease-fr-3"}'
```

```json title="captured/api-checkout.txt · 402 · magic values: none"
{
  "type": "OFFER",
  "object": "offer",
  "service": "certified-translation",
  "title": "Certified Translation",
  "price": {
    "usd": 189,
    "sentence": "$189 per job, within 3 days, charged on delivery."
  },
  "eta": "3d",
  "terms": "Charged on delivery. Cancel at $0 until it ships, except the work a person attested once the signature before certify is done. Send it back with a note and the AI redoes it, or you pay $0. Final 7 days after delivery.",
  "checkout_url": "http://localhost:8787/keys/sk_test_7Hq2",
  "next": {
    "do": "checkout",
    "url": "http://localhost:8787/keys/sk_test_7Hq2",
    "tool": null,
    "http": "GET http://localhost:8787/keys/sk_test_7Hq2",
    "then": {
      "do": "order",
      "service": "certified-translation",
      "arguments": {
        "document": {
          "url": "https://example.com/lease.pdf",
          "name": "lease.pdf"
        },
        "to": "fr",
        "country": "FR",
        "max_usd": 189,
        "key": "lease-fr-3",
        "service": "certified-translation"
      },
      "tool": "order_job",
      "http": "POST http://localhost:8787/jobs {\"service\":\"certified-translation\"}"
    }
  },
  "simulated": true
}
```

**Explained in** [Your first paid job](https://services.dev/docs.md), [How a job runs](https://services.dev/docs/start/how-it-runs.md), [Your API](https://services.dev/docs/api.md), [A buyer that is software](https://services.dev/docs/sdk/agent.md)

### `service.quote`

Read a Service as JSON, keyless; past the buyer key's cap, the checkout offer.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `service`

**Can answer** OFFER

| Takes | In | Type | Says |
|---|---|---|---|
| `max_usd` | query | `number` | A ceiling to price against the buyer key's cap. |

```bash
curl http://localhost:8787/translation.json   # input: the Service's own fields, each by its type word
```

```txt title="captured/api-order-body.txt · magic values: none"
document    file      required  { url, name }
to          language  required  a BCP 47 tag, such as fr-CA
```

**Explained in** [Your API](https://services.dev/docs/api.md)

### `service.waitlist`

Join the waitlist of a Service that takes no orders yet: the next a not_live answer names.

Who may call: anyone · effect: record · gate: none · idempotent: yes · returns: `waitlist`

**Can answer** `input_invalid`, `not_found`

| Takes | In | Type | Says |
|---|---|---|---|
| `email` | body | `string` | **Required.** Where to tell you when it takes orders. |

```txt
→ POST /contract-review/waitlist {"email":"ada@example.com"}   # the next the refusal named
```

```txt title="captured/legal-region.txt · magic values: none"
← 200 {"object":"waitlist","service":"contract-review","status":"live","position":1,"simulated":true}
```

**Explained in** [Publish before it takes orders](https://services.dev/docs/service/status.md)

### `service.list`

Every Service, keyless: its price, when it charges and whether it takes orders yet, and the metering rule every Service keeps: no record, no charge.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `list`

### `service.get`

One Service of the catalog, keyless, by its id: its price, when it charges and its status.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `service`

**Can answer** `not_found`

## Jobs

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`job.get`](https://services.dev/docs/reference/operations.md#jobget) | `GET /jobs/:id` | `jobs get <job>` | `get_job` | `jobs.get()` |
| [`job.wait`](https://services.dev/docs/reference/operations.md#jobwait) | `GET /jobs/:id?wait=<seconds>` | `jobs wait <job>` | `job://` | `jobs.wait()` |
| [`job.list`](https://services.dev/docs/reference/operations.md#joblist) | `GET /jobs` | `jobs list` | `list_jobs` | `jobs.list()` |
| [`job.accept`](https://services.dev/docs/reference/operations.md#jobaccept) | `POST /jobs/:id/accept` | `jobs accept <job>` | `accept_job` | `jobs.accept()` |
| [`job.send_back`](https://services.dev/docs/reference/operations.md#jobsend_back) | `POST /jobs/:id/send_back` | `jobs send-back <job> <note>` | `send_back_job` | `jobs.sendBack()` |
| [`job.cancel`](https://services.dev/docs/reference/operations.md#jobcancel) | `POST /jobs/:id/cancel` | `jobs cancel <job>` | `cancel_job` | `jobs.cancel()` |
| [`job.end`](https://services.dev/docs/reference/operations.md#jobend) | `POST /jobs/:id/end` | `jobs end <job> <end>` | `end_job` | `jobs.end()` |
| [`job.again`](https://services.dev/docs/reference/operations.md#jobagain) | `POST /jobs/:id/again` | `jobs again <job>` | `reorder_job` | `jobs.again()` |
| [`job.policy`](https://services.dev/docs/reference/operations.md#jobpolicy) | `POST /jobs/:id/policy` | `jobs set-policy <job> k=v …` | `set_job_policy` | `jobs.setPolicy()` |
| [`job.signal`](https://services.dev/docs/reference/operations.md#jobsignal) | `POST /jobs/:id/signals` | `jobs signal <job> <signal>` | `send_job_signal` | `jobs.signal()` |
| [`job.message`](https://services.dev/docs/reference/operations.md#jobmessage) | `POST /jobs/:id/messages` | `jobs message <job> <text>` | `send_job_message` | `jobs.message()` |
| [`job.thread`](https://services.dev/docs/reference/operations.md#jobthread) | `GET /jobs/:id/messages` | `jobs messages <job>` | `list_job_messages` | `jobs.messages()` |
| [`job.record`](https://services.dev/docs/reference/operations.md#jobrecord) | `GET /jobs/:id/record` | `jobs record <job>` | never | `jobs.record()` |
| [`job.approve`](https://services.dev/docs/reference/operations.md#jobapprove) | `POST /:portal/jobs/:id/approve` | `jobs approve <job>` | — | — |
| [`job.export`](https://services.dev/docs/reference/operations.md#jobexport) | `GET /jobs/:id/export` | `jobs export <job>` | — | — |
| [`job.ledger`](https://services.dev/docs/reference/operations.md#jobledger) | `GET /owner/jobs/:id` | `jobs ledger <job>` | — | — |

### `job.get`

Read a job, now or after waiting up to wait seconds while it works or waits on a person. Waiting on a party, it answers at once with the link to hand them.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_` · effect: read · gate: access · idempotent: yes · returns: `job`

**Can answer** `not_found`

| Takes | In | Type | Says |
|---|---|---|---|
| `wait` | query | `number` | Seconds to wait, up to 50; 0 reads it now. |
| `expand` | query | `array` | expand[]=timeline: the events of this job a buyer may read. |

```bash
curl http://localhost:8787/jobs/job_0000
```

```json title="captured/api-not-found.txt · 404 · magic values: none"
{
  "type": "EMPTY",
  "category": "invalid_request",
  "code": "not_found",
  "reason": "No job job_0000.",
  "param": "id",
  "next": null,
  "doc_url": "http://localhost:8787/docs/errors/not_found",
  "request_id": "req_c7PJaH",
  "simulated": true
}
```

**Explained in** [The job lifecycle](https://services.dev/docs/service/lifecycle.md), [Your API](https://services.dev/docs/api.md)

### `job.wait`

Wait up to 50 seconds for a job to finish, through any person's line.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_` · effect: read · gate: access · idempotent: yes · returns: `job`

**Can answer** `not_found`

| Takes | In | Type | Says |
|---|---|---|---|
| `wait` | query | `number` | Default `50`. Seconds to wait, up to 50. |

```bash
curl http://localhost:8787/jobs/job_7Hq2?wait=50
```

```txt title="captured/ask.txt · magic values: none"
← job_7Hq2 · delivered · lease.fr-CA.pdf · $49.00 charged
  record  glossary.terms · answered | glossary · buyer · answer · clock paused 2h 5min | translate · ai | review · ai · pass
```

**Explained in** [Your first paid job](https://services.dev/docs.md), [A buyer that is software](https://services.dev/docs/sdk/agent.md)

### `job.list`

The buyer's jobs, newest first: OK, or EMPTY when none match.

Also served at `GET /owner/jobs`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, [owner session](https://services.dev/docs/reference/credentials.md#owner-session) · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `input_invalid`, [`needs_a_person`](https://services.dev/docs/reference/errors.md#needs_a_person)

| Takes | In | Type | Says |
|---|---|---|---|
| `filter` | query | `string` | Only this Service's jobs. |
| `scope` | query | `string` | admin: every job, the owner's alone. |

Also takes `limit`, `starting_after`, as [the calls that share it](#takes-calls-share) do.

```bash
curl 'http://localhost:8787/jobs?limit=10'
```

```json title="captured/api-empty.txt · 200 · magic values: none"
{
  "type": "EMPTY",
  "object": "list",
  "data": [],
  "has_more": false,
  "url": "/jobs",
  "message": "No jobs yet."
}
```

**Explained in** [Conventions](https://services.dev/docs/api/conventions.md), [The customer portal](https://services.dev/docs/app/buyers.md)

### `job.accept`

Accept a delivered job. It is final; a job nobody accepts is final 7 days after delivery.

Who may call: `sk_test_`, `sk_live_`, `dlg_` · effect: act · gate: access · idempotent: yes · returns: `job`

**Emits** [`job.accepted`](https://services.dev/docs/reference/events.md#job.accepted), [`charge.earned`](https://services.dev/docs/reference/events.md#charge.earned), [`charge.released`](https://services.dev/docs/reference/events.md#charge.released) · **Can answer** `not_found`, [`not_delivered`](https://services.dev/docs/reference/errors.md#not_delivered), [`needs_the_party`](https://services.dev/docs/reference/errors.md#needs_the_party)

```bash
curl -X POST http://localhost:8787/jobs/job_7Hq2/accept -H 'content-type: application/json' -d '{}'
```

```json title="captured/api-ok.txt · 200 · magic values: none"
{
  "id": "job_7Hq2",
  "object": "job",
  "created": "2026-10-25T14:02:11.000Z",
  "livemode": false,
  "simulated": true,
  "service": "translation",
  "input": {
    "document": {
      "url": "https://example.com/lease.pdf",
      "name": "lease.pdf"
    },
    "to": "fr-CA"
  },
  "metadata": {},
  "price": {
    "usd": 49,
    "approved": false,
    "max_usd": 49
  },
  "payment": {
    "status": "final",
    "amount_usd": 49
  },
  "human": "AI delivers every job.",
  "stage": "review",
  "steps": [
    {
      "id": "stp_1",
      "object": "step",
      "step": "draft",
      "by": "ai",
      "status": "committed",
      "hash": "sha256:7dbf1c28960226c2d8a1cc1ca8a5125d56fb7cd3ce11585532c2caedd411bff4"
    },
    {
      "id": "stp_2",
      "object": "step",
      "step": "review",
      "by": "ai",
      "review": "adversarial",
      "verdict": "pass",
      "status": "committed",
      "hash": "sha256:95851b48d6d0adf87f5c3d1210a3157976f66081111d2367176e5f8c92508cb2"
    }
  ],
  "next": [],
  "status": "accepted",
  "output": {
    "document": {
      "url": "data:text/plain,Simulated%20Translation%3A%20document",
      "name": "lease.fr-CA.pdf"
    }
  },
  "final_at": "2026-11-01T14:02:11.000Z"
}
```

**Explained in** [Your first paid job](https://services.dev/docs.md), [A buyer that is software](https://services.dev/docs/sdk/agent.md)

### `job.send_back`

Send a job back with a note, and the AI redoes it: a delivered one as the buyer, a waiting one as the person at the line.

Also served at `POST /:portal/jobs/:id/send_back`, `POST /jobs/:id/send-back`, `POST /:portal/jobs/:id/send-back`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session, [member session](https://services.dev/docs/reference/credentials.md#member-session) · effect: act · gate: access · idempotent: with a key · returns: `job`

**Emits** [`job.sent_back`](https://services.dev/docs/reference/events.md#job.sent_back), [`job.canceled`](https://services.dev/docs/reference/events.md#job.canceled), [`charge.voided`](https://services.dev/docs/reference/events.md#charge.voided), [`charge.refunded`](https://services.dev/docs/reference/events.md#charge.refunded) · **Can answer** `not_found`, `not_delivered`, [`not_waiting`](https://services.dev/docs/reference/errors.md#not_waiting), [`nothing_to_redo`](https://services.dev/docs/reference/errors.md#nothing_to_redo)

| Takes | In | Type | Says |
|---|---|---|---|
| `note` | body | `string` | **Required.** What to fix. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

### `job.cancel`

Cancel a job before delivery. The buyer pays $0.

Who may call: `sk_test_`, `sk_live_`, `dlg_` · effect: act · gate: access · idempotent: yes · returns: `job`

**Emits** `job.canceled`, `charge.voided`, `charge.refunded` · **Can answer** `not_found`, [`not_cancelable`](https://services.dev/docs/reference/errors.md#not_cancelable)

### `job.end`

End a job at one of its ends, as that end's by allows. An end is forever.

Also served at `POST /owner/jobs/:id/end`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session · effect: act · gate: access · idempotent: yes · returns: `job`

**Emits** [`job.ended`](https://services.dev/docs/reference/events.md#job.ended), [`charge.collected`](https://services.dev/docs/reference/events.md#charge.collected), `charge.earned`, `charge.released`, `charge.refunded`, `charge.voided` · **Can answer** `not_found`, [`end_not_allowed`](https://services.dev/docs/reference/errors.md#end_not_allowed)

| Takes | In | Type | Says |
|---|---|---|---|
| `end` | body | `string` | **Required.** One of the Service's ends. |
| `reason` | body | `string` | Why, for the record. |

```txt
→ POST /jobs/job_7Hv5/end {"end":"withdrawn","reason":"The client withdrew."}
```

```txt title="captured/ends.txt · 200 · job_7Hv5 · ended · withdrawn · filing.txt · $60.00 final · magic values: none"
  record  draft · ai | check · reviewer | withdrawn · ended · buyer · The client withdrew.
  ledger  +$60.00 · AI $0.42 · reviewer $60.00 · you −$0.42
```

**Explained in** [How a job ends](https://services.dev/docs/service/ends.md)

### `job.again`

Order an ended job again, or a follow-on Service its end offers: a new job, with from on its record.

Who may call: `sk_test_`, `sk_live_`, `dlg_` · effect: act · gate: access · idempotent: with a key · returns: `job`

**Emits** `job.ordered`, `charge.held` · **Can answer** OFFER, `not_found`, [`not_terminal`](https://services.dev/docs/reference/errors.md#not_terminal), `input_invalid`, `policy_invalid`, `not_live`, `period_taken`

| Takes | In | Type | Says |
|---|---|---|---|
| `service` | body | `string` | A follow-on Service the end offers; the same Service when left out. |
| `max_usd` | body | `number` | The most you will pay, in dollars; without it, the offer. |
| `key` | body | `string` | Any string you choose. Retrying with the same key returns the same job. |

```txt
→ POST /jobs/job_7Hq6/again
```

```json title="captured/ends.txt · 402 · magic values: none"
{
  "type": "OFFER",
  "object": "offer",
  "service": "deal",
  "title": "Deal",
  "price": {
    "usd": 99,
    "sentence": "$99 per job, within 48 hours, charged on delivery."
  },
  "eta": "48h",
  "terms": "Charged on delivery. Cancel at $0 until it ships. You may end it at lost ($0). Send it back with a note and the AI redoes it, or you pay $0. Final 7 days after delivery.",
  "next": {
    "do": "again",
    "arguments": {
      "id": "job_7Hq6",
      "max_usd": 99,
      "key": "again-job_7Hq6"
    },
    "tool": "reorder_job",
    "http": "POST http://localhost:8787/jobs/job_7Hq6/again"
  },
  "simulated": true
}
```

**Explained in** [How a job ends](https://services.dev/docs/service/ends.md)

### `job.policy`

Set a job's policy values within their bounds: mechanics from the buyer or the brand's console, merits from the client alone.

Also served at `POST /owner/jobs/:id/policy`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session · effect: record · gate: access · idempotent: yes · returns: `job`

**Emits** [`policy.set`](https://services.dev/docs/reference/events.md#policy.set) · **Can answer** `not_found`, `policy_invalid`, [`merits_not_yours`](https://services.dev/docs/reference/errors.md#merits_not_yours), [`job_ended`](https://services.dev/docs/reference/errors.md#job_ended)

| Takes | In | Type | Says |
|---|---|---|---|
| `policy` | body | `object` | **Required.** The values to set, within their bounds. |

```txt
→ POST /owner/jobs/job_7Hq2/policy {"policy":{"match":0.7}}
```

```json title="captured/service-policy.txt · 403 · magic values: none"
{
  "type": "BLOCKED",
  "category": "blocked",
  "code": "merits_not_yours",
  "reason": "match binds the merits, and only the client sets them.",
  "param": "policy.match",
  "next": {
    "do": "get",
    "arguments": {
      "id": "job_7Hq2"
    },
    "tool": "get_job",
    "http": "GET http://localhost:8787/jobs/job_7Hq2?wait=50"
  },
  "doc_url": "http://localhost:8787/docs/errors/merits_not_yours",
  "request_id": "req_yvQQ1S",
  "simulated": true
}
```

**Explained in** [What the buyer sets, and what only the client sets](https://services.dev/docs/service/policy.md)

### `job.signal`

Raise one of the Service's inbound signals. A signal never blocks; its goto moves the job.

Who may call: `sk_test_`, `sk_live_`, `dlg_` · effect: record · gate: access · idempotent: with a key · returns: `job`

**Emits** [`signal.raised`](https://services.dev/docs/reference/events.md#signal.raised) · **Can answer** `not_found`, `input_invalid`, `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `signal` | body | `string` | **Required.** An inbound signal of the Service. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

```txt
→ POST /jobs/job_7Hq2/signals {"signal":"source_changed"}
```

```txt title="captured/service-signals.txt · 200 · job_7Hq2 · working · $79.00 held · magic values: none"
  record  long · signal | translate · ai | source_changed · signal · goto translate | translate · ai · redo
```

**Explained in** [Signals](https://services.dev/docs/service/signals.md)

### `job.message`

Say something in the job's thread as the client. The AI answers at once, or holds advice a rules row reserves until a member adopts it.

Who may call: `sk_test_`, `sk_live_`, `dlg_` · effect: record · gate: access · idempotent: with a key · returns: `message`

**Emits** [`thread.posted`](https://services.dev/docs/reference/events.md#thread.posted), [`message.sent`](https://services.dev/docs/reference/events.md#message.sent), [`message.held`](https://services.dev/docs/reference/events.md#message.held) · **Can answer** `not_found`, `input_invalid`, `job_ended`, [`talk_off`](https://services.dev/docs/reference/errors.md#talk_off)

| Takes | In | Type | Says |
|---|---|---|---|
| `text` | body | `string` | **Required.** What the client says; an email or phone in it is withheld. |
| `metadata` | body | `object` | Your keys; in the sandbox, sandbox.talk = advice or open. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

```txt
→ POST /jobs/job_7Hq2/messages {"text":"Will it read formal or informal?"}
```

```txt title="captured/talk-default.txt · magic values: none"
← 200 msg_7Hq3 · AI · answer · sent: Simulated: what the record shows about "Will it read formal or informal?". The job is delivered.
```

**Explained in** [Talk](https://services.dev/docs/service/talk.md)

### `job.thread`

Read the job's messages, oldest first: one resource with the posts. Held advice reads 'A professional is reviewing this answer.' until a member adopts it.

Who may call: `sk_test_`, `sk_live_`, `dlg_` · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `not_found`, `talk_off`

**Explained in** [Talk](https://services.dev/docs/service/talk.md)

### `job.record`

A job's record as you may read it, oldest first: a key reads the buyer's events of it, the owner the whole record. type=attested keeps the attestations: each step a person attested, under the rules version it pinned.

Also served at `GET /owner/jobs/:id/record`.

Never on MCP. An agent reads the same events with get_job's expand[]=timeline.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `not_found`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `type` | query | `string` | attested: only the attestations. |

### `job.approve`

Approve, sign or do a job waiting on a person: a proposal the gate commits or refuses, as it does the AI's. A key or a tool never can.

Also served at `POST /jobs/:id/approve`, `POST /jobs/:id/sign`, `POST /owner/jobs/:id/approve`.

Who may call: owner session, member session · effect: act · gate: person · idempotent: yes · returns: `job`

**Emits** [`step.proposed`](https://services.dev/docs/reference/events.md#step.proposed), [`step.committed`](https://services.dev/docs/reference/events.md#step.committed), [`step.refused`](https://services.dev/docs/reference/events.md#step.refused), [`ask.opened`](https://services.dev/docs/reference/events.md#ask.opened) · **Can answer** `not_found`, `not_waiting`, `needs_a_person`, [`needs_grant`](https://services.dev/docs/reference/errors.md#needs_grant), `needs_the_party`, [`guard_refused`](https://services.dev/docs/reference/errors.md#guard_refused), `end_not_allowed`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `output` | body | `object` | A person's step: the output they propose. |
| `checklist` | body | `object` | Each item of the step's checklist the person affirms: \{ `<item>:` true \}. |

```txt
→ POST /sworn-translators/jobs/job_7Hq2/approve
```

```txt title="captured/grow-role.txt · magic values: none"
← 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.
```

**Explained in** [human: the line](https://services.dev/docs/people.md), [Refusals and cures](https://services.dev/docs/people/refusals.md), [What an agent can never do](https://services.dev/docs/mcp/never.md)

### `job.export`

One job's evidence bundle: its record, every file it holds with its sha256, and what the record replays to.

Also served at `GET /owner/jobs/:id/export`.

Who may call: owner session · effect: read · gate: access · idempotent: yes · returns: `evidence`

**Can answer** `not_found`

```bash
curl http://localhost:8787/owner/jobs/job_7Hq2/export   # each artifact's text left out
```

```json title="captured/export.txt · magic values: metadata.sandbox.review = flag_once"
{
  "object": "evidence",
  "job": "job_7Hq2",
  "service": "localization",
  "record": "28 events: job.ordered, charge.held, decision.made, step.proposed, step.committed, review.done, job.delivered, charge.collected",
  "artifacts": [
    {
      "path": "strings/strings.de.json",
      "sha256": "sha256:0c7bd35fe41dfdcf7d5d17e225761e5e9a605ab843eee3e56b7dcec061283a37"
    },
    {
      "path": "listing/listing.de.txt",
      "sha256": "sha256:3cd302d9f573c87f793ef20985f8b7b9c2210fde89b985045ed694e806cff59e"
    },
    {
      "path": "review/qa.json",
      "sha256": "sha256:b02acc1d389f334d06bb64e508e451ff9394cc0b55217fde9aa7e93bca4a130d"
    },
    {
      "path": "strings/strings.de.json",
      "sha256": "sha256:b1c723d0ebe964c8745ded184fd02d9a2e36374d49078b6ab511ee90463a0553"
    },
    {
      "path": "review/qa.json",
      "sha256": "sha256:1b22cc595c1768dfbeabc8fe96d89fd564c1f8c80cd0e004fb8c5b657056de18"
    }
  ],
  "hashes": {
    "record": "sha256:460a07a7ec6cec91856f8bc7d13b00f2277404619272131528ac336e40a4946d"
  },
  "replayed": {
    "status": "delivered",
    "stage": "qa",
    "waiting": null,
    "ledger": {
      "charges": [
        {
          "cents": 4900,
          "at": "delivery"
        }
      ],
      "ai": 420,
      "services": [],
      "payout": 0,
      "fee": 0,
      "you": 4480
    }
  }
}
```

### `job.ledger`

One job, as the owner reads it: its whole record, its ledger, and what it left the brand.

Who may call: owner session · effect: read · gate: access · idempotent: yes · returns: `ledger`

**Can answer** `not_found`

```bash
curl http://localhost:8787/owner/jobs/job_7Hq2   # 2026-10-31: a day before the link expires
```

```txt title="captured/lapse.txt · magic values: client.email = silent@example.test"
← job_7Hq2 · waiting · on party · $120.00 held
  record  draft · ai | sign · reminded 1d before expiry
  asks    ask_7Hq2 open
```

**Explained in** [The owner console](https://services.dev/docs/app/owner.md), [The money](https://services.dev/docs/examples/legal/money.md), [The money](https://services.dev/docs/examples/tax/money.md)

## Members

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`member.join`](https://services.dev/docs/reference/operations.md#memberjoin) | `POST /:portal/join` | `members add <pool> <email> k=v …` | — | — |
| [`member.verify`](https://services.dev/docs/reference/operations.md#memberverify) | `POST /:portal/members/:id/verify` | — | — | — |
| [`member.availability`](https://services.dev/docs/reference/operations.md#memberavailability) | `POST /me/availability` | `me availability <availability>` | — | — |
| [`member.ledger.get`](https://services.dev/docs/reference/operations.md#memberledgerget) | `GET /me/ledger` | `me ledger` | — | — |
| [`member.ledger.add`](https://services.dev/docs/reference/operations.md#memberledgeradd) | `POST /me/ledger` | `me ledger add <name> k=v …` | — | — |
| [`member.ledger.checks`](https://services.dev/docs/reference/operations.md#memberledgerchecks) | `GET /me/ledger/checks` | `me ledger checks` | — | — |
| [`member.missed`](https://services.dev/docs/reference/operations.md#membermissed) | `GET /me/missed` | `me missed` | — | — |
| [`member.earnings`](https://services.dev/docs/reference/operations.md#memberearnings) | `GET /me/earnings` | `me earnings` | — | — |
| [`member.track`](https://services.dev/docs/reference/operations.md#membertrack) | `GET /me/track` | `me track` | — | — |
| [`member.subjects`](https://services.dev/docs/reference/operations.md#membersubjects) | `GET /me/subjects` | `me subjects` | — | — |
| [`member.login`](https://services.dev/docs/reference/operations.md#memberlogin) | `POST /owner/members/:id/login` | `members login <member>` | — | — |

### `member.join`

Join a Pool, email first and resumable: each call takes the answers it brings, one step of the application at a time, and once it's whole the verifier runs. The owner adds a member the same way.

Who may call: member session, owner session · effect: record · gate: person · idempotent: yes · returns: [`member`](https://services.dev/docs/reference/objects.md#member)

**Emits** [`member.applied`](https://services.dev/docs/reference/events.md#member.applied) · **Can answer** `not_found`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `email` | body | `string` | **Required.** The email the application starts from; the same one resumes it. |
| `answers` | body | `object` | The join fields this call answers, in the Pool's type words. |

```txt
→ POST /attorneys/join {"email":"grace@hopper.example","answers":{"name":"Grace Hopper","credential":"patent agent","number":"71,234","regions":["US-CA"],"areas":["patents"],"hours":10,"turnaround":"24h","min_fee":120}}   # the applicant, in one call
```

```json title="captured/member-wire.txt · 200 · mbr_7Hq2 · member · magic values: none"
{
  "id": "mbr_7Hq2",
  "object": "member",
  "livemode": false,
  "pool": "attorneys",
  "email": "grace@hopper.example",
  "join": {
    "step": 3,
    "of": 3,
    "next": [],
    "done": true
  },
  "application": {
    "name": "Grace Hopper",
    "credential": "patent agent",
    "number": "71,234",
    "regions": [
      "US-CA"
    ],
    "areas": [
      "patents"
    ],
    "hours": 10,
    "turnaround": "24h",
    "min_fee": 120
  },
  "verified": {
    "credentials": {
      "patent agent": [
        "US-CA"
      ]
    },
    "coverage": {
      "per_act": 1000000,
      "until": "2027-10-25T14:02:11.000Z"
    },
    "at": "2026-10-25T14:02:11.000Z",
    "by": "fixture",
    "fresh_until": "2026-11-24T14:02:11.000Z",
    "fresh": true
  },
  "availability": "on",
  "metadata": {},
  "simulated": true,
  "login": "http://localhost:8787/attorneys/login?t=ms_bU0qS0IkbnMxlMCPdfIc9j"
}
```

### `member.verify`

The verifier writes what is true of a member: each credential and its regions, and coverage in force. It is the only writer of verified facts, on its own write path with its `vt_` token, and every write joins the member's recheck journal.

Who may call: [`vt_`](https://services.dev/docs/reference/credentials.md#vt_) · effect: record · gate: access · idempotent: yes · returns: `member`

**Can answer** `not_found`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `facts` | body | `object` | **Required.** \{ credentials: \{ `<credential>:` `[<region>,` …] \}, coverage?: \{ per_act, until \} \}. |

```txt
→ POST /attorneys/members/mbr_7Hq2/verify {"facts":{"credentials":{"bar admission":["US-CA"]},"coverage":{"per_act":1000000,"until":"2026-10-01T00:00:00Z"}}}
```

```txt title="captured/clock-vacant.txt · 200 · mbr_7Hq2 · attorneys · grace@example.com · verified: bar admission in US-CA · on · magic values: member = mbr_eligible"
← 200 · mbr_7Hq2 · attorneys · grace@example.com · verified: bar admission in US-CA · on
```

### `member.availability`

Set your availability: on, off or away. Only a member who is on is offered work.

Who may call: member session · effect: record · gate: person · idempotent: yes · returns: `member`

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `availability` | body | `string` | **Required.** on, off or away. |

### `member.ledger.get`

Read your conflicts ledger: hashes only, salted with your id, never pooled and never seen by the brand.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `list`

```txt
→ GET /me/ledger   # the owner console, with no member session
```

```txt title="captured/member-ledger.txt · magic values: client.email = signs@example.test, member = mbr_eligible"
← 403 needs_a_person: Only a signer, in their own session at http://localhost:8787/attorneys, does this. A key never can.
```

### `member.ledger.add`

Add a party to your conflicts ledger: its name, email and organization are each hashed before anything is kept. A Task's parties are checked against it, mechanically.

Who may call: member session · effect: record · gate: person · idempotent: yes · returns: `list`

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `name` | body | `string` | The party's name. |
| `email` | body | `string` | The party's email. |
| `org` | body | `string` | The party's organization. |

```txt
→ POST /me/ledger {"name":"Initech","org":"Initech Inc"}   # as mbr_eligible
```

```json title="captured/member-ledger.txt · 200 · magic values: client.email = signs@example.test, member = mbr_eligible"
{
  "object": "list",
  "data": [
    {
      "hash": "sha256:c54368f1ea6c537b0c017f2fdf6c2875087c2583225d9c1aa3542ead950c4436"
    },
    {
      "hash": "sha256:2097647525c65b3e441ae863872c7050af5c6cdc95bf87c9aa92983eb4f19941"
    }
  ],
  "has_more": false,
  "url": "/me/ledger",
  "simulated": true
}
```

### `member.ledger.checks`

Every check of your conflicts ledger: the Task it ran for, when, how many parties, and whether one matched. You see every one, and no one else sees any.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `list`

### `member.missed`

The open Tasks your own declarations kept from you, each naming the one (your minimum fee, your areas, your hours), and never one eligibility kept from you; with the last 30 days of your Pool, in a line.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `list`

### `member.earnings`

Your pay, act by act: when each accrued, when it is payable, the payout that moved it, and a line per brand. One payout account across every Pool and brand.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `list`

**Explained in** [Pay](https://services.dev/docs/people/pool/pay.md)

### `member.track`

Your track record, yours to carry: your counts per Pool and brand, with each Task's hash, signed by the door and naming no party.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `track_record`

**Explained in** [Track records](https://services.dev/docs/people/pool/track.md#yours-to-carry)

### `member.subjects`

The subjects where you are of record: you stay of record across jobs until you withdraw.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `list`

**Explained in** [Of record](https://services.dev/docs/people/of-record.md)

### `member.login`

Mint a member's own session on localhost, for the printed link and members login. Elsewhere it is their passkey's.

Who may call: owner session · effect: record · gate: person · idempotent: yes · returns: `session`

**Can answer** `not_found`

## Tasks

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`task.list`](https://services.dev/docs/reference/operations.md#tasklist) | `GET /tasks` | `tasks list` | — | — |
| [`task.get`](https://services.dev/docs/reference/operations.md#taskget) | `GET /tasks/:id` | `tasks get <task>` | — | — |
| [`task.sheet`](https://services.dev/docs/reference/operations.md#tasksheet) | `GET /tasks/:id/sheet` | `tasks sheet <task>` | — | — |
| [`task.pass`](https://services.dev/docs/reference/operations.md#taskpass) | `POST /tasks/:id/pass` | `tasks pass <task> <reason>` | — | — |
| [`task.claim`](https://services.dev/docs/reference/operations.md#taskclaim) | `POST /tasks/:id/claim` | `tasks claim <task>` | — | — |
| [`task.clear`](https://services.dev/docs/reference/operations.md#taskclear) | `POST /tasks/:id/clear` | `tasks clear <task>` | — | — |
| [`task.packet`](https://services.dev/docs/reference/operations.md#taskpacket) | `GET /tasks/:id/packet` | `tasks packet <task>` | — | — |
| [`task.release`](https://services.dev/docs/reference/operations.md#taskrelease) | `POST /tasks/:id/release` | `tasks release <task>` | — | — |
| [`task.offer`](https://services.dev/docs/reference/operations.md#taskoffer) | `POST /owner/tasks/:id/offer` | `tasks offer <task> <member>` | — | — |
| [`task.arrive`](https://services.dev/docs/reference/operations.md#taskarrive) | `POST /tasks/:id/arrive` | `tasks arrive <task>` | — | — |
| [`task.complete`](https://services.dev/docs/reference/operations.md#taskcomplete) | `POST /tasks/:id/complete` | `tasks complete <task> k=v …` | — | — |
| [`task.attest`](https://services.dev/docs/reference/operations.md#taskattest) | `POST /tasks/:id/attest` | `tasks attest <task> k=v …` | — | — |
| [`task.send_back`](https://services.dev/docs/reference/operations.md#tasksend_back) | `POST /tasks/:id/send_back` | `tasks send-back <task> <defects>` | — | — |
| [`task.decline`](https://services.dev/docs/reference/operations.md#taskdecline) | `POST /tasks/:id/decline` | `tasks decline <task> <memo>` | — | — |
| [`task.recuse`](https://services.dev/docs/reference/operations.md#taskrecuse) | `POST /tasks/:id/recuse` | `tasks recuse <task>` | — | — |
| [`task.extend`](https://services.dev/docs/reference/operations.md#taskextend) | `POST /tasks/:id/extend` | `tasks extend <task>` | — | — |
| [`task.contest`](https://services.dev/docs/reference/operations.md#taskcontest) | `POST /tasks/:id/contest` | `tasks contest <task> <reason>` | — | — |
| [`task.message`](https://services.dev/docs/reference/operations.md#taskmessage) | `POST /tasks/:id/messages` | `tasks message <task> <kind> <text>` | — | — |

### `task.list`

Your feed: every open Task offered to you that you can take, newest first, as teasers. The owner console reads every Task.

Also served at `GET /owner/tasks`.

Who may call: member session, owner session · effect: read · gate: person · idempotent: yes · returns: `list`

### `task.get`

Read a Task's teaser: its teaser fields, the estimate, the rate and when it's due. Never a party name.

Also served at `GET /owner/tasks/:id`.

Who may call: member session, owner session · effect: read · gate: person · idempotent: yes · returns: [`task`](https://services.dev/docs/reference/objects.md#task)

**Can answer** `not_found`, [`task_taken`](https://services.dev/docs/reference/errors.md#task_taken)

### `task.sheet`

Read a Task's sheet: the teaser, its sheet fields with the parties, and what you attest to clear it. One member at a time, and every view is logged.

Who may call: member session · effect: record · gate: person · idempotent: yes · returns: `sheet`

**Can answer** `not_found`, [`ineligible`](https://services.dev/docs/reference/errors.md#ineligible), `task_taken`

### `task.pass`

Pass on a Task: free, with an optional reason, counted against the Task and never against you. Under `cascade` it goes to the next member at once.

Who may call: member session · effect: record · gate: person · idempotent: yes · returns: `task`

**Emits** [`task.offered`](https://services.dev/docs/reference/events.md#task.offered) · **Can answer** `not_found`, `task_taken`

| Takes | In | Type | Says |
|---|---|---|---|
| `reason` | body | `string` | Why, if you want to say. |

### `task.claim`

Claim a Task: the eligibility predicates run in order, and it is held for you until the Pool's hold runs out.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** [`task.claimed`](https://services.dev/docs/reference/events.md#task.claimed) · **Can answer** `not_found`, `ineligible`, `task_taken`

```txt
→ POST /tasks/tsk_7Hq2/claim
```

```txt title="captured/grow-role.txt · magic values: none"
← tsk_7Hq2 · claimed · translator · sign · * · $30.00 · 20 min · $90.00 an hour · due 2026-10-27 16:02
```

### `task.clear`

Clear a held Task: the conflict check runs again, and you attest each statement on its sheet. The packet opens, and the job waits on you.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** [`job.paused`](https://services.dev/docs/reference/events.md#job.paused), `task.offered` · **Can answer** `not_found`, `ineligible`, `task_taken`

| Takes | In | Type | Says |
|---|---|---|---|
| `attest` | body | `array` | **Required.** Each statement on the sheet, word for word. |

### `task.packet`

Read a cleared Task's packet: the summary, the AI's artifacts and their diff, the review reports, the checklist, the record, the signals, the clock and the hash you attest.

Who may call: member session · effect: read · gate: person · idempotent: yes · returns: `packet`

**Can answer** `not_found`, [`not_cleared`](https://services.dev/docs/reference/errors.md#not_cleared)

### `task.release`

Give a held Task back before it is done: it opens again at the fee it was posted at, and the dispatch runs on.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** `job.paused`, `task.offered` · **Can answer** `not_found`, `task_taken`

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md)

### `task.offer`

Name the member a Task goes to: the owner's pick, a direct, or an override of any dispatch. They must be eligible.

Who may call: owner session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** `task.offered` · **Can answer** `not_found`, `ineligible`, `task_taken`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `member` | body | `string` | **Required.** The mbr_ id of the member it goes to. |

### `task.arrive`

Check in at a work-register Task's site: the device's fix must fall inside the Pool's site radius, between its early and late marks around the window. Counted, it reads `on_site`.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Can answer** `not_found`, `task_taken`, [`not_on_site`](https://services.dev/docs/reference/errors.md#not_on_site)

| Takes | In | Type | Says |
|---|---|---|---|
| `fix` | body | `object` | **Required.** Where the device is: \{ lat, lng, accuracy_m \}. The portal sends it; the CLI reads it from the device. |

```txt
→ POST /tasks/tsk_7Hq2/arrive {"fix":{"lat":48.8556,"lng":2.3605,"accuracy_m":12}}   # as mbr_eligible, 3 Nov 13:56
```

```txt title="captured/pool-register.txt · 200 · tsk_7Hq2 · on_site · 38 m from the site · 4 min before the window · magic values: client.email = signs@example.test, member = mbr_eligible"
← 200 · tsk_7Hq2 · on_site · 38 m from the site · 4 min before the window
```

**Explained in** [A Pool's register](https://services.dev/docs/people/pool/register.md#where-and-when-a-check-in-counts)

### `task.complete`

Complete a work-register Task you are on site for: every evidence item the step names, each stamped with time and place and hashed together, for the gate to commit. Paid per completed Task.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** `step.proposed`, `step.committed`, `step.refused`, `pay.accrued` · **Can answer** `not_found`, `not_on_site`, [`evidence_missing`](https://services.dev/docs/reference/errors.md#evidence_missing), `input_invalid`, `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `check_out` | body | `object` | The check-out: \{ fix \}, inside the site radius. |
| `sign_off` | body | `string` | The ask_ id of the party's sign-off, when the step's evidence names one. |
| `items` | body | `object` | Each other evidence item by name: a photo, a scan or a checklist, as uploaded files or \{ `<item>:` true \}. |
| `party_absent` | body | `boolean` | On site with no party after the Pool's `site.grace`: completes the Task as `party_absent`, and the full fee accrues. |

```txt
→ POST /tasks/tsk_7Hq2/complete {"check_out":{"fix":{"lat":48.8557,"lng":2.3604,"accuracy_m":10}},"sign_off":"ask_7Hq4"}   # as mbr_eligible, 3 Nov 15:04
```

```txt title="captured/pool-register.txt · 200 · tsk_7Hq2 · done · completed · sha256:5be1c0a7d29e · magic values: client.email = signs@example.test, member = mbr_eligible"
  15:04 · step.committed · attend · person · certified interpreter · FR · mbr_eligible
  15:04 · pay.accrued · mbr_eligible · complete · $90.00
  15:04 · job.charged · job_7Hq2 · $140.00 · released on the client's sign-off
```

**Explained in** [A Pool's register](https://services.dev/docs/people/pool/register.md#where-and-when-a-check-in-counts)

### `task.attest`

Attest the Task you claimed and cleared: your proposal, for the gate to commit, freezing who, the credential, where, the hash and the rules on the record. With reservation, qualified: it travels with the output. Paid.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** `step.proposed`, `step.committed`, `step.refused`, [`pay.accrued`](https://services.dev/docs/reference/events.md#pay.accrued), [`message.adopted`](https://services.dev/docs/reference/events.md#message.adopted), `message.sent` · **Can answer** `not_found`, `not_cleared`, `guard_refused`, `needs_a_person`, [`needs_direction`](https://services.dev/docs/reference/errors.md#needs_direction), [`reserved`](https://services.dev/docs/reference/errors.md#reserved), `input_invalid`, `end_not_allowed`

| Takes | In | Type | Says |
|---|---|---|---|
| `output` | body | `object` | Your step's output, when you propose one. |
| `checklist` | body | `object` | Each checklist item you affirm: \{ `<item>:` true \}, or a list. |
| `reservation` | body | `string` | Qualified: the reservation you write, kept as its own file with the output. |
| `text` | body | `string` | Held advice: your edit of the answer, sent under your name in place of the draft. |

```txt
→ POST /tasks/tsk_7Hq2/attest
```

```txt title="captured/grow-role.txt · magic values: none"
← tsk_7Hq2 · done · translator · sign · * · $30.00 · attested · 20 min · $90.00 an hour · due 2026-10-27 16:02
```

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md)

### `task.send_back`

Send the work back with its defects: paid, and the job returns to the step you name, by default the one before. That work is redone and the step posts a new Task.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** `job.sent_back`, `pay.accrued` · **Can answer** `not_found`, `not_cleared`, [`defects_required`](https://services.dev/docs/reference/errors.md#defects_required), `input_invalid`, `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `defects` | body | `string` | **Required.** What is wrong: required. |
| `to` | body | `string` | The step it returns to; the one before when left out. |

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md)

### `task.decline`

Decline the Task, with a reasoned memo only the client reads: paid and never scored. The job waits on the client's direction: another member, or an end.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** [`task.declined`](https://services.dev/docs/reference/events.md#task.declined), `pay.accrued`, `job.paused`, `ask.opened` · **Can answer** `not_found`, `not_cleared`, [`memo_required`](https://services.dev/docs/reference/errors.md#memo_required), `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `memo` | body | `string` | **Required.** Why you decline: required, and the client alone reads it. |

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md), [Declines and direction](https://services.dev/docs/people/direction.md)

### `task.recuse`

Step aside from a Task you hold, for a conflict: nothing accrues and nothing is scored, and it reopens at once. The parties you name join your own ledger, and you are screened from every Task on the subject. The client reads that a professional stepped aside, never why.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** [`task.recused`](https://services.dev/docs/reference/events.md#task.recused), `job.paused`, `task.offered` · **Can answer** `not_found`, `task_taken`, `input_invalid`, `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `parties` | body | `array` | Who the conflict is with: each a name, or \{ name, email, org \}. Each is hashed into your own ledger, and no one else reads it. |

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md)

### `task.extend`

Extend your hold on a Task, once: by as long again as it first held, never past its due date.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `task`

**Emits** [`task.extended`](https://services.dev/docs/reference/events.md#task.extended) · **Can answer** `not_found`, `task_taken`, `input_invalid`

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md)

### `task.contest`

Contest a send-back your attested work received, with a reason. A peer with the same credential rules it on a reserved act; staff rule the rest.

Also served at `POST /owner/tasks/:id/contest`.

Who may call: member session, owner session · effect: record · gate: person · idempotent: yes · returns: `task`

**Emits** [`task.contested`](https://services.dev/docs/reference/events.md#task.contested), [`task.ruled`](https://services.dev/docs/reference/events.md#task.ruled) · **Can answer** `not_found`, [`not_contestable`](https://services.dev/docs/reference/errors.md#not_contestable), `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `reason` | body | `string` | Why the send-back was wrong: the member's. |
| `ruling` | body | `string` | upheld or overturned: the owner's or a staff role's, on an act no row reserves. |

**Explained in** [What a person can do](https://services.dev/docs/people/acts.md), [Track records](https://services.dev/docs/people/pool/track.md#contests)

### `task.message`

Reach the client through the job's thread, by kind: a question (an answer ask that pauses your hold and the step's clock until they reply), a note (no reply expected), or a flag (a named signal the owner sees, which never blocks). Their email and phone never reach you, nor yours them.

Who may call: member session · effect: record · gate: person · idempotent: with a key · returns: `message`

**Emits** `ask.opened`, `thread.posted`, `job.paused`, `signal.raised` · **Can answer** `not_found`, `not_cleared`, `input_invalid`, [`question_open`](https://services.dev/docs/reference/errors.md#question_open), `job_ended`, `talk_off`

| Takes | In | Type | Says |
|---|---|---|---|
| `kind` | body | `string` | **Required.** question, note or flag. |
| `text` | body | `string` | **Required.** Your words, as the client reads them. |
| `signal` | body | `string` | A flag: the fact, in snake_case, such as missed_fact. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

**Explained in** [Talk](https://services.dev/docs/service/talk.md), [What a person can do](https://services.dev/docs/people/acts.md)

## Subjects

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`subject.list`](https://services.dev/docs/reference/operations.md#subjectlist) | `GET /subjects` | `subjects list` | never | never |
| [`subject.get`](https://services.dev/docs/reference/operations.md#subjectget) | `GET /subjects/:id` | `subjects get <subject>` | never | never |
| [`subject.withdraw`](https://services.dev/docs/reference/operations.md#subjectwithdraw) | `POST /subjects/:id/withdraw` | `subjects withdraw <subject> <successor>` | — | — |

### `subject.list`

The subjects your jobs joined: each one keyed by an input or a receipt, with its jobs and who is of record on it.

Also served at `GET /owner/subjects`.

Never on MCP. An agent reads a subject's clocks with list_clocks and get_clock; the subject itself is the buyer's record, read over HTTP and the CLI.

Never on SDK. The SDK reads a subject's clocks with clocks.list() and clocks.get(); the subject itself is the buyer's record, read over HTTP and the CLI.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `input_invalid`

Takes `limit`, `starting_after`, as [the calls that share it](#takes-calls-share) do.

### `subject.get`

Read a subject: its key, every job that touched it, its insert-only history, its kept facts with every value and the job that wrote it, its periods, and who is of record, or that the seat is vacant and who covers it.

Also served at `GET /owner/subjects/:id`.

Never on MCP. An agent reads a subject's clocks with list_clocks and get_clock; the subject itself is the buyer's record, read over HTTP and the CLI.

Never on SDK. The SDK reads a subject's clocks with clocks.list() and clocks.get(); the subject itself is the buyer's record, read over HTTP and the CLI.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: [`subject`](https://services.dev/docs/reference/objects.md#subject)

**Can answer** `not_found`

```txt
→ GET /subjects/sbj_7Hq2
```

```json title="captured/service-subject.txt · 200 · magic values: none"
{
  "id": "sbj_7Hq2",
  "object": "subject",
  "livemode": false,
  "kind": "account",
  "key": {
    "field": "account",
    "value": "acme"
  },
  "created": "2026-10-25T14:02:11.000Z",
  "jobs": [
    "job_7Hq2",
    "job_7Hq3"
  ],
  "of_record": null,
  "history": [
    {
      "at": "2026-10-25T14:02:11.000Z",
      "type": "opened",
      "job": "job_7Hq2"
    },
    {
      "at": "2026-10-25T14:02:11.000Z",
      "type": "joined",
      "job": "job_7Hq3"
    }
  ],
  "facts": {
    "glossary": {
      "latest": {
        "value": [
          "Lessee",
          "Lessor",
          "Premises"
        ],
        "job": "job_7Hq3",
        "step": "draft",
        "at": "2026-10-25T14:02:11.000Z",
        "period": "2026"
      },
      "periods": {
        "2025": {
          "value": [
            "Lessee",
            "Lessor"
          ],
          "job": "job_7Hq2",
          "step": "draft",
          "at": "2026-10-25T14:02:11.000Z",
          "period": "2025"
        },
        "2026": {
          "value": [
            "Lessee",
            "Lessor",
            "Premises"
          ],
          "job": "job_7Hq3",
          "step": "draft",
          "at": "2026-10-25T14:02:11.000Z",
          "period": "2026"
        }
      },
      "history": [
        {
          "value": [
            "Lessee",
            "Lessor"
          ],
          "job": "job_7Hq2",
          "step": "draft",
          "at": "2026-10-25T14:02:11.000Z",
          "period": "2025"
        },
        {
          "value": [
            "Lessee",
            "Lessor",
            "Premises"
          ],
          "job": "job_7Hq3",
          "step": "draft",
          "at": "2026-10-25T14:02:11.000Z",
          "period": "2026"
        }
      ]
    }
  },
  "periods": [
    {
      "period": "2025",
      "service": "glossary",
      "job": "job_7Hq2",
      "status": "accepted"
    },
    {
      "period": "2026",
      "service": "glossary",
      "job": "job_7Hq3",
      "status": "accepted"
    }
  ],
  "simulated": true
}
```

### `subject.withdraw`

Withdraw as the member of record, naming your successor: a member who holds the role's credential takes the seat.

Who may call: member session · effect: act · gate: person · idempotent: yes · returns: `subject`

**Can answer** `not_found`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `successor` | body | `string` | **Required.** The mbr_ id of the member who takes the seat. |

**Explained in** [Of record](https://services.dev/docs/people/of-record.md)

## Clocks

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`clock.list`](https://services.dev/docs/reference/operations.md#clocklist) | `GET /clocks` | `clocks list` | `list_clocks` | `clocks.list()` |
| [`clock.get`](https://services.dev/docs/reference/operations.md#clockget) | `GET /clocks/:id` | `clocks get <clock>` | `get_clock` | `clocks.get()` |
| [`clock.ack`](https://services.dev/docs/reference/operations.md#clockack) | `POST /clocks/:id/ack` | `clocks ack <clock>` | never | `clocks.ack()` |
| [`clock.satisfy`](https://services.dev/docs/reference/operations.md#clocksatisfy) | `POST /clocks/:id/satisfy` | `clocks satisfy <clock>` | never | `clocks.satisfy()` |
| [`clock.choose`](https://services.dev/docs/reference/operations.md#clockchoose) | `POST /clocks/:id/choose` | `clocks choose <clock> <option>` | never | `clocks.choose()` |
| [`clock.assign`](https://services.dev/docs/reference/operations.md#clockassign) | `POST /owner/clocks/:id/assign` | `clocks assign <clock> <member>` | — | — |

### `clock.list`

The clocks your jobs and their subjects run: each one's date, who answers for it, its rungs and where it stands. A member reads the ones they answer for.

Also served at `GET /owner/clocks`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session, member session · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `job` | query | `string` | Only the clocks this job reads: its own and its subject's. |
| `subject` | query | `string` | Only this subject's: sbj_…. |
| `owner` | query | `string` | Only the clocks this owner answers for: buyer, brand, a role, a member's mbr_ id, or none. |
| `status` | query | `string` | open, due, satisfied or chosen. |

Also takes `limit`, `starting_after`, as [the calls that share it](#takes-calls-share) do.

```txt
→ GET /clocks   # in mbr_eligible's own session
```

```txt title="captured/clock-calls.txt · 200 · OK · 1 clock · magic values: member = mbr_lapsed_credential, member = mbr_eligible"
  clk_7Hq4 · reply_by · due 2026-12-20 · mbr_eligible · open
```

**Explained in** [Clocks](https://services.dev/docs/service/clocks.md)

### `clock.get`

Read a clock: its date, who answers for it, each rung and when it falls due, what moved it, and at its date the owner's options.

Also served at `GET /owner/clocks/:id`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session, member session · effect: read · gate: access · idempotent: yes · returns: [`clock`](https://services.dev/docs/reference/objects.md#clock)

**Can answer** `not_found`

```txt
→ GET /clocks/clk_7Hq2
```

```txt title="captured/clock-run-held.txt · magic values: none"
← clk_7Hq2 · deadline · due 2026-11-24 · buyer · due · extend held: refused:needs_grant · choose: extend, withdraw, in writing
```

**Explained in** [Clocks](https://services.dev/docs/service/clocks.md)

### `clock.ack`

Acknowledge a clock you answer for: inside its ack daily window, the day's acknowledgement, so the day is never missed. Only its owner can.

Also served at `POST /owner/clocks/:id/ack`.

Never on MCP. Only a clock's owner acts on it, with their own key or in their own app: an agent reads it with get_clock, and a call from anyone else answers needs_the_owner with the owner's app as next.hand_to_person.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session, member session · effect: record · gate: person · idempotent: with a key · returns: `clock`

**Emits** [`clock.acked`](https://services.dev/docs/reference/events.md#clock.acked) · **Can answer** `not_found`, [`needs_the_owner`](https://services.dev/docs/reference/errors.md#needs_the_owner), [`clock_closed`](https://services.dev/docs/reference/errors.md#clock_closed), [`clock_moved_on`](https://services.dev/docs/reference/errors.md#clock_moved_on)

Takes `due`, `key`, as [the calls that share it](#takes-calls-share) do.

```txt
→ POST /clocks/clk_7Hq4/ack {}   # in mbr_eligible's own session
```

```txt title="captured/clock-calls.txt · 200 · clk_7Hq4 · reply_by · due 2026-12-20 · mbr_eligible · open · magic values: member = mbr_lapsed_credential, member = mbr_eligible"
← 200 · clk_7Hq4 · reply_by · due 2026-12-20 · mbr_eligible · open
```

**Explained in** [Clocks](https://services.dev/docs/service/clocks.md)

### `clock.satisfy`

Close a clock you answer for, on evidence on the record: a file you send, or a receipt or file the job holds. A recurring clock rolls on to its next date.

Also served at `POST /owner/clocks/:id/satisfy`.

Never on MCP. Only a clock's owner acts on it, with their own key or in their own app: an agent reads it with get_clock, and a call from anyone else answers needs_the_owner with the owner's app as next.hand_to_person.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session, member session · effect: act · gate: person · idempotent: yes · returns: `clock`

**Emits** [`clock.satisfied`](https://services.dev/docs/reference/events.md#clock.satisfied), [`clock.recurred`](https://services.dev/docs/reference/events.md#clock.recurred), [`clock.rolled`](https://services.dev/docs/reference/events.md#clock.rolled) · **Can answer** `not_found`, `needs_the_owner`, [`evidence_required`](https://services.dev/docs/reference/errors.md#evidence_required), [`evidence_used`](https://services.dev/docs/reference/errors.md#evidence_used), `clock_closed`, `clock_moved_on`

| Takes | In | Type | Says |
|---|---|---|---|
| `evidence` | body | `string` | What is on the record already: the id of a receipt (rcp_…) or the sha256 of a file the job holds. |
| `file` | body | `object` | Or the proof itself, \{ url, name \}: kept on the job by its sha256. |

Also takes `due`, as [the calls that share it](#takes-calls-share) do.

```txt
→ POST /owner/clocks/clk_7Hq3/satisfy {}
```

```txt title="captured/clock-calls.txt · magic values: member = mbr_lapsed_credential, member = mbr_eligible"
← 422 evidence_required: lodge closes on evidence on the record, and no evidence came with it. Send file, { url, name }, or evidence: the id of a receipt or the sha256 of a file job_7Hq2 holds.
```

**Explained in** [Clocks](https://services.dev/docs/service/clocks.md)

### `clock.choose`

Choose one of a clock's lapse options at its date, unsatisfied, and the clock closes. A step's name runs that step, through the gate.

Also served at `POST /owner/clocks/:id/choose`.

Never on MCP. Only a clock's owner acts on it, with their own key or in their own app: an agent reads it with get_clock, and a call from anyone else answers needs_the_owner with the owner's app as next.hand_to_person.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session, member session · effect: act · gate: person · idempotent: yes · returns: `clock`

**Emits** [`clock.chosen`](https://services.dev/docs/reference/events.md#clock.chosen), `step.committed` · **Can answer** `not_found`, `needs_the_owner`, [`clock_not_due`](https://services.dev/docs/reference/errors.md#clock_not_due), `clock_closed`, `clock_moved_on`, `input_invalid`, `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `option` | body | `string` | **Required.** One of the clock's options, as written: a held run rung's step first, then its lapse options. |

Also takes `due`, as [the calls that share it](#takes-calls-share) do.

**Explained in** [A date the world sets](https://services.dev/docs/grow/clock.md), [Clocks](https://services.dev/docs/service/clocks.md)

### `clock.assign`

Assign the member who answers for a clock, and it has an owner again: one who holds its role's credential, or for the brand's clock a member of its Pools.

Also served at `POST /clocks/:id/assign`.

Who may call: owner session · effect: act · gate: person · idempotent: yes · returns: `clock`

**Emits** [`clock.assigned`](https://services.dev/docs/reference/events.md#clock.assigned) · **Can answer** `not_found`, `ineligible`, `clock_closed`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `member` | body | `string` | **Required.** The mbr_ id of the member who answers for it. |

```txt
→ POST /owner/clocks/clk_7Hq2/assign {"member":"mbr_eligible"}
```

```txt title="captured/clock-vacant.txt · 200 · clk_7Hq2 · renew · due 2026-12-24 · mbr_eligible · open · on the subject · magic values: member = mbr_eligible"
← 200 · clk_7Hq2 · renew · due 2026-12-24 · mbr_eligible · open · on the subject
```

**Explained in** [Clocks](https://services.dev/docs/service/clocks.md), [Dates at risk](https://services.dev/docs/operate/dates.md)

## Subscriptions

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`subscription.list`](https://services.dev/docs/reference/operations.md#subscriptionlist) | `GET /subscriptions` | `subscriptions list` | `list_subscriptions` | `subscriptions.list()` |
| [`subscription.get`](https://services.dev/docs/reference/operations.md#subscriptionget) | `GET /subscriptions/:id` | `subscriptions get <subscription>` | `get_subscription` | `subscriptions.get()` |
| [`subscription.cancel`](https://services.dev/docs/reference/operations.md#subscriptioncancel) | `POST /subscriptions/:id/cancel` | `subscriptions cancel <subscription>` | `cancel_subscription` | `subscriptions.cancel()` |

### `subscription.list`

Your subscriptions: each one's cycle, its allowances used and included, what it mints next, and the jobs it minted.

Also served at `GET /owner/subscriptions`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `service` | query | `string` | Only the subscriptions to this Service, by its id. |
| `subject` | query | `string` | Only the ones on this subject: sbj_…. |
| `status` | query | `string` | active, canceling or canceled. |

Also takes `limit`, `starting_after`, as [the calls that share it](#takes-calls-share) do.

**Explained in** [Schedules and triggers](https://services.dev/docs/service/subscriptions.md)

### `subscription.get`

Read a subscription: its cycle's start and end, each allowance used of included, each schedule date and when it next mints, each event it listens for, its charges, and every job it minted, offered or was refused.

Also served at `GET /owner/subscriptions/:id`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: [`subscription`](https://services.dev/docs/reference/objects.md#subscription)

**Can answer** `not_found`

```bash
curl http://localhost:8787/subscriptions/sbn_7Hq2
```

```json title="captured/grow-subscription-tabs.txt · magic values: none"
{
  "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
    }
  },
  "schedule": [
    {
      "name": "monthly",
      "service": "translation",
      "next": "2026-11-01T00:00:00.000Z"
    }
  ],
  "events": [
    {
      "event": "page.changed",
      "service": "translation"
    }
  ],
  "charges": [
    {
      "cycle": 1,
      "usd": 149,
      "at": "2026-10-25T14:02:11.000Z"
    }
  ],
  "minted": [],
  "jobs": [],
  "metadata": {},
  "created": "2026-10-25T14:02:11.000Z",
  "simulated": true
}
```

**Explained in** [Schedules and triggers](https://services.dev/docs/service/subscriptions.md)

### `subscription.cancel`

Cancel a subscription: it runs to the end of the cycle paid for, never renews, and nothing mints after. Twice changes nothing.

Also served at `POST /owner/subscriptions/:id/cancel`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session · effect: act · gate: access · idempotent: yes · returns: `subscription`

**Emits** [`subscription.canceled`](https://services.dev/docs/reference/events.md#subscription.canceled) · **Can answer** `not_found`

```bash
curl -X POST http://localhost:8787/subscriptions/sbn_7Hq2/cancel
```

```json title="captured/money-subscriptions-tabs.txt · 200 · magic values: none"
{
  "id": "sbn_7Hq2",
  "object": "subscription",
  "livemode": false,
  "service": "plan",
  "status": "canceling",
  "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
    }
  },
  "schedule": [
    {
      "name": "monthly",
      "service": "translation",
      "next": "2026-11-01T00:00:00.000Z"
    }
  ],
  "events": [
    {
      "event": "page.changed",
      "service": "translation"
    }
  ],
  "charges": [
    {
      "cycle": 1,
      "usd": 149,
      "at": "2026-10-25T14:02:11.000Z"
    }
  ],
  "minted": [],
  "jobs": [],
  "ends_at": "2026-11-25T14:02:11.000Z",
  "metadata": {},
  "created": "2026-10-25T14:02:11.000Z",
  "simulated": true
}
```

**Explained in** [Schedules and triggers](https://services.dev/docs/service/subscriptions.md)

## Asks

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`ask.get`](https://services.dev/docs/reference/operations.md#askget) | `GET /asks/:id` | `asks get <ask>` | — | — |
| [`ask.complete`](https://services.dev/docs/reference/operations.md#askcomplete) | `POST /asks/:id` | — | — | — |
| [`ask.revoke`](https://services.dev/docs/reference/operations.md#askrevoke) | `POST /asks/:id/revoke` | — | — | — |
| [`ask.reissue`](https://services.dev/docs/reference/operations.md#askreissue) | `POST /asks/:id/reissue` | `asks reissue <ask>` | `reissue_ask` | `asks.reissue()` |

### `ask.get`

Read an ask: its party, what it asks, its status and expiry, its items, and its link while it is open.

Who may call: [party link](https://services.dev/docs/reference/credentials.md#party-link), owner session · effect: read · gate: party · idempotent: yes · returns: [`ask`](https://services.dev/docs/reference/objects.md#ask)

**Can answer** `not_found`

```bash
curl http://localhost:8787/asks/ask_7Hq2
```

```json title="captured/ask.txt · magic values: none"
{
  "id": "ask_7Hq2",
  "object": "ask",
  "job": "job_7Hq2",
  "step": "glossary",
  "to": "answer",
  "party": "buyer",
  "what_hash": null,
  "status": "open",
  "expires_at": "2026-11-01T14:02:11.000Z",
  "url": "http://localhost:8787/a/ask_7Hq2?t=8FaWT5EoCj4h-uMyjlXp3_",
  "livemode": false,
  "simulated": true,
  "items": [
    {
      "id": "terms",
      "label": "Which terms stay in English?",
      "status": "open"
    }
  ]
}
```

### `ask.complete`

The party acts, through their own link: answer, upload, sign, consent, decide, or decline. An agent holding their delegated key may answer and upload; nothing else may act for them.

Who may call: party link · effect: act · gate: party · idempotent: with a key · returns: `ask`

**Emits** [`item.done`](https://services.dev/docs/reference/events.md#item.done), [`ask.done`](https://services.dev/docs/reference/events.md#ask.done), [`consent.given`](https://services.dev/docs/reference/events.md#consent.given), [`grant.given`](https://services.dev/docs/reference/events.md#grant.given), [`ask.voided`](https://services.dev/docs/reference/events.md#ask.voided), `ask.opened`, `step.proposed`, `step.committed`, `step.refused`, `job.canceled`, `job.ended`, `charge.voided`, `charge.collected`, `charge.earned` · **Can answer** `not_found`, `needs_the_party`, [`ask_expired`](https://services.dev/docs/reference/errors.md#ask_expired), [`ask_voided`](https://services.dev/docs/reference/errors.md#ask_voided), [`signature_void`](https://services.dev/docs/reference/errors.md#signature_void), `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `t` | query | `string` | The party link's token. Only the party holds it. |
| `answer` | body | `string` | An answer. |
| `file` | body | `object` | An upload: \{ url, name \}. |
| `items` | body | `object` | Each item by id: \{ answer \}, \{ waive \}, or \{ substitute \}. |
| `choice` | body | `string` | A decide ask's choice. |
| `member` | body | `string` | A direct ask's member: the one the client names. |
| `end` | body | `string` | A direct ask on a declined job: the end the client chooses instead of another member. |
| `decline` | body | `boolean` | Decline instead. |
| `reason` | body | `string` | Why, when declining. |

```txt
→ POST /asks/ask_7Hq3?t=… {"answer":"Yes, signed on 1 March. Text me at (415) 555-0199."}
```

```txt title="captured/ask-the-client.txt · magic values: none"
← 200 done
  20:02 · thread.posted · msg_7Hq3 · reply · re msg_7Hq2 · ask_7Hq3 · sha256:07bc91c3…
```

### `ask.revoke`

The party revokes a grant they gave, through their own link: it stops being live at once, and a step that needs it is refused needs_grant until they grant it again. No key, delegated or not, revokes it for them.

Who may call: party link · effect: act · gate: party · idempotent: yes · returns: `ask`

**Emits** [`grant.revoked`](https://services.dev/docs/reference/events.md#grant.revoked) · **Can answer** `not_found`, `needs_the_party`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `t` | query | `string` | The party link's token. Only the party holds it. |

```txt
→ POST /asks/ask_7Hq3/revoke   # x-services-test-dlg: dlg_test_4Rb1
```

```txt title="captured/service-grant.txt · magic values: none"
← 403 needs_the_party: Only Ada Lovelace can revoke the access grant, through their own link. Hand them the link in next.
```

**Explained in** [Grants and decisions](https://services.dev/docs/service/grants.md)

### `ask.reissue`

Reissue an open ask's link, to hand to its party again. The old one stops working at once.

Also served at `POST /asks/:id/link`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, owner session · effect: record · gate: access · idempotent: with a key · returns: `ask`

**Emits** [`ask.relinked`](https://services.dev/docs/reference/events.md#ask.relinked) · **Can answer** `not_found`, `ask_expired`, `ask_voided`

Takes `key`, as [the calls that share it](#takes-calls-share) do.

```bash
curl -X POST http://localhost:8787/asks/ask_7Hq2/reissue -H 'content-type: application/json' -d '{}'
```

```json title="captured/tab-relink.txt · 200 · magic values: none"
{
  "id": "ask_7Hq2",
  "object": "ask",
  "job": "job_7Hq3",
  "step": "sign",
  "to": "sign",
  "party": {
    "name": "Ada Lovelace",
    "email": "ada@example.com"
  },
  "what_hash": "sha256:a0d68ba4f62ae94932c99dfcdf2a187424bb307a60150ebae98d6f209f40d6e3",
  "status": "open",
  "expires_at": "2026-11-01T14:02:11.000Z",
  "url": "http://localhost:8787/a/ask_7Hq2?t=cHGjprSn08ODyOp_8zLIVH",
  "livemode": false,
  "simulated": true
}
```

## Files

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`job.files.list`](https://services.dev/docs/reference/operations.md#jobfileslist) | `GET /jobs/:id/files` | `jobs files list <job>` | never | `jobs.files.list()` |
| [`job.files.put`](https://services.dev/docs/reference/operations.md#jobfilesput) | `PUT /jobs/:id/files/:sha256` | `jobs files put <job> <path>` | never | `jobs.files.put()` |
| [`job.files.get`](https://services.dev/docs/reference/operations.md#jobfilesget) | `GET /jobs/:id/files/:sha256` | `jobs files get <job> <sha256>` | never | `jobs.files.get()` |

### `job.files.list`

The files a job holds, each by its sha256: the ones sent in, each step's artifacts and each receipt's file.

Never on MCP. A file is bytes: send it over HTTP or the SDK, and name it by URL in an order.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `not_found`

### `job.files.put`

Put a file in a job's documents plane under its own sha256, or POST it and the brand hashes it. The same file sent twice is stored once.

Also served at `POST /jobs/:id/files`.

Never on MCP. A file is bytes: send it over HTTP or the SDK, and name it by URL in an order.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: record · gate: access · idempotent: yes · returns: `file`

**Can answer** `not_found`, `input_invalid`

**Explained in** [Sending to outside systems](https://services.dev/docs/service/submit.md)

### `job.files.get`

Read a file a job holds, by its sha256: one sent in, a step's artifact, or a receipt's file.

Never on MCP. A file is bytes: send it over HTTP or the SDK, and name it by URL in an order.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: `bytes`

**Can answer** `not_found`

```bash
curl http://localhost:8787/jobs/job_7Hq2/files/82d85d1a577fa13c7b2b5949e8b1aea6e5f307e492ad3a279c7618ab8cbb6956   # the receipt's file, by its sha256
```

```txt title="captured/submit.txt · magic values: none (metadata.sandbox.outside defaults to ack)"
Simulated receipt from the registry
rcp_7Hq2 · accepted
filed 2026-10-25T14:02:11.000Z
```

**Explained in** [Sending to outside systems](https://services.dev/docs/service/submit.md)

## Keys

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`key.create`](https://services.dev/docs/reference/operations.md#keycreate) | `POST /keys` | — | — | — |
| [`key.raise`](https://services.dev/docs/reference/operations.md#keyraise) | `POST /keys/:key` | — | — | — |

### `key.create`

Get a test key: on localhost, the one dev printed, with its cap. Every call it makes is in the sandbox, and a live key is a person's to make, at checkout.

Who may call: anyone · effect: record · gate: none · idempotent: yes · returns: `key`

### `key.raise`

A person raises the buyer key's cap, at checkout.

Who may call: party link · effect: act · gate: party · idempotent: with a key · returns: `text`

## Test clocks

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`test_clock.create`](https://services.dev/docs/reference/operations.md#test_clockcreate) | `POST /test_clocks` | `test-clocks create <frozen_at>` | `create_test_clock` | `testClocks.create()` |
| [`test_clock.advance`](https://services.dev/docs/reference/operations.md#test_clockadvance) | `POST /test_clocks/:id/advance` | `test-clocks advance <clock> <to>` | `advance_test_clock` | `testClocks.advance()` |

### `test_clock.create`

Make a test clock frozen at a time. An order attaches to it with test_clock, and nothing on it moves until you advance it.

Who may call: `sk_test_`, owner session · effect: record · gate: access · idempotent: with a key · returns: [`test_clock`](https://services.dev/docs/reference/objects.md#test_clock)

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `frozen_at` | body | `string` | **Required.** The time it starts at, such as 2026-10-25T14:02:11Z. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

```txt
→ POST /test_clocks {"frozen_at":"2026-10-25T14:02:11Z"}
```

```txt title="captured/grow-subscription.txt · magic values: none"
← tc_7Hq3
```

### `test_clock.advance`

Move a test clock to a time, or by a span: every job on it fires what falls due, in time order across jobs. Moving it where it has been fires nothing.

Who may call: `sk_test_`, owner session · effect: act · gate: access · idempotent: yes · returns: `advance`

**Emits** [`ask.reminded`](https://services.dev/docs/reference/events.md#ask.reminded), [`ask.expired`](https://services.dev/docs/reference/events.md#ask.expired), `ask.opened`, `ask.voided`, [`grant.expired`](https://services.dev/docs/reference/events.md#grant.expired), `signal.raised`, [`receipt.timed_out`](https://services.dev/docs/reference/events.md#receipt.timed_out), [`job.at_risk`](https://services.dev/docs/reference/events.md#job.at_risk), [`job.late`](https://services.dev/docs/reference/events.md#job.late), [`clock.opened`](https://services.dev/docs/reference/events.md#clock.opened), [`clock.unopened`](https://services.dev/docs/reference/events.md#clock.unopened), [`clock.noticed`](https://services.dev/docs/reference/events.md#clock.noticed), [`clock.escalated`](https://services.dev/docs/reference/events.md#clock.escalated), [`clock.ack_due`](https://services.dev/docs/reference/events.md#clock.ack_due), [`clock.ack_missed`](https://services.dev/docs/reference/events.md#clock.ack_missed), [`clock.ran`](https://services.dev/docs/reference/events.md#clock.ran), [`clock.moved`](https://services.dev/docs/reference/events.md#clock.moved), [`clock.due`](https://services.dev/docs/reference/events.md#clock.due), `clock.satisfied`, `clock.recurred`, `clock.rolled`, [`clock.vacant`](https://services.dev/docs/reference/events.md#clock.vacant), `job.accepted`, `job.ended`, `job.canceled`, `charge.earned`, `charge.released`, `charge.voided` · **Can answer** `not_found`, `input_invalid`, [`clock_behind`](https://services.dev/docs/reference/errors.md#clock_behind), [`clock_advancing`](https://services.dev/docs/reference/errors.md#clock_advancing)

| Takes | In | Type | Says |
|---|---|---|---|
| `to` | body | `string` | **Required.** A time, or a span from the clock's time, such as 7d. |

```txt
→ POST /test_clocks/default/advance {"to":"30h"}   # past the 24h hold
```

```txt title="captured/ask-the-client.txt · magic values: none"
← 2026-10-26T20:02:11.000Z
  task    cleared · mbr_7Hq2 · hold until 2026-10-26T14:02:11.000Z · paused since 2026-10-25T14:02:11.000Z
```

## Events

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`event.list`](https://services.dev/docs/reference/operations.md#eventlist) | `GET /events` | `events list` | `list_events` | `events.list()` |
| [`event.stream`](https://services.dev/docs/reference/operations.md#eventstream) | `GET /events/stream` | `listen [--forward-to <url>]` | never | `events.stream()` |
| [`event.inbound`](https://services.dev/docs/reference/operations.md#eventinbound) | `POST /events` | `events send <type>` | `send_event` | `events.send()` |

### `event.list`

The brand's events after a seq, in order, as the caller's audience sees them: a key reads the buyer's rows, the owner reads everything.

Also served at `GET /owner/events`.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_`, owner session · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `after` | query | `integer` | The seq to read after. |
| `limit` | query | `integer` | At most this many. |
| `type` | query | `string` | Only this event type. |
| `job` | query | `string` | Only this job's events. |

```bash
curl 'http://localhost:8787/events?after=0&limit=2'
```

```json title="captured/api-events-list.txt · 200 · magic values: none"
{
  "type": "OK",
  "object": "list",
  "data": [
    {
      "id": "evt_5gX1X2B5swzY61",
      "object": "event",
      "seq": 1,
      "type": "job.ordered",
      "created": "2026-10-25T14:02:11.000Z",
      "job": "job_7Hq2",
      "service": "translation",
      "data": {
        "by": "buyer",
        "amount_usd": 49
      },
      "request": {
        "id": "req_WppKI6",
        "idempotency_key": null
      },
      "livemode": false,
      "simulated": true
    },
    {
      "id": "evt_88cLb4PC0MfpT4",
      "object": "event",
      "seq": 2,
      "type": "charge.held",
      "created": "2026-10-25T14:02:11.000Z",
      "job": "job_7Hq2",
      "service": "translation",
      "data": {
        "amount_usd": 49
      },
      "request": {
        "id": "req_WppKI6",
        "idempotency_key": null
      },
      "livemode": false,
      "simulated": true
    }
  ],
  "has_more": true,
  "url": "/events",
  "next_seq": 11
}
```

**Explained in** [Conventions](https://services.dev/docs/api/conventions.md), [Your events](https://services.dev/docs/api/events.md)

### `event.stream`

The brand's events, pushed as they happen over server-sent events, from a seq or the Last-Event-ID; with watch=, that watch's deliveries.

Never on MCP. A stream, not a call: an agent reads list_events after a seq, or job:// for one job.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_` · effect: read · gate: access · idempotent: yes · returns: `stream`

**Can answer** `input_invalid`, `not_found`

| Takes | In | Type | Says |
|---|---|---|---|
| `after` | query | `integer` | The seq to stream from. |
| `watch` | query | `string` | A watch's deliveries instead: wh_…. |

```txt
→ GET /events/stream   # held open, and nothing else is read
```

```txt title="captured/clock-live.txt · magic values: none"
← event: clock.noticed · job_7Hq2 · expires · rung notice 1d · owed at its time, rung by the alarm (request none)
```

### `event.inbound`

Send an event in: a job's inbound signal by name; an event a subscription listens for, which mints the Service it routes to; or, on a test key, a synthetic event of the catalog, fired to its audience like any other.

Who may call: `sk_test_`, `sk_live_`, `dlg_`, `tk_` · effect: record · gate: access · idempotent: with a key · returns: [`event`](https://services.dev/docs/reference/objects.md#event)

**Emits** `signal.raised`, [`subscription.minted`](https://services.dev/docs/reference/events.md#subscription.minted), [`allowance.used`](https://services.dev/docs/reference/events.md#allowance.used), [`allowance.exceeded`](https://services.dev/docs/reference/events.md#allowance.exceeded) · **Can answer** `input_invalid`, [`event_id_required`](https://services.dev/docs/reference/errors.md#event_id_required), `not_found`, `job_ended`

| Takes | In | Type | Says |
|---|---|---|---|
| `type` | body | `string` | **Required.** A job's inbound signal, with its job; an event a subscription listens for; or an event of the catalog. |
| `job` | body | `string` | The job whose signal it raises. |
| `data` | body | `object` | The event's data: a minted job's input fields, or the synthetic event's. |
| `subscription` | body | `string` | The subscription it mints on: sbn_…. |
| `subject` | body | `string` | Or the subject whose subscriptions mint on it: sbj_…. |
| `id` | body | `string` | Your own id for the event, such as the notice number: needed when it mints on a subscription. The same id sent again answers the first outcome, replayed: true, and mints nothing more. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

```txt
→ POST /events {"type":"page.changed","id":"page-1","data":{"site":"acme.example","document":"page-1.html"}}
```

```txt title="captured/grow-subscription.txt · magic values: none"
← subscription.minted · event page.changed → job_7Hq2 translation · $0.00
```

**Explained in** [Schedules and triggers](https://services.dev/docs/service/subscriptions.md)

## Watches

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`watch.create`](https://services.dev/docs/reference/operations.md#watchcreate) | `POST /watches` | `watches create <url>` | never | `watches.create()` |
| [`watch.list`](https://services.dev/docs/reference/operations.md#watchlist) | `GET /watches` | `watches list` | never | `watches.list()` |
| [`watch.delete`](https://services.dev/docs/reference/operations.md#watchdelete) | `DELETE /watches/:id` | `watches delete <watch>` | never | `watches.delete()` |
| [`watch.replay`](https://services.dev/docs/reference/operations.md#watchreplay) | `POST /watches/:id/replay` | `watches replay <watch>` | never | `watches.replay()` |

### `watch.create`

Watch events at a URL: each delivery is signed with the watch's whsec_ secret, shown once, and retried with backoff for three days.

Never on MCP. A watch delivers to a URL your server holds, which an agent's session doesn't: it reads list_events after a seq instead.

Who may call: `sk_test_`, `sk_live_` · effect: record · gate: access · idempotent: with a key · returns: [`watch`](https://services.dev/docs/reference/objects.md#watch)

**Can answer** `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `url` | body | `string` | **Required.** Where to deliver. |
| `events` | body | `array` | The event types to watch; every one a buyer receives when left out. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

```bash
curl -X POST http://localhost:8787/watches -H 'content-type: application/json' -d '{"url":"https://hooks.example.test/lingua","events":["job.delivered"]}'
```

```json title="captured/api-watch.txt · 200 · magic values: watch.url = https://hooks.example.test/lingua"
{
  "id": "wh_1rKV5VS8V3",
  "object": "watch",
  "url": "https://hooks.example.test/lingua",
  "events": [
    "job.delivered"
  ],
  "status": "enabled",
  "created": "2026-10-25T14:02:11.000Z",
  "livemode": false,
  "simulated": true,
  "deliveries": {
    "delivered": 0,
    "failing": 0,
    "gave_up": 0
  },
  "secret": "whsec_88PiGzGqWulKhuHAUGldLH9GSl5J8tTF"
}
```

**Explained in** [Your events](https://services.dev/docs/api/events.md)

### `watch.list`

The watches this key holds, with their deliveries.

Never on MCP. A watch delivers to a URL your server holds, which an agent's session doesn't: it reads list_events after a seq instead.

Who may call: `sk_test_`, `sk_live_` · effect: read · gate: access · idempotent: yes · returns: `list`

**Can answer** `input_invalid`

Takes `limit`, `starting_after`, as [the calls that share it](#takes-calls-share) do.

### `watch.delete`

Stop a watch. Deliveries still retrying stop with it.

Never on MCP. A watch delivers to a URL your server holds, which an agent's session doesn't: it reads list_events after a seq instead.

Who may call: `sk_test_`, `sk_live_` · effect: record · gate: access · idempotent: yes · returns: `watch`

**Can answer** `not_found`

### `watch.replay`

Deliver to a watch again every event it covers after a seq, in order.

Never on MCP. A watch delivers to a URL your server holds, which an agent's session doesn't: it reads list_events after a seq instead.

Who may call: `sk_test_`, `sk_live_` · effect: act · gate: access · idempotent: with a key · returns: `replay`

**Can answer** `not_found`, `input_invalid`

| Takes | In | Type | Says |
|---|---|---|---|
| `after` | body | `integer` | The seq to replay from; 0 when left out. |

Also takes `key`, as [the calls that share it](#takes-calls-share) do.

## Machine registers

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`brand.home`](https://services.dev/docs/reference/operations.md#brandhome) | `GET /index.json` | — | — | — |
| [`brand.health`](https://services.dev/docs/reference/operations.md#brandhealth) | `GET /healthz` | — | — | — |
| [`brand.card`](https://services.dev/docs/reference/operations.md#brandcard) | `GET /.well-known/agents.json` | — | — | — |
| [`brand.openapi`](https://services.dev/docs/reference/operations.md#brandopenapi) | `GET /openapi.json` | — | — | — |
| [`brand.llms`](https://services.dev/docs/reference/operations.md#brandllms) | `GET /llms.txt` | — | — | — |
| [`brand.manifest`](https://services.dev/docs/reference/operations.md#brandmanifest) | `GET /manifest.json` | — | — | — |
| [`brand.pricing`](https://services.dev/docs/reference/operations.md#brandpricing) | `GET /pricing` | — | — | — |
| [`brand.verify`](https://services.dev/docs/reference/operations.md#brandverify) | `GET /verify/suite.json` | — | — | — |

### `brand.home`

The brand's home, as JSON and as Markdown: its name, what it sells and where to start. GET / is its page.

Also served at `GET /index.md`.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `home`

### `brand.health`

Whether the brand answers: OK, with its version and how many Services it serves.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `health`

### `brand.card`

The brand's agent card: its Services, its access ladder, tools, links and probes.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `card`

```txt
→ GET /.well-known/agents.json
```

```txt title="captured/brand-smallest.txt · magic values: none"
  name     lingua.example
  openapi  https://lingua.example/openapi.json
```

**Explained in** [Machine registers](https://services.dev/docs/api/machine.md)

### `brand.openapi`

Every operation, as OpenAPI 3.1, with its webhooks.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `openapi`

```bash
curl http://localhost:8787/openapi.json   # one path item and one webhook, whole
```

```json title="captured/api-openapi-item.txt · magic values: none"
{
  "openapi": "3.1.0",
  "info": {
    "title": "lingua",
    "version": "2026-10-25",
    "description": "lingua's API: one operation per row of its contract. A list answers OK or EMPTY in its type, a price OFFER and a no BLOCKED, and every no is the one error shape."
  },
  "paths": {
    "/jobs/{id}/accept": {
      "post": {
        "operationId": "job.accept",
        "summary": "Accept a delivered job. It is final; a job nobody accepts is final 7 days after delivery.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Services-Version",
            "in": "header",
            "schema": {
              "type": "string",
              "enum": [
                "2026-10-25"
              ]
            },
            "description": "Pin the contract version; every answer says the one it used."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Any string: the first answer is kept 24 hours and replayed byte for byte, with Idempotent-Replayed: true."
          }
        ],
        "responses": {
          "200": {
            "description": "OK.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "object",
                    "status",
                    "next"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "job_…"
                    },
                    "object": {
                      "const": "job"
                    },
                    "created": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "service": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "working",
                        "waiting",
                        "delivered",
                        "accepted",
                        "canceled",
                        "ended"
                      ]
                    },
                    "stage": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "output": {
                      "type": "object"
                    },
                    "final_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "metadata": {
                      "type": "object"
                    },
                    "payment": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "enum": [
                            "held",
                            "charged",
                            "final",
                            "none"
                          ]
                        },
                        "amount_usd": {
                          "type": "number"
                        }
                      }
                    },
                    "timeline": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "With expand[]=timeline: the events of this job a buyer may read."
                    },
                    "next": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "The next call, ready to send: tool and arguments over MCP, or http. A hand_to_person, checkout or waitlist carries the url to give a person.",
                        "properties": {
                          "do": {
                            "type": "string"
                          },
                          "tool": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "arguments": {
                            "type": "object"
                          },
                          "http": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "after": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "BLOCKED: body_invalid, version_unknown.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Refused"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "body_invalid",
                            "version_unknown"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "BLOCKED: key_missing, key_invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Refused"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "key_missing",
                            "key_invalid"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "BLOCKED: needs_the_party.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Refused"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "needs_the_party"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "EMPTY: not_found.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Refused"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_found"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "BLOCKED: not_delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Refused"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "not_delivered"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "description": "BLOCKED: api_error.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Refused"
                    },
                    {
                      "properties": {
                        "code": {
                          "enum": [
                            "api_error"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "x-operation": "job.accept",
        "x-actor": [
          "sk_test_",
          "sk_live_",
          "dlg_"
        ],
        "x-effect": "act",
        "x-gate": "access",
        "x-idempotent": true,
        "x-emits": [
          "job.accepted",
          "charge.earned",
          "charge.released"
        ],
        "x-status": "live"
      }
    }
  },
  "webhooks": {
    "job.delivered": {
      "post": {
        "summary": "The output is ready for the buyer.",
        "parameters": [
          {
            "name": "services-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "t=<unix seconds>,v1=<HMAC-SHA256 of \"<t>.<body>\" with the watch's whsec_ secret>"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "properties": {
                      "type": {
                        "const": "job.delivered"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Received. Anything else is retried with backoff."
          }
        }
      }
    }
  }
}
```

**Explained in** [Machine registers](https://services.dev/docs/api/machine.md)

### `brand.llms`

What an agent reads first: the Services, and every operation.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `text`

**Explained in** [Machine registers](https://services.dev/docs/api/machine.md)

### `brand.manifest`

Every Service, as the manifest.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `manifest`

```bash
curl http://localhost:8787/manifest.json
```

```txt title="captured/api-manifest.txt · magic values: none"
brand     lingua
version   2026-10-25
keys      brand, version, services, tools, portals, rendered, fold
services  certified-translation, store-listing, translated-summary, translation
```

**Explained in** [The manifest](https://services.dev/docs/service/manifest.md)

### `brand.pricing`

The pricing document: calls are free, and only an order charges, at its Service's price. Every price, and the buyer key's hard ceiling.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `pricing`

```bash
curl http://localhost:8787/pricing
```

```json title="captured/api-pricing.txt · magic values: none"
{
  "model": "free",
  "charges": "orders",
  "says": "Every call is free. Only an order charges, at its price, once its record holds what the price releases on. Past the buyer key's cap, an order gets the OFFER.",
  "hardCeiling": 500,
  "unit": "usd-per-buyer-key",
  "offers": [
    {
      "id": "order_certified_translation",
      "usd": 189,
      "per": "job",
      "charged": "on delivery",
      "sentence": "$189 per job, within 3 days, charged on delivery."
    },
    {
      "id": "order_store_listing",
      "usd": 29,
      "per": "job",
      "charged": "on delivery",
      "sentence": "$29 per job, within 48 hours, charged on delivery."
    },
    {
      "id": "order_translated_summary",
      "usd": 79,
      "per": "job",
      "charged": "on delivery",
      "sentence": "$79 per job, within 48 hours, charged on delivery."
    },
    {
      "id": "order_translation",
      "usd": 49,
      "per": "job",
      "charged": "on delivery",
      "sentence": "$49 per job, within 48 hours, charged on delivery."
    }
  ]
}
```

**Explained in** [Machine registers](https://services.dev/docs/api/machine.md)

### `brand.verify`

The brand's conformance suite: one case per operation row, live credential and magic value, with its auth, audience, replay and next cases, over HTTP and MCP. services.dev verify runs it.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `suite`

```bash
curl http://localhost:8787/verify/suite.json
```

```txt title="captured/api-suite-json.txt · magic values: metadata.sandbox.outside = { reject: [missing_certificate] }, metadata.sandbox.outside = { reject: [a_code_no_route_names] }, metadata.sandbox.review = pass, metadata.sandbox.review = flag_once, metadata.sandbox.outside = ack, metadata.sandbox.outside = reject_once, metadata.sandbox.outside = never, metadata.sandbox.outside = alerts"
counted  {"rows":42,"cases":144,"skipped":54,"http":121,"mcp":23}
```

**Explained in** [Your conformance suite](https://services.dev/docs/api/verify.md)

## Other operations

| Operation | HTTP | CLI | MCP | SDK |
|---|---|---|---|---|
| [`receipt.answer`](https://services.dev/docs/reference/operations.md#receiptanswer) | `POST /owner/jobs/:id/receipts/:rcp` | — | — | — |
| [`sandbox.fixtures`](https://services.dev/docs/reference/operations.md#sandboxfixtures) | `GET /fixtures` | — | — | — |

### `receipt.answer`

An outside system's verdict on a filing it took as transmitted, sent later by it or by your own poller: accepted, accepted with alerts, or rejected with its codes.

Who may call: owner session · effect: act · gate: access · idempotent: with a key · returns: [`receipt`](https://services.dev/docs/reference/objects.md#receipt)

**Emits** [`receipt.accepted`](https://services.dev/docs/reference/events.md#receipt.accepted), [`receipt.rejected`](https://services.dev/docs/reference/events.md#receipt.rejected) · **Can answer** `not_found`

### `sandbox.fixtures`

The sandbox's magic values: where each goes, the branch it takes, and its status. Each works on a test key.

Who may call: anyone · effect: read · gate: none · idempotent: yes · returns: `list`

## Running a Service

The calls the owner runs a live Service with, from the console, the CLI or a coding agent, each explained on its Operate page:

| HTTP | CLI | Emits | Explained in |
|---|---|---|---|
| `POST https://services.dev/v1/deploys` | `deploy` | `version.deployed`, `version.tested`, `version.failed` | [Deploy](https://services.dev/docs/operate/deploy.md#deploy-to-test) |
| `POST https://services.dev/v1/deploys/:version/live` | `deploy --live [--breaking]` | `version.live` | [Deploy](https://services.dev/docs/operate/deploy.md#deploy-live) |
| `POST https://services.dev/v1/deploys/:version/live { stages, hold }` | `deploy --live --stages <shares> --hold <span>` | `version.staged`, `version.promoted`, `version.aborted` | [Deploy](https://services.dev/docs/operate/deploy.md#live-in-stages) |
| `POST https://services.dev/v1/deploys/:version/live { break_freeze }` | `deploy --live --break-freeze --reason <why>` | `version.live` | [Deploy](https://services.dev/docs/operate/deploy.md#freeze-the-dates-that-matter) |
| `POST https://services.dev/v1/stripe/connect` | `connect stripe` | `stripe.connected` | [Deploy](https://services.dev/docs/operate/deploy.md#deploy-live) |
| `PUT https://services.dev/v1/model` | `model set <key>` | `model.set` | [Deploy](https://services.dev/docs/operate/deploy.md#deploy-live) |
| `GET https://services.dev/v1/domains` | `domains` | `domain.verified` | [Deploy](https://services.dev/docs/operate/deploy.md#deploy-live) |
| `POST /test_clocks` | `test-clocks create <time> [--members N --market tight]` | `test_clock.created` | [Test and live](https://services.dev/docs/operate/testing.md#run-a-season-before-it-starts) |
| `POST /test_clocks/:id/seed` | `subscriptions seed <service> --count N --test-clock <tc>` | `subscription.created` | [Test and live](https://services.dev/docs/operate/testing.md#run-a-season-before-it-starts) |
| `POST /test_clocks/:id/advance` | `tick <time> --test-clock <tc>` | `test_clock.ready` | [Timers and replay](https://services.dev/docs/service/timers.md#on-a-test-clock) |
| `GET /verify/suite.json` | `verify --test-clock <tc>` | none: it reads | [Test and live](https://services.dev/docs/operate/testing.md#run-a-season-before-it-starts) |
| `POST https://services.dev/v1/previews` | `preview` | `version.tested` | [Test and live](https://services.dev/docs/operate/testing.md#where-each-mode-serves) |
| `POST https://services.dev/v1/deploys { region }` | `deploy --region <region>` | `version.deployed` | [Test and live](https://services.dev/docs/operate/testing.md#where-each-mode-serves) |
| `POST https://services.dev/v1/deploys { dry_run: true }` | `check --against live` | — | [Versions](https://services.dev/docs/operate/versions.md#what-counts-as-breaking) |
| `POST /owner/versions/:from/repin { to, dry_run }` | `jobs repin --from <version> --to <version> [--dry-run]` | `job.repinned` | [Versions](https://services.dev/docs/operate/versions.md#moving-jobs-in-flight) |
| `POST /owner/jobs/:id/repin { to }` | `jobs repin <job> --to <version>` | `job.repinned` | [Versions](https://services.dev/docs/operate/versions.md#moving-jobs-in-flight) |
| `POST https://services.dev/v1/deploys/:version/live { rollback: true, dry_run }` | `rollback <version> [--dry-run] [--breaking]` | `version.rolled_back` | [Versions](https://services.dev/docs/operate/versions.md#rolling-back) |
| `GET /versions?pinned=true` | `versions` | none: it reads | [Versions](https://services.dev/docs/operate/versions.md#rolling-back) |
| `GET /owner/jobs/:id/trace` | `jobs trace <job>` | none: it reads | [Observe](https://services.dev/docs/operate/observe.md#a-jobs-trace) |
| `GET /owner/jobs/:id` | `jobs ledger <job>` | none: it reads | [Observe](https://services.dev/docs/operate/observe.md#the-jobs-money) |
| `GET /owner/spend` | `spend --by service\|step\|model --since <date>` | `spend.alarm`, `ai.exhausted` | [Observe](https://services.dev/docs/operate/observe.md#ai-spend) |
| `GET /owner/logs` | `logs` | none: it reads | [Observe](https://services.dev/docs/operate/observe.md#on-the-console) |
| `GET /events/stream` | `listen` | none: it reads | [Observe](https://services.dev/docs/operate/observe.md#on-the-console) |
| `GET /owner/audit` | `audit --since <date> [--who agent]` | none: it reads | [Observe](https://services.dev/docs/operate/observe.md#the-audit-log) |
| `GET /owner/console` | `console [--since 90d]` | none: it reads | [Run your Service](https://services.dev/docs/operate.md#the-quarter-on-one-line) |
| `GET /owner/quality?service=` | `quality <service>` | `review.done` | [Quality](https://services.dev/docs/operate/quality.md#two-review-patterns-each-with-its-own-numbers) |
| `POST /owner/evals` | `evals create <service> --label <label>` | `eval.created` | [Quality](https://services.dev/docs/operate/quality.md#score-a-change-before-it-ships) |
| `POST /owner/evals/:id/runs` | `evals run <evl> --against <version>` | `eval.finished` | [Quality](https://services.dev/docs/operate/quality.md#score-a-change-before-it-ships) |
| `GET /owner/line?service=` | `line <service> [--all] [--by <field>]` | `line.ready` | [Move a step to AI](https://services.dev/docs/operate/line.md#the-lines-that-are-ready) |
| `POST /owner/line/:service/apply` | `line apply <service>` | `line.moved` | [Move a step to AI](https://services.dev/docs/operate/line.md#the-lines-that-are-ready) |
| `GET /pools/:name/health` | `pools health <pool> --since 7d` | `pool.alarm` | [Pool health](https://services.dev/docs/operate/pool-health.md#the-measures) |
| `POST /owner/pools/:name/standby` | `pools standby <pool> --role <role> --in <region> --season <from>..<to>` | `standby.offered` | [Pool health](https://services.dev/docs/operate/pool-health.md#a-week-in-april) |
| `POST /owner/pools/:name/raise` | `pools raise <pool> --role <role> --in <region> --sooner` | `task.raised` | [Pool health](https://services.dev/docs/operate/pool-health.md#a-week-in-april) |
| `POST /owner/pools/:name/invites` | `pools invite <pool> --role <role> --in <region>` | `pool.invited` | [Pool health](https://services.dev/docs/operate/pool-health.md#a-week-in-april) |
| `POST /owner/pools/:name/widen` | `pools widen <pool> --role <role> --to <region>` | `pool.widened` | [Pool health](https://services.dev/docs/operate/pool-health.md#a-week-in-april) |
| `POST /owner/clocks/:id/assign` | `clocks assign <clock> <member>` | `clock.assigned` | [Dates at risk](https://services.dev/docs/operate/dates.md#why-a-date-is-listed) |
| `POST /clocks/:id/satisfy` | `clocks satisfy <clock>` | `clock.satisfied` | [Dates at risk](https://services.dev/docs/operate/dates.md#why-a-date-is-listed) |
| `POST /clocks/:id/choose` | `clocks choose <clock> <option>` | `clock.chosen` | [Dates at risk](https://services.dev/docs/operate/dates.md#why-a-date-is-listed) |
| `POST /clocks/:id/ack` | `clocks ack <clock>` | `clock.acked` | [Dates at risk](https://services.dev/docs/operate/dates.md#why-a-date-is-listed) |
| `POST /owner/holds { at, where }` | `hold <service>.<step> [--where <filter>]` | `hold.placed`, `job.held` | [Incidents](https://services.dev/docs/operate/incidents.md#the-four-controls) |
| `POST /owner/holds { at, to, pay }` | `hold <service>.<step> --to <role> [--pay <usd>]` | `hold.placed` | [Incidents](https://services.dev/docs/operate/incidents.md#the-four-controls) |
| `POST /owner/holds { at, drain: true }` | `hold <service> --drain` | `hold.placed` | [Incidents](https://services.dev/docs/operate/incidents.md#drain-or-stop-everything) |
| `GET /owner/holds` | `holds` | none: it reads | [Incidents](https://services.dev/docs/operate/incidents.md#a-model-that-changed-overnight) |
| `GET /owner/jobs?decision.model_build=&since=` | `jobs affected --where <filter> --since <time>` | none: it reads | [Incidents](https://services.dev/docs/operate/incidents.md#what-it-already-touched) |
| `POST /owner/jobs/send-back { where, from }` | `jobs send-back --affected <filter> --from <step>` | `job.sent_back` | [Incidents](https://services.dev/docs/operate/incidents.md#what-it-already-touched) |
| `POST /owner/holds/:id/lift { repin_to, dry_run }` | `hold lift <hld> [--dry-run] [--repin-to <version>]` | `hold.lifted`, `job.repinned` | [Incidents](https://services.dev/docs/operate/incidents.md#lift-it-without-going-back) |
| `GET /owner/incidents/:id` | `incidents get <inc>` | `incident.opened`, `incident.closed` | [Incidents](https://services.dev/docs/operate/incidents.md#after-it) |
| `GET /owner/incidents/:id/report` | `incidents report <inc>` | none: it reads | [Incidents](https://services.dev/docs/operate/incidents.md#after-it) |
| `POST /owner/stop { except, to }` | `stop --live [--except <step>] [--to <role>]` | `brand.stopped` | [Incidents](https://services.dev/docs/operate/incidents.md#drain-or-stop-everything) |
| `POST /owner/start` | `start --live` | `brand.started` | [Incidents](https://services.dev/docs/operate/incidents.md#drain-or-stop-everything) |
| `POST /owner/keys/rotate { all: true }` | `keys rotate --all --live` | `key.rotated` | [Incidents](https://services.dev/docs/operate/incidents.md#which-control-for-which-cause) |
| `POST /jobs/:id/send_back` | `jobs send-back <job> <note> --from <step>` | `job.sent_back` | [Support](https://services.dev/docs/operate/support.md#a-redo) |
| `POST /owner/jobs/:id/refund` | `jobs refund <job> [--usd] --reason <why>` | `charge.refunded` | [Support](https://services.dev/docs/operate/support.md#a-refund-on-two-sellers) |
| `GET /owner/disputes/:id` | `disputes get <dsp>` | `dispute.opened` | [Support](https://services.dev/docs/operate/support.md#a-dispute) |
| `POST /owner/disputes/:id/submit` | `disputes submit <dsp>` | `dispute.closed` | [Support](https://services.dev/docs/operate/support.md#a-dispute) |
| `POST /owner/tasks/:id/rule` | `tasks rule <task> upheld\|overturned` | `task.ruled` | [Support](https://services.dev/docs/operate/support.md#the-queue) |
| `GET /owner/limits` | `limits` | `limit.reached` | [Limits](https://services.dev/docs/operate/limits.md#where-you-stand) |
| `POST https://services.dev/v1/plan` | `plan upgrade <plan>` | `plan.accepted` | [Limits](https://services.dev/docs/operate/limits.md#at-a-plan-limit) |

---

- Up: [Your first paid job](https://services.dev/docs.md)
- Previous: [Take your first real deal](https://services.dev/docs/examples/sales/launch.md)
- Next: [Objects](https://services.dev/docs/reference/objects.md)
