Segments
Build named audiences of contacts, either rule-driven or hand-picked
A segment is a named group of contacts you can point campaigns at or use to kick off workflows. Segments come in two flavours: Dynamic and Static.
Dynamic vs Static
| Dynamic | Static | |
|---|---|---|
| Membership | Derived live from filter conditions | Hand-picked — you choose every member |
condition field | Required — holds the filter definition | Must be omitted |
| Add/remove members via API | Not possible (membership follows the filter) | POST / DELETE /segments/:id/members |
| Membership recomputation | Recalculated in the background when tracking is on | N/A — changes only through explicit API calls |
| Entry/exit events | Emitted only with Track membership changes turned on | Never emitted by add/remove calls |
| Updating the filter | Triggers a member count recalculation | condition on an update is silently discarded |
Reach for dynamic segments when membership should follow behaviour ("Pro-plan subscribers who opened any email in the past 14 days"). Reach for static segments when the list is curated by hand — beta testers, event attendees, or contacts pulled in from another system.
Filtering on a dynamic segment
Dynamic membership is driven by a filter: a set of conditions matched against every contact. Conditions can reference:
- The built-in fields
email,subscribed,createdAt, andupdatedAt. - Any custom field you've saved on contacts (everything under
data.*). - Events you've tracked through
/v1/track(event.signed_up,event.purchased, and so on). - Engagement signals such as
email.opened,email.clicked, andemail.bounced. - Whether the contact belongs to some other segment.
Conditions combine with AND / OR and can be nested into groups, so audiences like "subscribed Pro users and (opened or clicked an email in the last 14 days)" are expressible.
Every available field, operator, and value type — with worked examples — is documented in the Segment filter reference.
Static segments
A static segment is a list you maintain yourself. Nothing joins or leaves it automatically; its membership is exactly what you've put in it.
Creating
In the dashboard, open Segments, hit Create Segment, and pick Static. If you like, seed it with initial members right away via the contact search.
Adding and removing members
Inside a static segment, the Add Members search finds contacts to include. It only surfaces contacts that already exist in the project — so there's no way to add a nonexistent one by mistake — and members already in the segment appear greyed out.
Via the API:
POST /segments/:id/members— add contacts by email. Body:{ emails: string[], createMissing?: boolean, subscribed?: boolean }. SettingcreateMissing: truecreates any missing contacts on the spot (subscribed by default, unlesssubscribed: falseis passed). The response summarises{ added, created, notFound }.DELETE /segments/:id/members— remove contacts by email. Body:{ emails: string[] }. Responds with{ removed }.
These two endpoints work exclusively on static segments — pointing them at a dynamic one yields 400.
API-driven membership changes take effect at once, but they never emit entry/exit events; those exist only for dynamic segments with tracking enabled.
Tracking membership changes
A dynamic segment can enable Track membership changes. With it on, Bitelio emits an event each time a contact joins or leaves:
segment.<slug>.entry— contact joinedsegment.<slug>.exit— contact left
Bitelio builds <slug> from the segment's name: everything lowercased, accents and punctuation removed, spaces turned into hyphens, and runs of hyphens collapsed to one. So "VIP Customers" yields segment.vip-customers.entry. Choose names whose slugs won't need to change — a rename means a new event name.
These events are a natural way to power workflows: greet contacts entering a "Trial users" segment, or win back those leaving "Active users". The payload format is documented under Webhooks.
How tracking works
Creating a dynamic segment or editing its filter refreshes the member count right away. From then on, membership is recomputed in the background at a regular interval: Bitelio diffs the current matches against the last known set, logs who entered and who left, and fires the matching events.
That background interval means the dashboard's memberCount can trail reality by a few minutes. When you need an up-to-date number, or want entry / exit events to fire without waiting:
POST /segments/:id/refresh— recount only (inexpensive, emits nothing).POST /segments/:id/compute— run a complete membership recomputation, flushing any pending entry/exit events.
Using segments
Segments plug into two places:
- As campaign audiences in email campaigns (point the campaign's audience at the segment)
- As workflow triggers in marketing automation (listen for the
segment.<slug>.entryevent)
Performance notes
- Date comparisons on
data.*fields work by ordering ISO 8601 strings. To usewithin/olderThanon a custom date, store it as an ISO 8601 string (2026-05-06T12:00:00Z), not as a Unix timestamp. - Chaining many untracked dynamic segments inside each other makes evaluation expensive. When one segment is referenced by others, turn on Track membership for it — its member list is then read directly instead of being recomputed on every evaluation.
memberCountis a cache that may run a few minutes behind. Treat it as an estimate, and hitPOST /segments/:id/refreshwhen an exact figure matters.
Deleting a segment
If a segment backs an active campaign (one in DRAFT, SCHEDULED, or SENDING), deleting it fails with 409 Conflict. Either cancel that campaign or switch its audience before removing the segment.
API reference
All segment endpoints — list, get, create, update, delete, list members, add/remove static members, refresh count, and compute membership — are catalogued in the API overview.