The person API
Build an app that works as the person who signs it in: it shows their cards, answers one when they press something in it, and keeps their phone quiet while they’re there. Herdr’s plugin is the first.
What an app may do
An app acts as the person, and only when they press something in it. Pending You never answers for them, and neither may your app.
It may
- See the cards the person’s assistants ask them, on one computer or all of them.
- Answer a card, write to its assistant, put it in Later, or hand it to another assistant.
- See which assistants are running, and in which terminal.
- Keep the person’s phone quiet while they’re using it.
It never may
- Act on a high-stakes card (it spends money, can’t be undone or goes public). It can show one: send the person to the card’s
url. - Answer on a timer, from a model’s decision, or for a card the person hasn’t seen.
- See or change their account, sign-in, billing, secrets or assistants.
Apps and review
Every app is registered with Pending You. Register yours in Settings › Developers: it works for you at once, and for everyone once we’ve reviewed it.
- Yours works for you at once.An app you register works for your own account and up to 10 testers while it waits for review.
- Everyone else needs it verified.Review checks who publishes it, its domain, its name and logo, its privacy policy, that its scopes match what it does, and that every answer comes from a press by the person.
- The person sees who it is.Its name, its publisher and whether it’s verified are on the approval, with what it may do in plain words.
- You agree to the developer terms.Use what your app sees only to serve the person who signed it in: never sell it or train models on it. Read the developer terms.
Register your app
In Pending You, open Settings › Developers and press Register an app. You get a client ID at once, and a client secret if your app is a website with a server.
- Name it, and say how it signs in.A terminal or script signs in with a code on a computer set up with Pending You. A desktop or phone app, a website with a server, or a single-page web app signs in with PKCE and comes back to one of its redirect addresses.
- List its redirect addresses, exactly.https on your website’s domain or one under it;
http://127.0.0.1orhttp://localhoston any port; or, for a desktop or phone app, its own scheme named for your domain backwards (example.acme.desk:/oauthfor acme.example). No wildcards, at most 10. - Choose the most it may ask for.Its scopes’ ceiling: a sign-in asks for some or all of them, and the person sees each in plain words. A web or desktop app has no computer of its own, so it always holds
cards:read:all. - Keep the secret on your server.A website’s client secret is shown once. Rotate it in Settings › Developers; the old one stops working at once.
Before review
- Prove your domain.Add a TXT record
pendingyou-app-verification=<token>to it, or put that line inhttps://<domain>/.well-known/pendingyou-app-verification.txt, then press Check now. Settings › Developers shows your token. - Add a privacy policy, terms and a support email.The policy says what your app keeps of people’s cards, and where. They stay as they are while we review it.
- Press Request review.We check who publishes it, its domain and redirect addresses, its name, its privacy policy, that its scopes match what it does, and that every answer comes from a press by the person. We write to its support email if something needs to change.
Signing in
OAuth 2.1 at Pending You, for the resource /v1. Terminals and plugins sign in by device code, proving which computer they’re on; web and desktop apps sign in with authorization code and PKCE (below).
- Make a key for the sign-in.A P-256 key that stays with your app (see DPoP below). Its thumbprint goes in the computer’s attestation, so the sign-in is bound to it from the start.
- Have the computer vouch for it, and start.
pendingyou machine attestsigns an attestation with the computer’s own key (made bynpx pendingyou init). A computer the person hasn’t set up can’t be allowed. - Show the code, and poll.The person allows it on their phone, on the card Pending You shows them, or at the link. Poll the token endpoint every
intervalseconds with a DPoP proof each time.
| curl -X POST https://www.pendingyou.com/oauth/device \ |
| -d client_id=YOUR-CLIENT-ID \ |
| -d scope='cards:read cards:answer' \ |
| -d resource=https://www.pendingyou.com/v1 \ |
| -d machine=build-01 \ |
| --data-urlencode machine_attestation="$(npx -y pendingyou@latest machine attest \ |
| --client-id YOUR-CLIENT-ID --cnf YOUR-KEY-THUMBPRINT --name build-01)" |
| { |
| "device_code": "Gq7…", |
| "user_code": "WDJB-MJHT", |
| "verification_uri": "https://www.pendingyou.com/device", |
| "verification_uri_complete": "https://www.pendingyou.com/device?code=WDJB-MJHT", |
| "expires_in": 600, |
| "interval": 3, |
| "client_id": "YOUR-CLIENT-ID" |
| } |
| curl -X POST https://www.pendingyou.com/oauth/token \ |
| -H "DPoP: $PROOF" \ |
| -d grant_type=urn:ietf:params:oauth:grant-type:device_code \ |
| -d device_code=Gq7… \ |
| -d client_id=YOUR-CLIENT-ID |
| { |
| "access_token": "usr_…:…:…", |
| "token_type": "DPoP", |
| "expires_in": 900, |
| "refresh_token": "usr_…:…:…", |
| "scope": "cards:read cards:answer" |
| } |
Example:Signing in with a code, in a whole appexamples/answer-cards/sign-in.ts
Sign in from a web or desktop app
A desktop app, a single-page app or a website signs in through the person’s browser: authorization code with PKCE (S256 only), for the resource /v1. The person sees your app’s card, with where it goes back to, and allows it.
- Make a key, a verifier and a state.A P-256 key for the sign-in (see DPoP below), a PKCE verifier, and a random
state. Name the key’s thumbprint asdpop_jkt(RFC 9449 §10), so the code is bound to it too. - Send the person to Pending You.Use one of your app’s registered redirect addresses, exactly. A desktop app may listen on any port of its registered
http://127.0.0.1address (RFC 8252), or come back through its own scheme. - Exchange the code.Check the
statethat comes back, then exchange the code with the verifier and a DPoP proof by your key. A desktop or single-page app must prove it; Pending You asks for a nonce first.
| https://www.pendingyou.com/oauth/authorize |
| ?response_type=code |
| &client_id=YOUR-CLIENT-ID |
| &redirect_uri=http://127.0.0.1:53682/callback |
| &scope=cards:read cards:read:all cards:answer |
| &state=RANDOM-STATE |
| &code_challenge=BASE64URL-SHA256-OF-THE-VERIFIER |
| &code_challenge_method=S256 |
| &resource=https://www.pendingyou.com/v1 |
| &dpop_jkt=YOUR-KEY-THUMBPRINT |
| http://127.0.0.1:53682/callback?code=usr_…:…:…&state=RANDOM-STATE&iss=https://www.pendingyou.com |
| curl -X POST https://www.pendingyou.com/oauth/token \ |
| -H "DPoP: $PROOF" \ |
| -d grant_type=authorization_code \ |
| -d code=usr_…:…:… \ |
| -d redirect_uri=http://127.0.0.1:53682/callback \ |
| -d client_id=YOUR-CLIENT-ID \ |
| -d code_verifier=THE-VERIFIER \ |
| -d resource=https://www.pendingyou.com/v1 |
- Every card.An app signed in this way has no computer, so it asks for
cards:readandcards:read:all, and sees every card the person’s assistants ask them. - One sign-in per copy.Signing in again with the same key replaces the sign-in; another copy of your app, with its own key, keeps its own. A website’s sign-in replaces the person’s earlier one.
- A website with a server.It exchanges its code with its client secret, and may skip DPoP: its tokens are then bearer tokens, which never leave its server.
| // A desktop app: the browser comes back to this computer. |
| import { fileStore, loopbackSignIn } from '@recordplane/pendingyou-sdk/node' |
| const signIn = await loopbackSignIn({ |
| origin: 'https://www.pendingyou.com', |
| clientId: 'YOUR-CLIENT-ID', |
| scopes: ['cards:read', 'cards:read:all', 'cards:answer'], |
| store: fileStore(`${process.env.HOME}/.config/example-app/pendingyou.json`), |
| open: (url) => openInBrowser(url), |
| }) |
| await signIn.signedIn |
| // A single-page app: away to Pending You, and back to its own page. |
| import { finishRedirectSignIn, redirectToSignIn } from '@recordplane/pendingyou-sdk' |
| await redirectToSignIn({ origin: 'https://www.pendingyou.com', clientId: 'YOUR-CLIENT-ID', redirectUri, scopes }) |
| // …then on redirectUri's page: |
| await finishRedirectSignIn({ store }) |
Example:A desktop app’s sign-in through the browser, in a whole appexamples/answer-cards/sign-in.ts
DPoP
A device sign-in’s tokens, and a desktop or single-page app’s, are bound to your app’s key (RFC 9449), so a copy of them is useless without it. Send each request with a proof made for it.
| curl https://www.pendingyou.com/v1/cards \ |
| -H "Authorization: DPoP $ACCESS_TOKEN" \ |
| -H "DPoP: $PROOF" |
- A new proof for every request.Its method and address (no query), the time, a random
jti, and at /v1 the access token’s SHA-256 (ath). ES256 only. - Nonces.Every answer carries
DPoP-Nonce: put the latest in your next proof. The token endpoint requires one; /v1 asks again withuse_dpop_noncewhen yours is old. Make the proof again with the new one and send the request once more.
| // One key for the sign-in: keep its private JWK with the tokens, nowhere else. |
| const key = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign']) |
| const { kty, crv, x, y } = await crypto.subtle.exportKey('jwk', key.publicKey) |
| const jwk = { kty, crv, x, y } |
| const b64url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))) |
| .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '') |
| const sha256 = async (text) => b64url(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))) |
| const part = (value) => b64url(new TextEncoder().encode(JSON.stringify(value))) |
| // What the sign-in is bound to (the attestation's --cnf): RFC 7638. |
| const thumbprint = await sha256(JSON.stringify({ crv, kty, x, y })) |
| // A proof for each request: its method and address, now, the token's hash, and the last nonce. |
| async function proof(method, url, accessToken, nonce) { |
| const header = { typ: 'dpop+jwt', alg: 'ES256', jwk } |
| const claims = { |
| jti: crypto.randomUUID(), |
| htm: method, |
| htu: url.split(/[?#]/)[0], |
| iat: Math.floor(Date.now() / 1000), |
| ...(accessToken ? { ath: await sha256(accessToken) } : {}), |
| ...(nonce ? { nonce } : {}), |
| } |
| const signing = `${part(header)}.${part(claims)}` |
| const signature = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, key.privateKey, |
| new TextEncoder().encode(signing)) |
| return `${signing}.${b64url(signature)}` |
| } |
Scopes
Ask for what your app does, no more. The person may allow less: every card, not just one computer’s, is a tick that starts off.
| Scope | What it allows |
|---|---|
cards:read | See the cards the assistants on the signed-in computer ask the person. |
cards:read:all | See every card, from all the person’s computers and assistants. |
cards:answer | Answer cards when the person presses something, and take an answer back in its 5 seconds. |
cards:reply | Write to the assistant that asked, on its card. |
cards:later | Put cards in Later and bring them back. |
cards:delegate | Hand a card to another of the person’s assistants, and take it back. |
assistants:read | See which assistants are running, and in which terminal. |
presence:desk | Keep the person’s phone quiet while they’re using the app. |
Endpoints
13 routes under https://www.pendingyou.com/v1, each with the scope it needs. Open one for its fields, its example and what it may refuse.
GET/v1/meWho and what this sign-in is
The person, their workspace, the app and this sign-in: its scopes, its reach (one computer or every card), its computer, and when it ends. Any scope.
| GET /v1/me |
| { |
| "user": { |
| "id": "usr_0a1b2c3d4e5f60718293" |
| }, |
| "workspace": { |
| "id": "ws_0a1b2c3d4e5f60718293" |
| }, |
| "app": { |
| "id": "app_00000000000000000002", |
| "name": "Pending You for Herdr", |
| "verified": true |
| }, |
| "grant": { |
| "id": "apg_7d6c5b4a39281706f5e4", |
| "scopes": ["cards:read", "cards:answer", "presence:desk"], |
| "reach": "machine", |
| "machine": { |
| "id": "mch_1e2d3c4b5a6978877665", |
| "name": "build-01" |
| }, |
| "dpop": true, |
| "createdAt": "2026-10-05T19:20:00.000Z", |
| "expiresAt": "2026-11-04T19:20:00.000Z" |
| } |
| } |
GET/v1/cardsThe cards in reach, one list at a time
Waiting on the person in the stack’s order (pending), in Later soonest back first (snoozed), with a helper (delegated), or done in the last 30 days newest first. Filters repeat (`?area=prj_…&area=prj_…`); pages follow `next`. The first `whole` cards come whole.
| Field ? optional | Type | What it’s for |
|---|---|---|
status? | "pending" | "snoozed" | "delegated" | "done" | |
area? | string[] | |
assistant? | string[] | |
agent? | string[] | |
app? | ("claude-code" | "codex" | "cursor" | "gemini-cli" | "opencode" | "pi" | "grok" | "muse" | "chatgpt" | "claude" | "dots" | "other" | "webhook")[] | |
machine? | string[] | |
threadId? | string | |
limit? | integer | |
whole? | integer | |
after? | string |
| GET /v1/cards?status=pending&limit=20 |
| { |
| "cards": [ |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "version": 3, |
| "status": "pending", |
| "turn": "you", |
| "kind": "choice", |
| "title": "Which queue should the nightly sweeper use?", |
| "summary": "Two queues can take it. The old one is slower but already has alerts.", |
| "urgency": "today", |
| "blocking": true, |
| "options": [ |
| { |
| "id": "a", |
| "label": "The new queue" |
| }, |
| { |
| "id": "b", |
| "label": "The old queue" |
| } |
| ], |
| "recommendedIds": ["a"], |
| "threadId": "req_4f1c9a0e2b7d6c5a8e31", |
GET/v1/cards/{id}One card, whole
A card out of reach is not found, exactly as one that doesn’t exist.
| GET /v1/cards/req_4f1c9a0e2b7d6c5a8e31 |
| { |
| "card": { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "…": "…" |
| } |
| } |
POST/v1/cards/{id}/answerAnswer a card
With the version you showed the person. Its assistant hears it once its 5 seconds are over; until `undoUntil` this app may take it back.
| Field ? optional | Type | What it’s for |
|---|---|---|
version | integer | |
choiceIds? | string[] | |
approved? | boolean | |
text? | string | |
note? | object | |
text | string | |
via? | "text" | "voice" | |
theyDecide? | true | |
answers? | map | |
failedStep? | integer | |
askId? | string |
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/answer |
| Idempotency-Key: 6f1d2c9e-… |
| { |
| "version": 3, |
| "choiceIds": ["a"], |
| "note": { |
| "text": "Keep the alerts on." |
| } |
| } |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "status": "answered", |
| "version": 4, |
| "undoUntil": "2026-10-06T14:05:05.000Z" |
| } |
POST/v1/cards/{id}/undoTake this app’s answer back
Within its 5 seconds, and only an answer given in this app.
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/undo |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "status": "pending", |
| "version": 5 |
| } |
POST/v1/cards/{id}/messagesWrite to the assistant that asked
The person’s words, on its card. They hand it the turn and it acts on them as the answer, so they count against the answers’ limit.
| Field ? optional | Type | What it’s for |
|---|---|---|
body | string | |
via? | "text" | "voice" |
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/messages |
| { |
| "body": "Ask Sam first: it’s their queue." |
| } |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "messageId": "msg_3c2b1a0f9e8d7c6b5a49", |
| "turn": "agent", |
| "version": 4 |
| } |
POST/v1/cards/{id}/laterPut a card in Later
Until a time (a minute to two months ahead) or a preset, `1h`, `tonight` or `tomorrow`, in `timeZone`.
| Field ? optional | Type | What it’s for |
|---|---|---|
until | ISO datetime | "1h" | "tonight" | "tomorrow" | A time, or a preset: in an hour, tonight (6:00 PM) or tomorrow morning (8:00 AM), in timeZone. |
timeZone? | string | The person’s IANA time zone, e.g. "America/Chicago", for the presets. Default UTC. |
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/later |
| { |
| "until": "tomorrow", |
| "timeZone": "America/Chicago" |
| } |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "status": "snoozed", |
| "until": "2026-10-07T13:00:00.000Z", |
| "version": 4 |
| } |
POST/v1/cards/{id}/backBring a card back from Later
Now, to its place in the stack. One already back answers with its version.
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/back |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "status": "pending", |
| "version": 5 |
| } |
POST/v1/cards/{id}/delegateHand a card to another assistant
One of the person’s assistants in reach (GET /v1/assistants). `freely`: its answer goes straight back; `loop`: it comes to the person first. Until `undoUntil` the helper can’t see it.
| Field ? optional | Type | What it’s for |
|---|---|---|
to | object | |
assistantId | string | |
agentId? | string | |
mode | "freely" | "loop" | |
note? | string |
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/delegate |
| { |
| "to": { |
| "assistantId": "con_2b3c4d5e6f708192a3b4" |
| }, |
| "mode": "loop", |
| "note": "Sam owns the queues." |
| } |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "status": "delegated", |
| "version": 4, |
| "hears": "while-working", |
| "undoUntil": "2026-10-06T14:05:05.000Z" |
| } |
POST/v1/cards/{id}/take-backTake a card back from its helper
Before the helper answers it: the card waits on the person again.
| POST /v1/cards/req_4f1c9a0e2b7d6c5a8e31/take-back |
| { |
| "id": "req_4f1c9a0e2b7d6c5a8e31", |
| "status": "pending", |
| "version": 5 |
| } |
GET/v1/changesWhat changed since a cursor
Ids and types only, in commit order, kept to the reach. Without `after`: from now. With `wait` (up to 25 seconds) it waits for the first change. Read GET /v1/cards again on start and every 10 minutes too: a card that comes into reach makes no change.
| Field ? optional | Type | What it’s for |
|---|---|---|
after? | string | |
wait? | integer | |
limit? | integer |
| GET /v1/changes?after=djEuNzAwMC4xMg&wait=25 |
| { |
| "changes": [ |
| { |
| "type": "card.created", |
| "cardId": "req_4f1c9a0e2b7d6c5a8e31", |
| "at": "2026-10-06T14:02:11.000Z" |
| } |
| ], |
| "cursor": "djEuNzAxMi4w", |
| "more": false |
| } |
GET/v1/assistantsThe assistants in reach
Each sign-in, its computer and how it hears answers, and its agents: live, recent or quiet, their open cards, and the sessions running now with the terminal each runs in. Live ones first.
| GET /v1/assistants |
| { |
| "assistants": [ |
| { |
| "id": "con_9f8e7d6c5b4a39281706", |
| "app": "claude-code", |
| "appName": "Claude Code", |
| "name": "Claude Code on build-01", |
| "machine": { |
| "id": "mch_1e2d3c4b5a6978877665", |
| "name": "build-01" |
| }, |
| "hears": "while-working", |
| "agents": [ |
| { |
| "id": "agt_6a5b4c3d2e1f0a9b8c7d", |
| "name": "sweeper", |
| "state": "live", |
| "liveAt": "2026-10-06T14:04:40.000Z", |
| "openCards": 1, |
| "liveSessions": [ |
| { |
| "appSessionId": "5b0c…", |
| "cwd": "/home/sam/code/sweeper", |
| "liveAt": "2026-10-06T14:04:40.000Z", |
POST/v1/presenceSay the person is at this app
`here: true` every 30 seconds while they’re active in it (it lasts 75), `here: false` when they leave. Meanwhile their phones stay quiet, as with Pending You open on a computer. Answers 204.
| Field ? optional | Type | What it’s for |
|---|---|---|
here | boolean |
| POST /v1/presence |
| { |
| "here": true |
| } |
| 204 No Content |
- Reads have ETags.Send the ETag back as
If-None-Matchand a read that hasn’t changed answers 304. - Answers need an Idempotency-Key.A new key for each answer (every other change to a card takes one too). The same key and request within 24 hours get the first answer again, so a retry never answers twice.
- Show the version you answer.An answer names the card’s
version. If the card changed meanwhile, it’s a 409version_conflict: show it again and ask.
Example:Showing a card and answering it from the person’s key pressexamples/answer-cards/answer.ts
The change feed
GET /v1/changes says what changed since a cursor, in commit order: ids and types only. Read the cards you show again.
| Type | When |
|---|---|
card.created | An assistant asked something new. |
card.answered | Someone answered it: the person, here or elsewhere, or a helper. |
card.changed | Anything else: words, Later, handed over, back, updated. |
card.closed | Its assistant picked the answer up, withdrew it, or it ran out. |
- Wait for the next change.
waitholds the request up to 25 seconds until something changes. Keep thecursorand pass it back asafter; without one, the feed starts from now. - Read the cards again every 10 minutes.On start, and every 10 minutes, read GET /v1/cards too: a card that comes into reach makes no change of its own.
Example:Following the feed, and passing each change to your own serverexamples/change-feed/watch.ts
Errors
Refusals are problem details (RFC 9457), application/problem+json, with a stable code. Sign-in’s own refusals are OAuth’s, with WWW-Authenticate.
| { |
| "type": "https://www.pendingyou.com/docs/api/errors#high_stakes", |
| "title": "This card is high stakes", |
| "status": 403, |
| "code": "high_stakes", |
| "detail": "Answer it in Pending You: it spends money, can’t be undone or goes public.", |
| "requestId": "rid_0b5e8c1d2a3f4e5d6c7b", |
| "url": "https://www.pendingyou.com/app/r/req_4f1c9a0e2b7d6c5a8e31" |
| } |
Limits
Past one: 429 rate_limited with retry-after. They’re per sign-in, and per person for all their apps together.
| Limit | A minute | Counts |
|---|---|---|
| Reads | 300 | Every GET |
| Writes | 60 | Every POST |
| Answers | 30 | Answers, and words on a card |
| Waits | 6 | GET /v1/changes that waits |
| Per person | 1,200 | Every call of every app |
Versions
/v1 only adds: paths, optional fields, answer fields and new values. Ignore what you don’t know.
- Changes in behaviour are dated.Send
Pending-You-Version: 2026-10-05to pin one; every answer says which it followed. Anything that can’t be added waits for /v2. - The OpenAPI document.OpenAPI 3.1 at
/v1/openapi.json, made from the same definitions the API serves. Device sign-in and DPoP are inx-pendingyou-deviceandx-pendingyou-dpop.
The SDK
@recordplane/pendingyou-sdk does all of this for you: device sign-in, sign-in by code with PKCE, DPoP and nonces, refreshes, ETags, Idempotency-Keys and the change feed. No dependencies; Node 20 or later, browsers and Workers.
| import { createClient, deviceSignIn } from '@recordplane/pendingyou-sdk' |
| import { cliAttester, fileStore } from '@recordplane/pendingyou-sdk/node' |
| const store = fileStore(`${process.env.HOME}/.config/example-app/pendingyou.json`) |
| const signIn = await deviceSignIn({ |
| origin: 'https://www.pendingyou.com', |
| clientId: 'YOUR-CLIENT-ID', |
| scopes: ['cards:read', 'cards:answer'], |
| machine: 'build-01', |
| attest: cliAttester(), |
| store, |
| }) |
| console.log(`Allow it at ${signIn.verificationUriComplete}`) |
| await signIn.signedIn |
| const pendingYou = createClient({ store }) |
| for await (const batch of pendingYou.changes()) { |
| if (batch.resync) await showAll(await pendingYou.cards.list()) |
| for (const change of batch.changes) await show(await pendingYou.cards.get(change.cardId)) |
| } |
Example:Every call Pending You for Herdr makes, as a terminal queueexamples/herdr-queue/
Example:Every example, with how to run itexamples/
Changelog
| Version | What changed |
|---|---|
2026-10-05 | The first version: the queue, answers and undo, words, Later, handing over, assistants, presence and the change feed. |