# Segments (/concepts/segments)

Segments let you create named groups of contacts that can be targeted in campaigns and used as triggers in workflows. There are two types: **Dynamic** and **Static**.

## Dynamic vs Static

|                            | Dynamic                                                 | Static                                              |
| -------------------------- | ------------------------------------------------------- | --------------------------------------------------- |
| Membership                 | Computed from filter conditions in real time            | Manually curated — you decide who is in             |
| `condition` field          | Required — describes the filters                        | Must be omitted                                     |
| Add/remove members via API | Not allowed (the filter decides)                        | `POST` / `DELETE` `/segments/:id/members`           |
| Membership recomputation   | Re-evaluated in the background when tracked             | N/A — membership only changes when you call the API |
| Entry/exit events          | Fired only when **Track membership changes** is enabled | Not fired by add/remove API calls                   |
| Updating the filter        | Member count is recomputed                              | `condition` is silently ignored on update           |

Use dynamic segments for behavioural targeting ("subscribed users on the Pro plan who opened any email in the last 14 days"). Use static segments for one-off curated lists like beta testers, conference attendees, or contacts imported from an external system.

## Filtering on a dynamic segment

A dynamic segment's membership is defined by a filter — a set of conditions evaluated against your contacts. You can filter on:

* Built-in contact fields: `email`, `subscribed`, `createdAt`, `updatedAt`.
* Custom fields you've stored on contacts (anything under `data.*`).
* Custom events tracked via `/v1/track` (`event.signed_up`, `event.purchased`, etc.).
* Email engagement (`email.opened`, `email.clicked`, `email.bounced`, etc.).
* Membership of another segment.

Filters can be combined with `AND` or `OR` and nested into groups for more complex audiences — for example "subscribed Pro users **and** (opened **or** clicked an email in the last 14 days)".

For the full list of fields, operators, and value types — plus worked examples — see the [Segment filter reference](/guides/segment-filters).

## Static segments

Static segments are manually curated lists. Membership doesn't change automatically — you decide exactly who is in.

### Creating

Go to **Segments** in the dashboard and click **Create Segment**, then choose **Static**. You can optionally add initial members straight away using the contact search.

### Adding and removing members

Open a static segment and use the **Add Members** search to find contacts. The search looks up contacts already in your project, so you can't accidentally add someone who doesn't exist. Contacts already in the segment are greyed out.

Programmatically, use:

* `POST /segments/:id/members` — add contacts by email. Body: `{ emails: string[], createMissing?: boolean, subscribed?: boolean }`. With `createMissing: true`, contacts that don't exist yet are created (and start subscribed unless you pass `subscribed: false`). The response reports `{ added, created, notFound }`.
* `DELETE /segments/:id/members` — remove contacts by email. Body: `{ emails: string[] }`. Returns `{ removed }`.

Both endpoints **only** accept static segments. Calling them on a dynamic segment returns `400`.

Membership changes via these API calls are immediate but **do not** fire `entry`/`exit` events — those are reserved for dynamic, tracked segments.

## Tracking membership changes

Dynamic segments can opt into **Track membership changes**. When enabled, Plunk fires events whenever a contact enters or leaves the segment:

* `segment.<slug>.entry` — contact joined
* `segment.<slug>.exit` — contact left

The `<slug>` is derived from the segment name: lowercased, accents and punctuation stripped, whitespace replaced with hyphens, repeated hyphens collapsed. `"VIP Customers"` becomes `segment.vip-customers.entry`. Pick segment names that produce stable slugs — renaming a segment changes the event name.

Use these events to drive workflows (welcome a contact when they enter a "Trial users" segment, send a re-engagement email when they exit "Active users", etc.). See [Webhooks](/guides/webhooks) for the payload format.

### How tracking works

The member count updates immediately when you create or change a dynamic segment's filter. After that, Plunk recomputes membership in the background on a regular cadence — diffing current matches against the previous set, recording entries and exits, and emitting the corresponding events.

Because of this background cadence, the `memberCount` shown in the dashboard can lag the live state by a few minutes. If you need a fresh value or want to drive a workflow off `entry` / `exit` immediately:

* `POST /segments/:id/refresh` — force a count refresh (cheap, no events).
* `POST /segments/:id/compute` — force a full membership recomputation, which fires any pending entry/exit events.

## Using segments

Segments can be used to:

* Target contacts in [email campaigns](/concepts/campaigns) (set the campaign audience to a segment)
* Trigger workflows in [marketing automation](/concepts/workflows) (use the `segment.<slug>.entry` event)

## Performance notes

* Date filters on `data.*` rely on ISO 8601 string ordering — store dates as ISO 8601 strings (`2026-05-06T12:00:00Z`) rather than Unix timestamps if you want to use `within` / `olderThan` on them.
* Nesting many untracked dynamic segments inside one another increases evaluation cost. If a segment is referenced by others, enable **Track membership** on it so its members are looked up directly instead of recomputed each time.
* The cached `memberCount` may be a few minutes behind reality. Treat it as approximate; use `POST /segments/:id/refresh` to force a refresh if you need an exact count.

## Deleting a segment

Deleting a segment that's referenced by an active campaign (in `DRAFT`, `SCHEDULED`, or `SENDING` state) returns `409 Conflict`. Cancel the campaign or pick a different audience first.

## API reference

See the segments API endpoints in the [API overview](/api-reference/overview#segments) — list, get, create, update, delete, list members, add/remove static members, refresh count, and compute membership.

## What's next

<Cards>
  <Card title="Filter reference" href="/guides/segment-filters">
    Deep-link reference for every filter field, operator, and value type.
  </Card>

  <Card title="Custom fields" href="/guides/custom-fields">
    How `data.*` fields are typed and used in filters.
  </Card>

  <Card title="Campaigns" href="/concepts/campaigns">
    Use a segment as a campaign audience.
  </Card>

  <Card title="Workflows" href="/concepts/workflows">
    Trigger workflows from segment entry / exit events.
  </Card>
</Cards>
