> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Lists

> Curated groups of users, addressed with list_id, including list ID patterns and wildcards.

export const Endpoint = ({method, path, name, href, children, bare}) => {
  const verb = String(method || "").toUpperCase();
  const title = verb + " " + path;
  const label = children || name || path;
  if (bare) {
    return href ? <a href={href}><code>{title}</code></a> : <code>{title}</code>;
  }
  if (!href) {
    return <span className="cx-endpoint" data-method={verb} title={title}>
        <span className="cx-endpoint-label">{label}</span>
        <span className="cx-endpoint-method">{verb}</span>
      </span>;
  }
  return <a className="cx-endpoint" data-method={verb} href={href} title={title}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">{verb}</span>
    </a>;
};

export const AppLink = ({href, children, name, bare}) => {
  const label = children || name || "Open in Courier";
  if (bare) {
    return <a href={href} target="_blank" rel="noreferrer">{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="app" href={href} target="_blank" rel="noreferrer">
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method" aria-hidden="true">↗</span>
    </a>;
};

export const Guide = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="guide" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">GUIDE</span>
    </a>;
};

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

A list is a group of users you curate. Membership changes only when you change it.

Use a list for hand-picked groups: beta testers, a VIP cohort, an internal team. When membership should follow from profile data, use an <Doc href="/docs/recipients/lists-and-audiences/audiences">audience</Doc>.

Browse lists and their members under <AppLink href="https://app.courier.com/directory/lists">Directory → Lists</AppLink>, or manage them over the API as below.

## How it works

Each list has a dotted-namespace `list_id` such as `acme-corp.beta-testers`. Target one on a send with `list_id`:

```json theme={null}
{
  "to": { "list_id": "acme-corp.beta-testers" }
}
```

The <Endpoint method="GET" path="/lists/{list_id}" name="Get a List" href="/docs/api-reference/lists/get-a-list">Lists API</Endpoint> manages lists and their members. <Endpoint method="PUT" path="/lists/{list_id}" name="Update a List" href="/docs/api-reference/lists/update-a-list" /> is an upsert, so it also creates one. The `/lists/{list_id}/subscriptions` endpoints add, replace, read, and remove members. <Endpoint method="PUT" path="/lists/{list_id}/subscriptions/{user_id}" name="Subscribe a user Profile to a List" href="/docs/api-reference/lists/subscribe-a-user-profile-to-a-list" /> subscribes one user.

### List patterns

Because `list_id` is a dotted namespace, one pattern can target many lists at once. Set `list_pattern` on the recipient. `*` matches any single part. `**` is a trailing wildcard:

```json theme={null}
{
  "to": { "list_pattern": "acme-corp.*.beta-testers" }
}
```

The same pattern syntax filters lists on <Endpoint method="GET" path="/lists" name="List Lists" href="/docs/api-reference/lists/list-lists" /> through its `pattern` parameter.

Naming a list after an entity, such as `document.doc_a1b2.watchers`, turns this into per-entity subscriptions. <Guide href="/docs/guides/notify-everyone-watching">Notify everyone watching</Guide> walks that pattern end to end.

#### Valid ids and patterns

A `list_id` holds up to six dot-separated segments. It cannot be empty, include `*`, `#`, or a space, start or end with a period, or hold two periods in a row.

A wildcard replaces one whole segment, never part of one, so every `*` follows a period. Courier rejects these patterns:

| Pattern          | Why                                                         |
| ---------------- | ----------------------------------------------------------- |
| `*`, `**`        | Matches every list. At least one segment has to be literal. |
| `*.*`            | Same reason: nothing but wildcards and periods.             |
| `news*`          | A wildcard cannot match part of a segment. Use `news.*`.    |
| `news.*.**`      | A `*` cannot be followed later by `**`.                     |
| `news.***`       | At most two consecutive asterisks.                          |
| `news.**.weekly` | `**` is trailing only.                                      |
| `.news`, `news.` | Cannot start or end with a period.                          |
| `news..weekly`   | No consecutive periods.                                     |
| `news weekly`    | No spaces.                                                  |

### Scoping a list send to a tenant

`context.tenant_id` controls branding, preferences, and template variables. It does **not** restrict which list members receive the message. To send only to users in one tenant, add an inline `filters` array with the `MEMBER_OF` operator:

```json theme={null}
{
  "to": {
    "list_id": "acme-corp.beta-testers",
    "filters": [
      { "operator": "MEMBER_OF", "path": "tenant_id", "value": "acme-corp" }
    ]
  },
  "context": { "tenant_id": "acme-corp" }
}
```

Users must have tenant membership, set with <Endpoint method="PUT" path="/users/{user_id}/tenants/{tenant_id}" name="Add a user to a Tenant" href="/docs/api-reference/tenant-memberships/add-a-user-to-a-tenant" />, to pass the filter.

## Limits & behavior

* **List membership is exact and immediate.** A member is present the moment you add them. There is no rebuild step and no propagation delay.
* **`MEMBER_OF` is a send-time filter.** Use it inline on the recipient to scope a send by tenant. It is not part of the list definition.

## FAQ

<AccordionGroup>
  <Accordion title="Should I use a list or an audience?">
    Use a list when you pick members by hand and want membership exact, like beta testers or VIPs. Use an <Doc href="/docs/recipients/lists-and-audiences/audiences">audience</Doc> when membership follows from profile data and should update itself.
  </Accordion>

  <Accordion title="Can I send to several lists at once?">
    Use `list_pattern` with `*` for a single part, or `**` as a trailing wildcard, to match many `list_id`s in one send.
  </Accordion>
</AccordionGroup>
