---
title: "Dispatch"
url: https://services.dev/docs/people/pool/dispatch
captured: [task-dispatch.txt]
---
# Dispatch

URL: https://services.dev/docs/people/pool/dispatch

> Who sees a Task first. Every eligible member's feed, one member at a time in rank order, the owner's pick, or one named member, each with a default you can override.

Who sees a Task first, and for how long is it theirs alone?

## Four dispatches

| Dispatch | Who sees it | The default when |
|---|---|---|
| `feed` | Every eligible member's feed, at once. The first claim wins. | the Pool names no parties to check |
| `cascade` | One eligible member at a time, in rank order, each for a window. A pass moves it on at once. | `clear.check` names parties, so fewer members read a sheet with party names on it |
| `pick` | The member the owner or a staff role names: `task.offer` | never: you choose it |
| `direct` | One named member, from the client's direction, offered it alone | the client named them |

Set `dispatch` on the Pool or on one role to override the default. `{ cascade: 3 }` offers each round to three members at once.

## One member at a time

The legal example names parties, so its Tasks go by `cascade`. One member turns work around the same day and another in two days, so the faster one is offered it first. A pass hands it on at once, and a window that runs out hands it on too:

```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
```

The sandbox's eligible member is last in every `cascade`, so a walk always ends with a member who can take it.

## One order for every brand

A member's feed lists every Task offered to them in one order, whichever brand posted it: fit first, then the fee per estimated hour, then the due date. Placement, the brand that runs the Pool and a brand's plan never move a Task up. Only `affinity` gives first refusal, and an exporter's preference for its own brands exists only as a declared [`export.first`](https://services.dev/docs/people/pool/share.md#export-your-pool), printed on every importer's `check` and on the door's `/terms`.

## What you are offered is yours to set

A member's own declarations filter their feed before any rank runs. None of them is ever scored:

| A member sets | Where | Keeps from them |
|---|---|---|
| `join.areas` | at join, and in the portal | Tasks outside the kinds of work they named |
| `join.min_fee` | at join, and in the portal | Tasks below the least they would take |
| `join.hours` | at join, and in the portal | offers past their spare hours this week |
| `join.turnaround` | at join, and in the portal | Tasks due sooner than they can turn them around |
| `member.availability` | `POST /me/availability`: on, off or away | every offer, while off or away |
| `member.brands` | the portal's brand switches | every Task a brand they turned off posts |

What those declarations kept from them is theirs to read, at `/me/missed`, and never a Task eligibility kept from them:

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

## Rank

`cascade` ranks eligible members by `rank`: `fit` (what they declared at join: areas, minimum fee, hours), then `track`, then `speed`. A member still on probation ranks by fit and speed alone until their `probation.tasks` are done. Tiers sit above rank and offer to the top eligible tier first. Neither ever overrides eligibility.

`affinity` gives the member who did a subject's last Task first refusal on its next one, for its window. A member who is ineligible by then is skipped.

## The owner's pick, and a direct

The owner, or a staff role, names the member a Task goes to from the console. The member must be eligible. A client's direction after a decline names the next member the same way, and they are offered it alone.

## Keys

| Key | Written | Left out | Does |
|---|---|---|---|
| `dispatch` | `Dispatch` | `'feed'`, or `'cascade'` when `clear.check` names parties | How this Pool's Tasks reach members. |
| `rank` | `('fit' \| 'track' \| 'speed' \| 'price')[]` | [`'fit'`, `'track'`, `'speed'`] | How `cascade` ranks members. A member on probation ranks by fit and speed alone. |
| `affinity` | `Within` | none | First refusal on follow-on work for the same subject: the Service's subject, or until it names one, the job a job was ordered again from. |
| `window` | `ShortSpan` | `'1h'` | How long one member has a `cascade`'s offer, or a first refusal, to themselves. |

Worked in: [the closers' Pool](https://services.dev/docs/examples/sales/pool.md), ranked by fit, then track.

---

- Up: [Pool(): your marketplace](https://services.dev/docs/people/pool.md)
- Previous: [Eligibility](https://services.dev/docs/people/pool/eligibility.md)
- Next: [Tasks: offered, never assigned](https://services.dev/docs/people/pool/tasks.md)
