API reference

Lists and segments

Subscription lists, saved filters and the segment definition format

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

name
string required
120 characters at most. Contacts see it on the preferences page.
description
string
500 characters at most.
isPublic
boolean
Show the list on the preferences page. Default 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} changes name, description or isPublic. Fields left out stay as they are; an empty description clears it.
  • DELETE /v1/lists/{id} deletes the list and its subscriptions. The contacts stay. Answers 204.

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

name
string required
120 characters at most.
description
string
500 characters at most.
definition
object required
The filter. See Segment definition.
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} changes name, description or definition. Fields left out stay as they are.
  • DELETE /v1/segments/{id} answers 204, or 409 segment_in_use while 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 }
OperatorValue
equals, not_equalsA string, number or boolean
contains, not_containsA string
greater_than, less_thanA number
before, afterAn ISO 8601 date, such as 2026-01-31 (UTC when no time zone)
is_true, is_falseNone
exists, not_existsNone

Field

Filters on a built-in contact field.

{ "type": "field", "field": "mailbox_provider", "operator": "equals", "value": "GOOGLE" }
FieldOperatorsValue
emailequals, not_equals, contains, not_containsA string
first_name, last_nameequals, not_equals, contains, not_contains, exists, not_existsA string
created_atbefore, afterAn ISO 8601 date
mailbox_providerequals, not_equalsGOOGLE, MICROSOFT, YAHOO, AOL, APPLE, OTHER, UNKNOWN
sourceequals, not_equalsFORM, 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" }
  ]
}