Skip to main content
Someone comments on a document, and everyone watching it should hear about it. This guide models watchers as a list per entity. A list_id is a dotted namespace, so document.doc_a1b2.watchers names the watchers of one document.

Prerequisites

  • A for each user you subscribe
  • A published to send

Name the list after the entity

Pick a naming convention before you write any code, because the name is the only thing tying a list to the thing it belongs to. A list_id holds up to six dot-separated segments:
Keeping the entity type in the first segment is what makes useful later. Entity ids with dots in them won’t work, since a dot starts a new segment.

Wire up watching

1

Subscribe a user when they start watching

One call, and it is idempotent. creates the list if it doesn’t exist, so you never check first.
Call this wherever your app already records a watch: an explicit Watch button, or implicitly when someone comments or is assigned.
2

Notify everyone watching

Address the list instead of a user. Courier fans out to every subscriber and applies each recipient’s own .
One send reaches every watcher, however many there are. You don’t fan out yourself.
3

Unsubscribe when they stop watching

removes one watcher and leaves the rest alone.
When the entity itself is deleted, delete the whole list with rather than removing watchers one at a time.

Ask who is watching what

Both directions of the relationship are readable, which is what makes this usable as your app’s own watch state rather than a write-only copy of it. Both are .

Reach every entity of a kind

Because the entity type leads the list_id, one pattern can address many lists at once. Swap list_id for list_pattern, where * matches a single segment:
That reaches everyone watching any document, without you listing them.
A recipient watching several matched entities is deduplicated, so nobody receives the same message twice from one send.

Per-watcher preferences

A subscription can carry its own preferences, which apply to that subscription alone. Someone can follow a noisy project on digest while staying on instant notifications everywhere else. See for the status model.

Verify

1

Subscribe two users

Subscribe two test users to document.doc_a1b2.watchers.
2

Read the list back

Call and confirm both appear, which also proves the list was created by the subscribe call.
3

Send and check the logs

Send to the list, then open . You should see one message per subscriber, each with its own status.
4

Unsubscribe one and send again

The second send reaches one recipient. The unsubscribed user gets nothing.

FAQ

Subscribing creates the list when it doesn’t exist, so a Watch button is one call with no setup and no existence check.
Courier applies each recipient’s preferences during fan-out, so an opted-out watcher is filtered and the rest still receive the message. The send doesn’t fail.
Lists are built for fan-out, so a busy document with thousands of watchers is one send. Read members back with cursor pagination rather than all at once.
Add a MEMBER_OF filter on the recipient alongside context.tenant_id. Tenant context alone sets branding and preferences without restricting who receives the message. See .
Use an audience when membership follows from profile data, such as every user on the Pro plan. Watching is an explicit act that no profile field implies, so a list is the right shape.