Segments
Segments are rule-based dynamic audiences that evaluate at query time. No stale membership data — segments always reflect the current state of your contacts.
Overview
Segments are different from static lists. A list is a manually-curated group of contacts, while a segment is a set of rules that dynamically includes contacts matching the criteria. When you query a segment, the rules are evaluated against your current contact data, so the results are always up to date.
Segments can be used anywhere lists are used: campaign audience targeting, sequence enrollment triggers, and API filtering.
The segment object
{
"id": 1,
"siteId": 1,
"name": "Engaged US Subscribers",
"description": "Subscribed contacts in the US who opened an email in the last 30 days",
"rules": {
"operator": "AND",
"conditions": [
{ "type": "attribute", "field": "status", "op": "eq", "value": "subscribed" },
{ "type": "attribute", "field": "country", "op": "eq", "value": "US" },
{ "type": "engagement", "metric": "opened", "op": "within_days", "days": 30 }
]
},
"contactCount": 1250,
"createdAt": "2026-03-01T10:00:00.000Z",
"updatedAt": "2026-03-01T10:00:00.000Z"
}Rules format
Rules use a JSON structure with AND/OR grouping across multiple condition types:
{
"operator": "AND",
"conditions": [
{ "type": "attribute", "field": "country", "op": "eq", "value": "US" },
{ "type": "tag", "op": "has", "tagId": 5 },
{ "type": "list", "op": "in", "listId": 3 },
{ "type": "engagement", "metric": "opened", "op": "within_days", "days": 30 },
{ "type": "send_history", "op": "received_campaign", "targetId": 12 },
{
"type": "group",
"operator": "OR",
"conditions": [
{ "type": "attribute", "field": "city", "op": "eq", "value": "New York" },
{ "type": "attribute", "field": "city", "op": "eq", "value": "San Francisco" }
]
}
]
}Condition types
| Type | Description |
|---|---|
attribute | Match contact fields: status, city, country, custom fields, etc. |
tag | Check if contact has or doesn't have a specific tag |
list | Check if contact is in or not in a specific list |
engagement | Match based on email engagement (opened, clicked, bounced, complained) |
send_history | Match based on campaign/sequence history |
group | Nested AND/OR group for complex logic |
Attribute operators
| Operator | Description |
|---|---|
eq | Equals |
neq | Not equals |
in | In list of values (pass array) |
not_in | Not in list of values |
contains | Contains substring |
starts_with | Starts with prefix |
Engagement operators
| Operator | Description |
|---|---|
within_days | Event occurred within last N days |
not_within_days | Event did not occur within last N days |
never | Event has never occurred |
Send history operators
| Operator | Description |
|---|---|
received_campaign | Contact received a specific campaign |
not_received_campaign | Contact did not receive a specific campaign |
completed_sequence | Contact completed a specific sequence |
Create a segment
POST /v1/segments
| Parameter | Type | Description |
|---|---|---|
siteIdrequired | integer | The site this segment belongs to |
namerequired | string | Segment name (max 200 chars) |
description | string | What this segment represents |
rulesrequired | object | Rules JSON with operator and conditions |
curl -X POST https://pushmail.dev/api/v1/segments \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"siteId": 1,
"name": "Engaged US Subscribers",
"description": "Active subscribers in the US with recent opens",
"rules": {
"operator": "AND",
"conditions": [
{ "type": "attribute", "field": "status", "op": "eq", "value": "subscribed" },
{ "type": "attribute", "field": "country", "op": "eq", "value": "US" },
{ "type": "engagement", "metric": "opened", "op": "within_days", "days": 30 }
]
}
}'{
"data": {
"id": 1,
"siteId": 1,
"name": "Engaged US Subscribers",
"contactCount": 1250,
"rules": "{...}",
"createdAt": "2026-03-01T10:00:00.000Z"
}
}List segments
GET /v1/segments
| Parameter | Type | Description |
|---|---|---|
siteIdrequired | integer | Filter by site (query param) |
curl "https://pushmail.dev/api/v1/segments?siteId=1" \
-H "Authorization: Bearer pm_live_YOUR_KEY"Get a segment
GET /v1/segments/:id
Returns the segment with a live contact count (re-evaluated at query time).
curl https://pushmail.dev/api/v1/segments/1 \
-H "Authorization: Bearer pm_live_YOUR_KEY"Update a segment
PUT /v1/segments/:id
| Parameter | Type | Description |
|---|---|---|
name | string | New segment name |
description | string | New description |
rules | object | New rules (contact count is recomputed) |
curl -X PUT https://pushmail.dev/api/v1/segments/1 \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"rules": {
"operator": "AND",
"conditions": [
{ "type": "attribute", "field": "status", "op": "eq", "value": "subscribed" },
{ "type": "attribute", "field": "country", "op": "in", "value": ["US", "CA", "UK"] }
]
}
}'Delete a segment
DELETE /v1/segments/:id
Campaigns referencing this segment will have their segmentId cleared.
curl -X DELETE https://pushmail.dev/api/v1/segments/1 \
-H "Authorization: Bearer pm_live_YOUR_KEY"List segment contacts
GET /v1/segments/:id/contacts
Returns paginated contacts matching the segment rules, evaluated live.
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Results per page, 1-100 (default: 50) |
curl "https://pushmail.dev/api/v1/segments/1/contacts?page=1&limit=20" \
-H "Authorization: Bearer pm_live_YOUR_KEY"{
"data": {
"segment": { "id": 1, "name": "Engaged US Subscribers" },
"contacts": [
{ "id": 42, "email": "jane@example.com", "firstName": "Jane", "status": "subscribed" }
],
"pagination": { "page": 1, "limit": 20, "total": 1250, "totalPages": 63 }
}
}Preview count for existing segment
POST /v1/segments/:id/count
Preview the contact count for new rules against an existing segment, without saving changes. Useful for the segment editor UI to show a live count as rules are modified.
curl -X POST https://pushmail.dev/api/v1/segments/1/count \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"rules": {
"operator": "AND",
"conditions": [
{ "type": "attribute", "field": "status", "op": "eq", "value": "subscribed" },
{ "type": "attribute", "field": "country", "op": "in", "value": ["US", "CA"] }
]
}
}'{
"data": { "count": 3200 }
}Preview contact count
POST /v1/segments/preview-count
Preview how many contacts would match a set of rules without creating a segment. Useful for building rules interactively.
curl -X POST https://pushmail.dev/api/v1/segments/preview-count \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"siteId": 1,
"rules": {
"operator": "AND",
"conditions": [
{ "type": "attribute", "field": "status", "op": "eq", "value": "subscribed" }
]
}
}'{
"data": { "count": 5000 }
}Using segments with campaigns
Pass segmentId when creating a campaign to target a dynamic audience:
curl -X POST https://pushmail.dev/api/v1/campaigns \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"siteId": 1,
"name": "Re-engagement Campaign",
"templateId": 10,
"segmentId": 1
}'Using segments with sequences
Set triggerType to segment_enter with the segment ID in triggerConfig:
curl -X POST https://pushmail.dev/api/v1/sequences \
-H "Authorization: Bearer pm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"siteId": 1,
"name": "Win-back Sequence",
"triggerType": "segment_enter",
"triggerConfig": { "segmentId": 1 }
}'