---
title: "The attorneys' Pool"
url: https://services.dev/docs/examples/legal/pool
captured: [legal-build.txt, legal-fill.txt, member-ledger.txt, pool-eligibility.txt, pool-join.txt, task-dispatch.txt]
---
# The attorneys' Pool

URL: https://services.dev/docs/examples/legal/pool

> One Pool for the legal brand's six Services. Attorneys and patent practitioners join, the verifier checks each credential, Tasks are offered one member at a time, conflicts stay private, and tiers come from outcomes.

*legal's words: matter is a job, asset is a subject, review is a task, signer is a member, network is a pool, outcome is a end (examples/legal/brand.ts).*

One Pool serves all six Services. Attorneys and patent practitioners join on their own, and the Pool's verifier is the only writer of what they hold. Every person step posts a Task to it. A member sees a teaser that names no party, a sheet that does, and the packet only once they've cleared conflicts.

```bash
cat pools/attorneys.ts
```

```txt title="magic values: none"
  roles: {
    attorney: { credential: 'bar admission' },
    practitioner: { credential: ['patent attorney', 'patent agent'], where: '*' },
    watcher: { credential: ['patent attorney', 'patent agent'], where: '*' },
    student: { credential: 'law school enrollment' },
  scopes: { 'bar admission': 'region', 'patent attorney': '*', 'patent agent': '*', 'law school enrollment': 'region' },
    name: 'text: your name as admitted', credential: ['bar admission', 'patent attorney', 'patent agent', 'law school enrollment'],
  verify: 'https://verify.example.test/credentials',
  coverage: { per_act: 1_000_000, by: 'either', usd: 4 },
  sees: { teaser: ['act', 'region', 'fee_usd', 'signals'], sheet: ['parties', 'kind', 'amount'] },
  clear: { attest: ['I checked conflicts across my full book, including my firm'], check: ['client', 'applicant', 'recipient'] },
  affinity: '30d', rank: ['fit', 'track', 'speed'],
  tiers: [{ name: 'reviewer' }, { name: 'signer', after: 20, sendbacks: '5%', on_time: '95%' }, { name: 'counsel', after: 100, sendbacks: '3%', on_time: '98%' }],
```

[The whole file](https://services.dev/docs/examples/legal/build.md#poolsattorneysts), and `check` after it.

## What each line sets

| Key | In this Pool |
|---|---|
| `roles` | `attorney` holds a bar admission, `practitioner` and `watcher` a patent registration, `student` a law school enrollment. A Service names a role; none names `student` yet |
| `scopes` | A bar admission and an enrollment count where they're held. A patent registration counts everywhere |
| `join` | Eight fields over three steps, after the email, and resumable |
| `verify` | The only writer of what a member holds, through its own `vt_` token |
| `coverage` | $1,000,000 an act. A member's own policy covers their act, and the Pool's covers one who carries none, at $4 an act on the brand's ledger |
| `sees` | The teaser shows the act, the region, the fee and the signals. The sheet adds the parties, the kind and the amount |
| `clear` | The conflicts statement a member attests, and the party inputs checked against their own ledger |
| `affinity` | 30 days of first refusal on follow-on work for the same asset |
| `rank` | Fit, then track record, then speed |
| `tiers` | Reviewer, then signer after 20 reviews, then counsel after 100, each with a send-back and on-time bar |
| left out: `dispatch` | One member at a time, because `clear.check` names parties |
| left out: `hold` | Three times the step's estimate, at least a day |
| left out: `fill` | The default: raise 10% at 2 hours, widen a tier at 4, then firms at 8 and staff at 12 |
| left out: `payouts` | Pay accrues at the act and is payable two days after it |

## Join and verify

Joining is email first and resumable, one group of fields a step:

```txt
→ POST /attorneys/join {"email":"grace@example.com"}
```

```txt title="magic values: client.email = signs@example.test"
← 200 mbr_7Hq2 · attorneys · grace@example.com · applying, step 1 of 3: name, credential, number · on
→ POST /attorneys/join {"email":"grace@example.com","answers":{"name":"Grace Hopper","credential":"bar admission","number":"CA-301"}}
← 200 mbr_7Hq2 · attorneys · grace@example.com · applying, step 2 of 3: regions, areas, hours · on
→ POST /attorneys/join {"email":"grace@example.com","answers":{"regions":["US-CA","US-NY"],"areas":["contracts","collections"],"hours":10}}
← 200 mbr_7Hq2 · attorneys · grace@example.com · applying, step 3 of 3: turnaround, min_fee · on
→ POST /attorneys/join {"email":"grace@example.com","answers":{"turnaround":"24h","min_fee":40}}
← 200 mbr_7Hq2 · attorneys · grace@example.com · verified (simulated): bar admission in US-CA, US-NY · on
  verified {"credentials":{"bar admission":["US-CA","US-NY"]},"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}
```

Only the verifier's own token writes verified facts. The brand's key can't:

```txt
→ POST /attorneys/signers/mbr_7Hq2/verify {"facts":{"credentials":{"bar admission":["US-CA","US-NY","US-NJ"]},"coverage":{"per_act":2000000,"until":"2027-10-25T00:00:00Z"}}}   Authorization: Bearer sk_test_7Hq2
```

```txt title="magic values: client.email = signs@example.test"
← 401 key_invalid: This door takes the network's verifier token, Authorization: Bearer vt_…, and nothing else.
→ POST /attorneys/signers/mbr_7Hq2/verify {"facts":{"credentials":{"bar admission":["US-CA","US-NY","US-NJ"]},"coverage":{"per_act":2000000,"until":"2027-10-25T00:00:00Z"}}}   Authorization: Bearer vt_test_7Hq2
← 200 mbr_7Hq2 · attorneys · grace@example.com · verified: bar admission in US-CA, US-NY, US-NJ · on
```

## Who may claim

Each claim is checked in order: credential, region, coverage, availability, separation, conflict, clearance. The answer names the predicate that failed and its cure:

```txt
→ POST /reviews/tsk_7Hq2/claim   # as mbr_lapsed_credential
```

```txt title="magic values: client.email = signs@example.test, member = mbr_lapsed_credential, member = mbr_out_of_region, member = mbr_lapsed_coverage, member = mbr_eligible"
← 403 ineligible · credential: attorney needs bar admission, and the verifier holds no current one for mbr_lapsed_credential.
  cure  Renew bar admission; the verifier writes it once it checks.

→ POST /reviews/tsk_7Hq2/claim   # as mbr_out_of_region
← 403 ineligible · region: attorney needs bar admission in US-CA, and mbr_out_of_region holds it in ZZ.
  cure  Add US-CA to your regions at /join; the verifier checks the admission.

→ POST /reviews/tsk_7Hq2/claim   # as mbr_lapsed_coverage
← 403 ineligible · coverage: attorney needs $1,000,000 of coverage in force per act, and mbr_lapsed_coverage's policy lapsed on 2026-10-24.
  cure  Renew the policy; the verifier writes the new term.

→ POST /reviews/tsk_7Hq2/claim   # as mbr_eligible
← 200 tsk_7Hq2 · claimed · attorney · advise · US-CA · $50.00 · 45 min · $66.67 an hour · due 2026-10-27 14:02
```

## Offered one at a time

Because `clear.check` names parties, a Task goes to one member at a time in rank order, each for a window. A pass moves it on at once:

```txt
→ POST /matters {"service":"contract-review","contract":"lease.pdf","client":{"name":"Ada Lovelace","email":"signs@example.test"},"client_region":"US-CA","max_usd":99,"key":"lease"}
```

```txt title="magic values: client.email = signs@example.test, member = mbr_eligible"
← job_7Hq2 · waiting · on claim · $99.00 held · advise · tsk_7Hq2

# dispatch cascade: offered to mbr_7Hq3 alone, for a window
  mbr_7Hq3      GET /tasks → tsk_7Hq2
  mbr_7Hq2      GET /tasks → nothing
  mbr_eligible  GET /tasks → nothing

→ POST /reviews/tsk_7Hq2/pass   # as mbr_7Hq3

# a pass moves it on at once: mbr_7Hq2
  mbr_7Hq3      GET /tasks → nothing
  mbr_7Hq2      GET /tasks → tsk_7Hq2
  mbr_eligible  GET /tasks → nothing

→ POST /test_clocks/default/advance {"to":"61min"}

# the window ran out: the sandbox's eligible member, last in every cascade
  mbr_7Hq3      GET /tasks → nothing
  mbr_7Hq2      GET /tasks → nothing
  mbr_eligible  GET /tasks → tsk_7Hq2

  offers  mbr_7Hq3 · cascade · 14:02 | mbr_7Hq2 · cascade · 14:02 | mbr_eligible · cascade · 15:02
```

## Conflicts without disclosure

A member's conflicts ledger is theirs. A match keeps the Task from them, and no one learns which entry matched, not even the owner:

```txt
→ GET /reviews   # as mbr_eligible
```

```txt title="magic values: client.email = signs@example.test, member = mbr_eligible"
← 0 Tasks: the cascade skipped a conflicted member
→ POST /reviews/tsk_7Hq2/claim   # as mbr_eligible
← 403 ineligible · conflict: A party on this review matches an entry in mbr_eligible's conflicts ledger.
  cure  None for this Task: your ledger stays yours, and no one learns which entry matched.

→ GET /me/ledger   # the owner console, with no member session
← 403 needs_a_person: Only a signer, in their own session at http://localhost:8787/attorneys, does this. A key never can.
# what the owner reads: /owner/tasks no hash of it · /owner/jobs/job_7Hq2 no hash of it · /owner/jobs/job_7Hq2/export no hash of it · /owner/events?limit=1000 no hash of it
```

## When no one claims

Tiers are computed from outcomes. When no member claims a review in time, the fill ladder raises the fee, then opens it to the tier below:

```txt
→ POST /test_clocks/tc_7Hq3/advance {"to":"2h"}
```

```txt title="magic values: client.email = signs@example.test"
# the record: task.posted → task.raised → task.widened → task.claimed → attested
  14:02 · task.posted · tsk_7Hq2 · $50.00 · dispatch cascade
  16:02 · task.raised · tsk_7Hq2 · $55.00 · raise 10% 2h · funded by the brand
  18:02 · task.widened · tsk_7Hq2 · opened reviewer · widen 4h
  18:02 · task.claimed · tsk_7Hq2 · mbr_7Hq4 · simulated
  ladder  raise 10% 2h: raised to $55.00 · widen 4h: opened reviewer · posted $50.00, now $55.00
```

[Pool()](https://services.dev/docs/people/pool.md) covers every key, and [When no one claims](https://services.dev/docs/people/pool/fill.md) the ladder.

---

- Up: [The legal example](https://services.dev/docs/examples/legal.md)
- Previous: [The rules table](https://services.dev/docs/examples/legal/rules.md)
- Next: [Provisional patent application](https://services.dev/docs/examples/legal/provisional-patent.md)
