Lists and segments
Lists and Segments explained. To subscribe a contact to a list, see Contacts.
Lists
The list object
{
"id": "5a8e2c1f-0b3d-4e6a-9f7c-2d1e8b4a6c3f",
"name": "Product updates",
"description": "New features, once a month",
"isPublic": true,
"counts": { "subscribed": 4210, "pending": 37, "unsubscribed": 158 },
"createdAt": "2026-09-01T08:00:00Z",
"updatedAt": "2026-09-01T08:00:00Z"
}
isPublic lists appear on the preferences page. pending counts subscriptions awaiting double opt-in confirmation.
List lists
GET /v1/lists
Every list of the space, with its counts.
curl https://api.kulamail.com/v1/lists \
-H "Authorization: Bearer ml_your_api_key"
Create a list
POST /v1/lists
true.curl https://api.kulamail.com/v1/lists \
-H "Authorization: Bearer ml_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "name": "Product updates", "description": "New features, once a month" }'
Returns 201 with the list object.
Get, update and delete a list
GET /v1/lists/{id}returns the list.PATCH /v1/lists/{id}changesname,descriptionorisPublic. Fields left out stay as they are; an emptydescriptionclears it.DELETE /v1/lists/{id}deletes the list and its subscriptions. The contacts stay. Answers204.
Segments
The segment object
{
"id": "7b6a5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
"name": "Engaged pro users",
"description": null,
"definition": {
"match": "all",
"conditions": [
{ "type": "attribute", "key": "plan", "operator": "equals", "value": "pro" },
{ "type": "activity", "operator": "opened", "withinDays": 30 }
]
},
"contactCount": 812,
"activeContactCount": 790,
"createdAt": "2026-10-01T12:00:00Z",
"updatedAt": "2026-10-01T12:00:00Z"
}
contactCount counts contacts matching now, whatever their status; activeContactCount only those who can still receive email.
List segments
GET /v1/segments
Create a segment
POST /v1/segments
curl https://api.kulamail.com/v1/segments \
-H "Authorization: Bearer ml_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Trial users who did not activate",
"definition": {
"match": "all",
"conditions": [
{ "type": "attribute", "key": "plan", "operator": "equals", "value": "trial" },
{ "type": "event", "name": "project_created", "operator": "not_performed" }
]
}
}'
Returns 201 with the segment object, or 400 with a message naming the invalid condition.
Get, update and delete a segment
GET /v1/segments/{id}returns the segment.PATCH /v1/segments/{id}changesname,descriptionordefinition. Fields left out stay as they are.DELETE /v1/segments/{id}answers204, or409 segment_in_usewhile a scheduled or sending campaign uses the segment.
Segment definition
A definition combines up to 50 conditions:
{
"match": "all",
"conditions": [ ... ]
}
match is all (every condition must hold) or any (at least one). A definition with no condition matches every contact.
Each condition has a type: attribute, field, list, event or activity.
Attribute
Filters on a contact attribute.
{ "type": "attribute", "key": "seats", "operator": "greater_than", "value": 10 }
| Operator | Value |
|---|---|
equals, not_equals | A string, number or boolean |
contains, not_contains | A string |
greater_than, less_than | A number |
before, after | An ISO 8601 date, such as 2026-01-31 (UTC when no time zone) |
is_true, is_false | None |
exists, not_exists | None |
Field
Filters on a built-in contact field.
{ "type": "field", "field": "mailbox_provider", "operator": "equals", "value": "GOOGLE" }
| Field | Operators | Value |
|---|---|---|
email | equals, not_equals, contains, not_contains | A string |
first_name, last_name | equals, not_equals, contains, not_contains, exists, not_exists | A string |
created_at | before, after | An ISO 8601 date |
mailbox_provider | equals, not_equals | GOOGLE, MICROSOFT, YAHOO, AOL, APPLE, OTHER, UNKNOWN |
source | equals, not_equals | FORM, API, IMPORT, DASHBOARD, MCP |
List
Filters on a list subscription.
{ "type": "list", "listId": "5a8e2c1f-0b3d-4e6a-9f7c-2d1e8b4a6c3f", "operator": "not_subscribed" }
Operators: subscribed, not_subscribed.
Event
Filters on events the contact performed.
{ "type": "event", "name": "order_paid", "operator": "performed", "withinDays": 30 }
Operators: performed, not_performed. Optional withinDays (1 to 3650) limits the check to the last days; without it, any time counts.
Activity
Filters on emails the contact received, opened or clicked.
{ "type": "activity", "operator": "not_opened", "withinDays": 90 }
Operators: received, not_received, opened, not_opened, clicked, not_clicked. Optional withinDays (1 to 3650) and campaignId (one campaign only). Opens and clicks by privacy proxies and link scanners don't count.
Examples
Inactive subscribers, to clean your list:
{
"match": "all",
"conditions": [
{ "type": "activity", "operator": "received", "withinDays": 90 },
{ "type": "activity", "operator": "not_opened", "withinDays": 90 }
]
}
Customers on either paid plan:
{
"match": "any",
"conditions": [
{ "type": "attribute", "key": "plan", "operator": "equals", "value": "pro" },
{ "type": "attribute", "key": "plan", "operator": "equals", "value": "business" }
]
}