---
title: "The door: where members join and work"
url: https://services.dev/docs/people/pool/door
captured: [pool-door.txt, pool-smallest.txt]
---
# The door: where members join and work

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

> A Pool's door is where its members find the work, join, read the terms and do their Tasks. It serves at /<pool> on your brand, on a domain you operate, or on an existing door, and every door keeps one member charter.

A door answers one question: where do a Pool's people find the work, join, read the terms and do their Tasks?

Every Pool has one. Its delivery portal renders there, under the door's name, so buyers never see the door and members never see services.dev. Moving a Pool's door changes the host, never a path: the portal is `/<pool>` wherever it serves, and the old `/<pool>` on the brand redirects to it, path and all. Which door to choose, with its plan and its placement line, is in [where a Pool's members come from](https://services.dev/docs/people.md#where-a-pools-members-come-from).

## A Pool with no door

A first Pool needs no second domain. With `door` left out, the Pool serves on the Service's brand: its portal at `/<pool>`, with `/join`, `/terms` and `/sample` under it, in the brand's words. On localhost, `dev` serves every Pool this way, door or not:

```bash
npx services.dev dev --no-try
```

```txt title="magic values: none"
  translators  http://localhost:8787/translators · join at /translators/join, or npx services.dev members add translators <email>
```

## Use an existing door

An existing door already recruits your profession, verifies its people and speaks their words. Name it, and your Pool lists there:

```bash
cat pools/translators.ts
```

```txt title="magic values: none"
import { Pool } from 'services.dev'

// The translators work on an existing door. gigs.expert already recruits sworn translators and verifies each
// one, so the Pool names it as its door and leaves its own verifier out: a member verified there is verified
// here. The Pool keeps its own roles, pay, dispatch and fill ladder, and publishes its open Tasks on the board.
export const translators = Pool({
  name: 'translators',
  door: 'gigs.expert',
  roles: { translator: { credential: 'sworn translator' } },
  join: { languages: 'language[]: each language you are sworn in' },
  public: true,
})
```

- **One application.** A member joins the door once; your Pool asks only what `join` adds.
- **One verification.** Leave `verify` out and the door's verifier writes the facts, at the door's freshness. Name your own, and both must hold.
- **Your market rules.** Roles, pay, dispatch, what a member sees, the fill ladder and tiers stay in your Pool file.
- **The door's name.** Members see the door's brand and words; each Task names the brand that posted it.

Every door publishes its terms, versioned, as `/door.json`:

```bash
curl https://gigs.expert/door.json
```

```json title="magic values: none"
{
  "door": "gigs.expert",
  "version": "2026-10",
  "operator": "gigs.expert",
  "accepts": ["sworn translator", "certified interpreter", "notary translator"],
  "placement": { "translator": 6, "interpreter": 9 },
  "verify": "gigs.expert's verifier",
  "fresh": "30d",
  "words": { "Task": "assignment", "member": "expert" },
  "charter_additions": ["A brand's first Task to you pays within one day of the act."],
  "notice": "30d"
}
```

`door:` pins that version. `check` reads it back with the line it charges, and a change reaches your Pool only after the door's notice:

```bash
npx services.dev check
```

```txt title="magic values: none"
  door    gigs.expert · operated by gigs.expert: its roster, its /join, its terms and its verifier
  terms   gigs.expert@2026-10 · placement $6.00 a filled Task · accepts sworn translator · 30 days' notice before a change
  portal  https://gigs.expert/translators · its home, /join, /terms and /sample are the door's
  board   https://gigs.expert/board · open Tasks by role and region, the rate earned and how fast they were paid
  facts   verified by gigs.expert's verifier · fresh 30d · your Pool adds 1 question: languages
  roles   translator · sworn translator · from translators on gigs.expert · $30 an act
  ledger  +$89.00 · AI $0.84 (example) · translator $30.00 · placement $6.00 to gigs.expert · you $52.16
          placement is gigs.expert's line on each Task its members fill · never from the member's fee
```

A door can refuse a listing. A role whose credential the door doesn't accept fails with `door_declined` and the door's reason:

```bash
npx services.dev check   # after adding a role whose credential the door declined
```

```txt title="magic values: none"
pools/translators.ts · 9 lines · translators · 2 roles: translator, notary · joins in 1 step on gigs.expert
  ✗ door_declined: gigs.expert doesn't list notary public.
    Its door.json accepts sworn translator, certified interpreter and notary translator.
    Give notary one of those, or move it to a Pool with no door, at /notaries on your brand.
✗ 1 to fix
```

Choose an existing door to work its people under your own market rules. When its rules suit you as they are, [import its Pool](https://services.dev/docs/people/pool/share.md#import-a-pool-another-operator-runs) instead.

## What a door serves

The same routes on every door. With no door they sit under `/<pool>` on the brand.

| Route | Shows |
|---|---|
| `/` | [the deal](#what-a-member-reads-on-the-doors-home), before anyone gives an email |
| `/join` | the terms, then one application for every Pool on the door, one step at a time, resumed by email, ending on the payout account |
| `/terms` | [the member charter](#the-member-charter) first, then the door's additions, then this Pool's own |
| `/sample` | [one Task, start to pay](#a-task-start-to-pay) |
| `/board` | with `public: true`, [the demand board](#the-demand-board) |
| `/door.json` | the door's terms, versioned, for `check` and for agents |
| `/<pool>` | [the delivery portal](https://services.dev/docs/app/members.md), for a signed-in member |
| `/llms.txt` and `.md` twins | the same pages for agents, the board included |

## The member charter

These ten lines open every door's `/terms`, in this order, before any term a Pool adds:

1. You are never charged to work. Any flat software fee is shown before you join.
2. A Task's fee is fixed when it posts, and it only rises.
3. Attest, send back, decline or complete: each is [paid](https://services.dev/docs/people/pool/pay.md).
4. A pass is free and never counted against you.
5. A decline is never scored.
6. Your [track record](https://services.dev/docs/people/pool/track.md#yours-to-carry) is yours, and you can export it.
7. No party gets your email or phone.
8. Pay accrues at the act, and no buyer claws it back. The one exception is a recorded finding of bad faith, which [pay](https://services.dev/docs/people/pool/pay.md#three-moments) defines.
9. Every brand's Tasks reach you in [one neutral feed order](https://services.dev/docs/people/pool/dispatch.md#one-order-for-every-brand).
10. Turn off any brand, or leave any Pool, and keep your pay and your record.

A Pool, an importer and a door operator may add terms. None may remove or narrow one of these.

| `check` gate | Why |
|---|---|
| [`charter_weakened`](https://services.dev/docs/reference/check.md#gates-on-the-money-and-the-live-brand) | A Pool's keys, an import or a door's `charter_additions` would remove or narrow a line of the member charter, such as a fee that falls, a scored decline or a charge per Task. |

## A Task, start to pay

`/sample` walks one Task as a member lives it, and names the member's control at each step:

```bash
curl http://localhost:8787/translators/sample.md
```

## What a member reads on the door's home

| The home shows | Read from |
|---|---|
| each role, the credential it needs and the regions it serves | the Pools' `roles` and `scopes`, and `/door.json` |
| the fee range and the median rate members earned per hour | posted fees, and 30 days of accepted acts |
| when pay lands, and any hold on a new member's first Tasks | [`payouts`](https://services.dev/docs/people/pool/pay.md#every-schedule) and `hold_first` |
| coverage: who carries it, and how much per act | [`coverage`](https://services.dev/docs/people/pool/coverage.md) |
| the member fee, or "none" | `fee` |
| every brand that posts here, each with its off switch | the Pools listed on the door, and every importer |
| one application for every Pool on the door | `/join` |

## The demand board

With `public: true`, the door publishes every open Task by role and region, with what members earned per hour, how fast they were paid and where supply is short. It never names a party:

```bash
curl http://localhost:8787/translators/board.md
```

It shows the same numbers as [Pool health](https://services.dev/docs/app/owner.md) in the owner console, less anything about a single job.

## Grow your supply

`grow` fills a Pool before the work outruns it:

| `grow` | Does |
|---|---|
| `waitlist: true` | A region or role with no open demand takes applications onto a waitlist. Each applicant is verified once demand opens, and the board says where it is short. |
| `referral_usd` | A member who refers another earns this when the new member finishes probation. [Pay](https://services.dev/docs/people/pool/pay.md#other-lines-a-member-earns) lists it. |
| `invites` | Each member may invite this many people, who skip the waitlist but never verification. |

## How it earns

A door your account operates adds no line. One another operator runs earns [placement](https://services.dev/docs/people/pool/share.md#the-placement-line) on each Task its members fill.

## Every form

| Key | Written | Left out | Does |
|---|---|---|---|
| `door` | `string` | `/<pool>` on the Service's brand | The door's domain: one your account operates, where the supply Site is generated, or an existing door the Pool lists on, pinned at its `/door.json` version. |
| `public` | `boolean` | none | Publish the demand board on the door and in its llms.txt. |
| `verify` | `Url \| Fn` | the existing door's verifier | The writer of verified facts. On a door of your own, or with no door, it is required. |
| `grow` | `{ waitlist?: true; referral_usd?: number; invites?: number }` | none | Supply growth: a waitlist, a referral bonus, invites per member. |

## Three brands

| Brand | Pool | Door | One door, several brands |
|---|---|---|---|
| api.lawyer | attorneys and patent agents | [gigs.lawyer](https://gigs.lawyer) | serves api.lawyer, patent.click and incorporations.legal from one roster; gigs.attorney redirects to it |
| apis.tax | preparers, signers and representatives | [gigs.tax](https://gigs.tax) | one application and one verified PTIN for every tax Pool on the door |
| api.forsale | setters, closers and nurturers | [closers.deals](https://closers.deals) | closers work there, for every seller brand on it; closers.sale and setters.sale recruit to it, and gigs.sale routes each visitor |

A vertical door exists only where a profession recruits in different words. Each example's delivery portal shows its door: [legal](https://services.dev/docs/examples/legal/delivery-portal.md), [tax](https://services.dev/docs/examples/tax/delivery-portal.md), [sales](https://services.dev/docs/examples/sales/delivery-portal.md).

---

- Up: [Pool(): your marketplace](https://services.dev/docs/people/pool.md)
- Previous: [Join: the application](https://services.dev/docs/people/pool/join.md)
- Next: [Verify a member](https://services.dev/docs/people/pool/verify.md)
