Segmentation
Create dynamic contact segments with filter groups, nested logic, and async computation.
Segments let you group contacts based on filters - contact fields, account data, metadata, firmographic attributes, and behavioral events. Segments are computed asynchronously and can be used for targeting in journeys and experiments.
Create a segment
molted segments create \
--name "Enterprise customers in US" \
--filters '{
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "customer" },
{ "type": "metadata", "field": "region", "operator": "eq", "value": "us-west" },
{ "type": "firmographic", "field": "companySize", "operator": "gte", "value": 500 }
]
}'| Flag | Type | Required | Description |
|---|---|---|---|
--name | string | Yes | Segment name. |
--filters | JSON | Yes | Filter group as JSON. See filter reference below. |
POST https://api.molted.email/v1/segmentscurl -X POST https://api.molted.email/v1/segments \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise customers in US",
"filterGroup": {
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "customer" },
{ "type": "metadata", "field": "region", "operator": "eq", "value": "us-west" },
{ "type": "firmographic", "field": "companySize", "operator": "gte", "value": 500 }
]
}
}'| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Segment name. |
filterGroup | object | Yes | Filter group object. See filter reference below. |
Get a segment
molted segments get seg_abc123GET https://api.molted.email/v1/segments/:idcurl "https://api.molted.email/v1/segments/seg_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"List segments
molted segments listGET https://api.molted.email/v1/segmentscurl "https://api.molted.email/v1/segments" \
-H "Authorization: Bearer YOUR_API_KEY"Response
Returns an array of active segments. Archived segments are excluded.
[
{
"id": "seg_abc123",
"tenantId": "tenant_abc123",
"name": "Enterprise customers in US",
"filterGroup": { "logic": "and", "filters": [ "..." ] },
"status": "active",
"contactCount": 142,
"snapshotVersion": 3,
"createdAt": "2026-03-01T12:00:00Z",
"updatedAt": "2026-03-15T08:30:00Z"
}
]If you have no segments yet, the response is an empty array:
[]No segments yet? Create your first segment with molted segments create. See creating segments above for filter syntax and examples.
Update a segment
molted segments update seg_abc123 \
--name "Enterprise US (updated)" \
--filters '{
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "customer" },
{ "type": "metadata", "field": "region", "operator": "in", "value": ["us-west", "us-east"] }
]
}'| Flag | Type | Required | Description |
|---|---|---|---|
--name | string | No | Updated segment name. |
--filters | JSON | No | Updated filter group as JSON. |
PATCH https://api.molted.email/v1/segments/:idcurl -X PATCH https://api.molted.email/v1/segments/seg_abc123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise US (updated)",
"filterGroup": {
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "customer" },
{ "type": "metadata", "field": "region", "operator": "in", "value": ["us-west", "us-east"] }
]
}
}'Archive a segment
molted segments archive seg_abc123DELETE https://api.molted.email/v1/segments/:idcurl -X DELETE "https://api.molted.email/v1/segments/seg_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"Compute segment membership
Trigger an async recomputation of the segment's current members. Each computation creates a point-in-time snapshot of matching contacts.
Most flows do not need to call this directly: contacts sync automatically enqueues a recompute for every active segment in the tenant, so segments list / segments get reflect the new contacts shortly after the sync. Use this command when you want to force a refresh without syncing contacts (e.g., after manually editing contact metadata, or to verify a freshly created segment).
Compute also lazily migrates legacy filter_group shapes (e.g. { operator, conditions, op } from older API versions) to the canonical { logic, filters, type, operator } shape and writes the canonical version back. After the next compute completes, segments get returns the canonical shape regardless of how the segment was originally created. The evaluator already tolerates legacy shapes -- the rewrite is a cleanup so consumers reading the JSONB directly see one shape.
molted segments compute seg_abc123POST https://api.molted.email/v1/segments/:id/computecurl -X POST "https://api.molted.email/v1/segments/seg_abc123/compute" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'List segment members
Results are paginated. Use limit and offset to page through members.
molted segments members seg_abc123 --limit 50 --offset 0| Flag | Type | Required | Description |
|---|---|---|---|
--limit | number | No | Max results per page (default 50). |
--offset | number | No | Pagination offset (default 0). |
GET https://api.molted.email/v1/segments/:id/memberscurl "https://api.molted.email/v1/segments/seg_abc123/members?limit=50&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"Get contact segments
List all segments a contact belongs to. This is a reverse lookup -- given a contact, it returns every segment they are a member of.
To check whether a contact is in one specific segment instead, use analytics segment-check.
molted segments contact con_abc123GET https://api.molted.email/v1/segments/contact/:contactId/segmentscurl "https://api.molted.email/v1/segments/contact/con_abc123/segments" \
-H "Authorization: Bearer YOUR_API_KEY"Filter types
Each filter has a type that determines which fields you can query.
| Type | Description | Queryable fields |
|---|---|---|
contact_field | Filter on contact properties. | email, name, ownerEmail, lifecycleStage, dealStage |
account_field | Filter on the contact's linked account. | name, domain, plus any key in the account's metadata object |
metadata | Filter on contact custom metadata. | Any key in the contact's metadata object |
firmographic | Filter on account custom metadata. | Any key in the account's metadata object |
behavioral | Filter on event counts within a time window. | Uses behavioralWindow - see behavioral filters below |
Standard filter structure
{
"type": "contact_field",
"field": "lifecycleStage",
"operator": "eq",
"value": "customer"
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | One of: contact_field, account_field, metadata, firmographic, behavioral. |
field | string | Yes | The field to filter on. Valid fields depend on the type. |
operator | string | Yes | Comparison operator. See operators below. |
value | any | Varies | The value to compare against. Not required for exists/not_exists. |
Behavioral filters
Behavioral filters count how many times an event occurred within a time window. They use a behavioralWindow object instead of a simple value.
{
"type": "behavioral",
"field": "event_count",
"operator": "gte",
"value": 0,
"behavioralWindow": {
"eventName": "email_opened",
"windowDays": 30,
"countOperator": "gte",
"countValue": 3
}
}| Field | Type | Required | Description |
|---|---|---|---|
behavioralWindow.eventName | string | Yes | The event to count (e.g. email_opened, email_clicked, email_sent). |
behavioralWindow.windowDays | number | Yes | Lookback window in days. Maximum 90. |
behavioralWindow.countOperator | string | Yes | Operator to apply to the event count (e.g. gte, eq, lt). |
behavioralWindow.countValue | number | Yes | The threshold count to compare against. |
This example matches contacts who opened an email 3 or more times in the last 30 days.
Operators
| Operator | Value type | Description | Example value |
|---|---|---|---|
eq | any | Equals. | "pro", 500, true |
neq | any | Not equals. | "free" |
gt | number | Greater than. | 100 |
gte | number | Greater than or equal. | 500 |
lt | number | Less than. | 50 |
lte | number | Less than or equal. | 1000 |
contains | string | String contains substring. | "example.com" |
not_contains | string | String does not contain substring. | "test" |
in | array | Value is in the provided list. | ["us-west", "us-east"] |
not_in | array | Value is not in the provided list. | ["spam", "bounced"] |
exists | none | Field exists and is not null. No value needed. | - |
not_exists | none | Field does not exist or is null. No value needed. | - |
between | array | Value is between two bounds (inclusive). | [100, 500] |
Type constraints: gt, gte, lt, lte, and between only work with numeric values. contains and not_contains only work with strings. Using an operator with the wrong value type returns no matches.
Filter group structure
A filter group wraps one or more filters with a logic operator.
{
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "customer" },
{ "type": "metadata", "field": "region", "operator": "eq", "value": "us-west" }
]
}| Field | Type | Required | Description |
|---|---|---|---|
logic | string | Yes | and or or. Determines how filters are combined. |
filters | array | Yes | Array of filters or nested filter groups. |
Nested logic
Filter groups can contain other filter groups for complex AND/OR logic:
{
"logic": "or",
"filters": [
{
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "lead" },
{
"type": "behavioral",
"field": "event_count",
"operator": "gte",
"value": 0,
"behavioralWindow": {
"eventName": "email_opened",
"windowDays": 30,
"countOperator": "gte",
"countValue": 3
}
}
]
},
{
"logic": "and",
"filters": [
{ "type": "contact_field", "field": "lifecycleStage", "operator": "eq", "value": "customer" },
{ "type": "metadata", "field": "plan", "operator": "eq", "value": "enterprise" }
]
}
]
}This matches contacts who are either (leads with 3+ email opens in the last 30 days) OR (enterprise customers). Nesting depth is unlimited.