---
title: "What the buyer sets, and what only the client sets"
url: https://services.dev/docs/service/policy
captured: [legal-walk.txt, sales-check.txt, service-policy.txt]
codes: [job_ended, merits_not_yours, policy_invalid]
---
# What the buyer sets, and what only the client sets

URL: https://services.dev/docs/service/policy

> policy declares what an order may set, each value with a default and bounds. Mechanics are the buyer's; merits are the client's alone; the rest is the house's.

`policy` answers one question: what may an order change about how the work runs, and who may change it?

## Smallest form

```ts title="services/checked-translation.ts"
  policy: {
    match: { word: 'number', default: 0, min: 0, max: 1, binds: 'merits', note: 'the least back-translation match the client accepts' },
    turnaround: { word: 'number', default: 2, min: 1, max: 10, note: 'days the work may take' },
  },
```

## Every form

**On the Service**

| Key | Written | Left out | Does |
|---|---|---|---|
| `policy` | `{ <name>: PolicyEntry }` | none | What an order may set, within your bounds: the buyer's mechanics, and the merits only the client sets. |

**Each entry**

| Key | Written | Left out | Does |
|---|---|---|---|
| `word` | `Word` | required | Its type word, the same words `input` uses. |
| `default` | `unknown \| ((job) => unknown)` | required | The value when the order leaves it out: a value, or a function of the order's input, as a stall defaults to 7 days on a consumer deal and 14 otherwise. |
| `min` | `number` | none | For a `number`: the least an order may set. |
| `max` | `number` | none | For a `number`: the most an order may set. |
| `note` | `string` | none | What it means, as the manifest carries it. |
| `binds` | `'mechanics' \| 'merits'` | none | 'mechanics' (default): how the work runs, the buyer's. 'merits': what the client decides about their own work, theirs alone. |
| `set_by` | `'buyer' \| 'client' \| 'tenant'` | the buyer for mechanics; always the client for merits | Who may set it. |

## Every violation at once

An order sends `policy` beside its input. One that breaks a bound hears every violation in one answer, with `next.fill` naming each field to fix:

```txt
→ POST /checked-translation {"document":"manual.pdf","to":"de","policy":{"match":1.5,"turnaround":"fast"},"max_usd":79,"key":"bad"}
```

```json title="422 · magic values: none"
{
  "type": "BLOCKED",
  "category": "invalid_request",
  "code": "policy_invalid",
  "reason": "The policy breaks 2 bounds: match must be at most 1; turnaround must be a number.",
  "param": "policy",
  "next": {
    "do": "order",
    "service": "checked-translation",
    "arguments": {
      "document": {
        "url": "data:text/plain,The%20manual.pdf%2C%20as%20the%20buyer%20sent%20it.",
        "name": "manual.pdf"
      },
      "to": "de",
      "policy": {
        "match": null,
        "turnaround": null
      },
      "max_usd": 79,
      "key": "bad",
      "service": "checked-translation"
    },
    "fill": [
      "policy.match",
      "policy.turnaround"
    ],
    "tool": "order_job",
    "http": "POST http://localhost:8787/jobs {\"service\":\"checked-translation\"}"
  },
  "doc_url": "http://localhost:8787/docs/errors/policy_invalid",
  "request_id": "req_b3cvlB",
  "simulated": true
}
```

`next` is the same order with each bad value set to `null`, and `fill` names them, so an agent fixes the two fields and sends it again.

A good one resolves, every default filled, and every step, guard and signal reads it as `job.policy`:

```txt
→ POST /checked-translation {"document":"manual.pdf","to":"de","policy":{"turnaround":3},"max_usd":79,"key":"set"}
```

```txt title="magic values: none"
← job_7Hq2 · waiting · on person · $79.00 held
  policy  {"match":0,"turnaround":3}
```

## Mechanics and merits

`binds: 'mechanics'`, the default, is how the work runs: a turnaround, a cap, a timeout. The buyer sets it.

`binds: 'merits'` is what the client decides about their own work: here, the least match they accept. Only the client sets it. When the buyer orders for themselves, the buyer is the client; when a buyer orders for a separate client, only that client sets it. The brand can't set a merits value either:

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

```txt title="403 · magic values: none"
  "code": "merits_not_yours",
  "reason": "match binds the merits, and only the client sets them.",
```

Parameters are the buyer's, merits are the client's, and semantics are the house's: the ends, the gate and when money moves are never set per order.

## Changing it later

`POST /jobs/:id/policy` changes values on a running job, within the same bounds, and records `policy.set`. The buyer moves a mechanics value; a merits value still answers `merits_not_yours` to anyone but the client. An ended job takes no more changes.

```txt
→ POST /jobs/job_7Hq2/policy {"policy":{"turnaround":5}}
```

```txt title="200 · job_7Hq2 · waiting · on person · $79.00 held · magic values: none"
  policy  {"match":0,"turnaround":5}
```

## Seven surfaces

| Op | API | CLI | MCP | SDK | Site | Docs | App |
|---|---|---|---|---|---|---|---|
| `service.order` | `POST /jobs`, `POST /:service` | `<service> k=v …` | `order_<service>` | `<service>.order()` | `/<service>`: the order form | 10 error pages | customer portal |
| `job.policy` | `POST /jobs/:id/policy`, `POST /owner/jobs/:id/policy` | `jobs set-policy <job> k=v …` | `set_job_policy` | `jobs.setPolicy()` |  | 4 error pages | customer portal, owner console |

Events: `policy.set`.

[Operations](https://services.dev/docs/reference/operations.md) has every column of every row.

## Refusals

| Code | Category | HTTP | Why |
|---|---|---|---|
| [`job_ended`](https://services.dev/docs/reference/errors.md#job_ended) | conflict | 409 | The job has ended, for good. |
| [`policy_invalid`](https://services.dev/docs/reference/errors.md#policy_invalid) | invalid_request | 422 | A policy value is out of bounds; the reason lists each. |
| [`merits_not_yours`](https://services.dev/docs/reference/errors.md#merits_not_yours) | blocked | 403 | Only the client sets the merits. |

## In the example brands

The lines that use it in the example brands, as their files write them, and what `check` read aloud for each file.

```ts title="legal · services/demand-letter.ts · 26 lines"
  policy: { floor: { word: 'money', default: 0, binds: 'merits', note: 'the least you will settle for: your default answer to an offer; only you set it' } },
```

```txt title="npx services.dev check · captured/legal-walk.txt"
services/demand-letter.ts · 26 lines · $149 per job, within 48 hours, charged on send's receipt.
  steps   consent · client · consent → draft · ai → attack · ai · adversarial · 3 lenses → sign · attorney · client · stamped · 2-item checklist → send · the recipient → courier · a courier · routed → reply · ai → settle · client · decide
```

[The legal example](https://services.dev/docs/examples/legal.md) runs every one of its files, captured.

```ts title="sales · services/deal.ts · 42 lines"
  policy: {
    floor: { word: 'number', default: 0.8, min: 0.1, max: 1, binds: 'merits', note: 'the least share of list a close may take' },
    humans_from: { word: 'money', default: 50_000, note: 'a closer takes deals at or above this value' },
    setter: { word: ['ai', 'on_warm', 'person'], default: 'on_warm', note: 'who books the meeting' },
    stall_days: { word: 'number', default: job => (job.input.motion === 'B2C' ? 7 : 14), min: 3, max: 90 },
  },
```

```txt title="npx services.dev check · captured/sales-check.txt"
services/deal.ts · 42 lines · nothing on effort; 5% of closed_value at won, proven by the account's payment, held 60 days by bank, 120 by card.
  policy  floor · merits, the seller's alone · 0.8 of value · 0.1 to 1
          humans_from · mechanics · $50,000
          setter · mechanics · on_warm · ai, on_warm or person
          stall_days · mechanics · 14, or 7 for B2C · 3 to 90
```

[The sales example](https://services.dev/docs/examples/sales.md) runs every one of its files.

---

- Up: [Service()](https://services.dev/docs/service.md)
- Previous: [Guards](https://services.dev/docs/service/guards.md)
- Next: [Signals](https://services.dev/docs/service/signals.md)
