---
title: "The delivery portal"
url: https://services.dev/docs/app/members
captured: [legal-fill.txt, task-sheet.txt, task-teaser.txt]
---
# The delivery portal

URL: https://services.dev/docs/app/members

> Every Pool gets a portal for its members, phone-first. It opens on the Pool's terms for them; Tasks are offered, never assigned; the packet shows the document they sign; and each act runs only in the member's own session, paid at the act whether they sign, send it back or decline. Their dates sit beside their Tasks, with the clocks they acknowledge.

The delivery portal is where the people in a [Pool](https://services.dev/docs/people/pool.md) take the Tasks your Services offer them. AI does the work; a member reviews, signs or advises where your [rules](https://services.dev/docs/people/rules.md) give the step to a credentialed person. It serves phone-first on the Pool's [door](https://services.dev/docs/people/pool/door.md):

```bash
npx services.dev dev --members 12 --market tight --no-try
```

```txt title="magic values: client.email = signs@example.test"
  attorneys           http://localhost:8787/attorneys · join at /attorneys/join, or npx services.dev signers add attorneys <email>
```

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

## A member's terms

Every Pool holds these terms, from the [member charter](https://services.dev/docs/people/pool/door.md#the-member-charter), each proved:

| Term | What enforces it | Proved in |
|---|---|---|
| You are never charged to work: no fee per Task, per lead or by a share. | No ledger line charges a member. The one member charge a Pool may carry is a flat software fee, shown before you join. | `check` fails `member_charged` on any line that charges a member to work |
| Every act the gate accepts is paid: an attestation, a send-back, a decline or a completed visit. | Pay accrues at the act (pay.accrued), is payable 2d later and is paid on the Pool's schedule. Only a contest ruling that records bad faith takes it back: [Pay](https://services.dev/docs/people/pool/pay.md#three-moments). | `member-acts`, `legal-decline`: `pay.accrued` at each act |
| Your no is final. | A decline is paid and never scored. Only the client directs what happens next, and the member who declined can never be named for it again. | `legal-decline`: `needs_direction`, and the decliner refused when named |
| No one reads your conflicts. | Your ledger holds HMACs under a key that is yours alone. The owner reads that a check ran, never what it found. | `member-ledger`: the owner reads no hash of it |
| Your contact never reaches a party. | Everything between you and a party goes through the job thread, and an email or phone number in it is withheld both ways. | `ask-the-client`: the email withheld |
| A pass is free, and never scored. | A pass counts against the Task. Only a quorum of passes for one reason moves its fee, due date or reach. | `task-sheet`: passes counted on the Task |
| A fee never drops. | The fee is fixed when the Task posts. The fill ladder only raises it. | `legal-fill`: the ladder raises, never lowers |
| A job that ends under you after you cleared its Task pays 50% of the fee. | The kill share (kill: '50%') accrues when a cleared Task is withdrawn, or a claimed visit is cancelled inside `notice`. | the Pool's `kill`, accrued as `pay.accrued` at the withdrawal |

The same terms open the Pool's `/join`, before anyone gives an email:

*Screen: the delivery portal at `/attorneys/join`, nothing waiting (captured/screens/legal-delivery-join-1.html).*

## Joined, and verified

A member is offered nothing until [the verifier](https://services.dev/docs/people/pool/verify.md) writes what it checked. Their portal shows the card:

*Screen: the delivery portal at `/attorneys`, nothing waiting (captured/screens/legal-delivery-verified.html).*

## What it derives from

Each screen is drawn from the [operations registry](https://services.dev/docs/reference/operations.md) rows a member's own session calls; no key, agent or console acts for the member. Each act and its pay: [what a person can do](https://services.dev/docs/people/acts.md#the-acts).

| Operation | Does | HTTP |
|---|---|---|
| `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. | `POST /jobs/:id/send_back` |
| `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. | `POST /:portal/jobs/:id/approve` |
| `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. | `POST /:portal/join` |
| `member.availability` | Set your availability: on, off or away. Only a member who is on is offered work. | `POST /me/availability` |
| `member.ledger.get` | Read your conflicts ledger: hashes only, salted with your id, never pooled and never seen by the brand. | `GET /me/ledger` |
| `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. | `POST /me/ledger` |
| `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. | `GET /me/ledger/checks` |
| `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. | `GET /me/missed` |
| `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. | `GET /me/earnings` |
| `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. | `GET /me/track` |
| `task.list` | Your feed: every open Task offered to you that you can take, newest first, as teasers. The owner console reads every Task. | `GET /tasks` |
| `task.get` | Read a Task's teaser: its teaser fields, the estimate, the rate and when it's due. Never a party name. | `GET /tasks/:id` |
| `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. | `GET /tasks/:id/sheet` |
| `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. | `POST /tasks/:id/pass` |
| `task.claim` | Claim a Task: the eligibility predicates run in order, and it is held for you until the Pool's hold runs out. | `POST /tasks/:id/claim` |
| `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. | `POST /tasks/:id/clear` |
| `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. | `GET /tasks/:id/packet` |
| `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. | `POST /tasks/:id/release` |
| `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. | `POST /tasks/:id/arrive` |
| `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. | `POST /tasks/:id/complete` |
| `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. | `POST /tasks/:id/attest` |
| `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. | `POST /tasks/:id/send_back` |
| `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. | `POST /tasks/:id/decline` |
| `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. | `POST /tasks/:id/recuse` |
| `task.extend` | Extend your hold on a Task, once: by as long again as it first held, never past its due date. | `POST /tasks/:id/extend` |
| `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. | `POST /tasks/:id/contest` |
| `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. | `POST /tasks/:id/messages` |
| `member.subjects` | The subjects where you are of record: you stay of record across jobs until you withdraw. | `GET /me/subjects` |
| `subject.withdraw` | Withdraw as the member of record, naming your successor: a member who holds the role's credential takes the seat. | `POST /subjects/:id/withdraw` |
| `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. | `GET /clocks` |
| `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. | `GET /clocks/:id` |
| `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. | `POST /clocks/:id/ack` |
| `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. | `POST /clocks/:id/satisfy` |
| `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. | `POST /clocks/:id/choose` |

| Path | Shows |
|---|---|
| `/<pool>` | One inbox in fixed groups: **Needs you**, **Your dates**, **Offered to you**, **Waiting on the client**, **Done this week**, then the subjects where the member is of record. Above it, the member's card, availability, earnings and the tier |
| `/<pool>/join` | The terms, then the application, one step at a time, resumed by email |
| `/tasks/:id/sheet` | The parties and the kind of work, and what clearing attests; then claim or pass |
| `/tasks/:id/packet` | Once cleared: what they sign, each file by its hash with its versions and what changed, what the review found, the client's answers, the pay and the clock; then the acts |
| `/tasks/:id/visit` | In a [work register](https://services.dev/docs/people/pool/register.md): the window, the site once claimed, check in, each evidence item, the party's sign-off on the member's phone, and check out |
| `/<pool>/earnings` | Every accrual: its act, its fee, when it's payable, and the payout that moved it, a line per brand |
| `/<pool>/track` | The tier rule, and how far the next tier is |
| `/me/missed` | The open Tasks a member's own declarations kept from them |

## How offers reach a member

A Task arrives as an offer event carrying only its teaser, never a party. It lands in the inbox while the member is on; away or off, nothing is offered:

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

```json title="200 · magic values: client.email = signs@example.test, member = mbr_eligible"
{
  "id": "tsk_7Hq2",
  "object": "task",
  "livemode": false,
  "pool": "attorneys",
  "role": "attorney",
  "status": "open",
  "fields": {
    "act": "sign",
    "region": "US-NY",
    "fee_usd": 75,
    "signals": [
      "collections",
      "recipient_elsewhere"
    ]
  },
  "est": {
    "minutes": 30,
    "from": "step"
  },
  "hourly_usd": 150,
  "due": "2026-10-27T14:02:11.000Z",
  "simulated": true
}

# the push payload: the review.offered event a signer receives
{
  "type": "review.offered",
  "data": {
    "task": "tsk_7Hq2",
    "offer": "cascade",
    "teaser": {
      "id": "tsk_7Hq2",
      "object": "task",
      "livemode": false,
      "pool": "attorneys",
      "role": "attorney",
      "status": "open",
      "fields": {
        "act": "sign",
        "region": "US-NY",
        "fee_usd": 75,
        "signals": [
          "collections",
          "recipient_elsewhere"
        ]
      },
      "est": {
        "minutes": 30,
        "from": "step"
      },
      "hourly_usd": 150,
      "due": "2026-10-27T14:02:11.000Z",
      "simulated": true
    }
  }
}

# no party in either: ada lovelace absent · ada@example.com absent · initech absent · ap@initech.example absent
```

A `cascade` offer is the member's alone for its window. A window let close moves to the next member and is never scored.

## When the fee or the scope isn't right

A [pass](https://services.dev/docs/reference/operations.md#taskpass) is free and counted against the Task, never the member. A quorum of passes for one reason fires the matching rung early: three fee passes raise the fee now, not at two hours:

```txt
→ GET /reviews/tsk_7Hq2/sheet   # as mbr_7Hq4
```

```txt title="magic values: client.email = signs@example.test, member = mbr_eligible"
← 200 sheet · fee $75.00
→ POST /reviews/tsk_7Hq2/pass {"reason":"fee"}   # as mbr_7Hq4
← 200 tsk_7Hq2 · open · attorney · sign · US-NY · $82.50 · signals collections, recipient_elsewhere · 30 min · $165.00 an hour · due 2026-10-27 14:02

→ GET /owner/reviews/tsk_7Hq2   # the owner console
← passes {"fee":3} · viewers 3 · posted $75.00, now $82.50
  fired  raise 10% 2h · 14:02 · raised to $82.50 · jumped
```

What their own minimum fee, areas or hours kept from them is at `/me/missed`, and [what they are offered is theirs to set](https://services.dev/docs/people/pool/dispatch.md#what-you-are-offered-is-yours-to-set):

*Screen: the delivery portal at `/me/missed`, nothing waiting (captured/screens/legal-delivery-missed.html).*

## Clear, then the packet

The inbox, the sheet, then the packet once the member [claimed](https://services.dev/docs/reference/operations.md#taskclaim) and [cleared](https://services.dev/docs/reference/operations.md#taskclear) it. [Every screen, in order](https://services.dev/docs/examples/legal/delivery-portal.md) walks the rest.

*Screen: the delivery portal at `/attorneys`, tsk_7Hq2 · open (captured/screens/legal-delivery-inbox.html).*

*Screen: the delivery portal at `/reviews/tsk_7Hq2/sheet`, tsk_7Hq2 · open (captured/screens/legal-delivery-sheet.html).*

## What they are signing

The packet opens on the document itself: each file the AI wrote, by hash, with its versions and what changed. The attestation binds the packet's hash:

*Screen: the delivery portal at `/reviews/tsk_7Hq2/packet`, job_7Hq2 · waiting at sign · with the member (captured/screens/legal-delivery-packet.html).*

## The dates they answer for

**Your dates** puts each open Task's due date beside its job's [clocks](https://services.dev/docs/service/clocks.md), with every clock the member answers for, late first, then today, this week and later. Each line is read from the clock's own keys:

| The clock declares | The member reads | Their control |
|---|---|---|
| `owner: 'of_record'` with them of record, a role they did on the job, or the owner console's [assign](https://services.dev/docs/reference/operations.md#clockassign) | yours | the two below, on that clock only |
| `rungs: ['notice 30d']` | the next rung's date: notice 16 Mar | — |
| `rungs: ['ack daily 7d']` | acknowledge each day from that date | [Acknowledge today](https://services.dev/docs/reference/operations.md#clockack), inside the window |
| `rungs: ['run tentative 3d']` | the step that runs itself, and when | — |
| `lapse: [...]` | was due, at the date, unsatisfied | one button per option, a [choice](https://services.dev/docs/reference/operations.md#clockchoose) |
| `moves: { extend: '6mo after due' }` | the new date, and the one it moved from | — |
| `lives_on: 'subject'` | the date stays with the subject after the job ends | — |

A clock no one can answer for never reaches a member; the [owner console](https://services.dev/docs/operate/dates.md) raises it.

*Screen: the delivery portal at `/attorneys`, no clock (captured/screens/app-member-deadlines.html).*

In [the tax example](https://services.dev/docs/examples/tax/delivery-portal.md), an extension moved a date, and the portal says from when. The subjects the member is of record on follow:

*Screen: the delivery portal at `/practitioners`, tsk_7Hq1 done · after the extension (captured/screens/tax-delivery-task.html).*

## Getting paid

`/<pool>/earnings` lists every accrual with its act, its fee and when it's payable, under the terms above:

*Screen: the delivery portal at `/attorneys/earnings`, nothing waiting (captured/screens/legal-delivery-earnings.html).*

Payouts land on the Pool's schedule, to one payout account for every Pool and brand. [Pay](https://services.dev/docs/people/pool/pay.md) has every schedule.

Worked in: [closing on closers.deals](https://services.dev/docs/examples/sales/delivery-portal.md).

---

- Up: [Your first paid job](https://services.dev/docs.md)
- Previous: [The customer portal](https://services.dev/docs/app/buyers.md)
- Next: [The owner console](https://services.dev/docs/app/owner.md)
