ChatKomodo Docs Home Sign in

API reference · Customers

CRM

Custom contact fields, tags, saved segments (dynamic audiences) and the contact card of a number.

List custom contact fields

GET/v1/crm/fields

Custom contact attributes defined by the business (max 30).

Responses

  • 200Fields
    Response fields
    • fieldsarray of objectrequired
    • fields[].keystringrequired
      Lowercase letters, digits, _; starts with a letter; max 30
    • fields[].labelstringrequired
      Up to 60 characters
    • fields[].typestringrequired
      One of: text, number, date, select
    • fields[].optionsarray of string
      Required for select
      Up to 50 items
Request
curl "https://api.chatkomodo.com/v1/crm/fields" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/crm/fields', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.get(
    "https://api.chatkomodo.com/v1/crm/fields",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/crm/fields', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "fields": [
    {
      "key": "uploads/abc123.jpg",
      "label": "string",
      "type": "text",
      "options": [
        "string"
      ]
    }
  ]
}

Replace the custom contact fields

PUT/v1/crm/fields

Replace the whole list of custom attribute definitions.

Request body

  • fieldsarray of objectrequired
    Up to 30 items
  • fields[].keystringrequired
    Lowercase letters, digits, _; starts with a letter; max 30
  • fields[].labelstringrequired
    Up to 60 characters
  • fields[].typestringrequired
    One of: text, number, date, select
  • fields[].optionsarray of string
    Required for select
    Up to 50 items

Responses

  • 200Saved
    Response fields
    • fieldsarray of objectrequired
    • fields[].keystringrequired
      Lowercase letters, digits, _; starts with a letter; max 30
    • fields[].labelstringrequired
      Up to 60 characters
    • fields[].typestringrequired
      One of: text, number, date, select
    • fields[].optionsarray of string
      Required for select
      Up to 50 items
  • 400Validation error
Request
curl -X PUT "https://api.chatkomodo.com/v1/crm/fields" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "fields": [
    {
      "key": "plan",
      "label": "Plan",
      "type": "select",
      "options": [
        "Free",
        "Pro"
      ]
    },
    {
      "key": "renewal",
      "label": "Renewal date",
      "type": "date"
    }
  ]
}'
const res = await fetch('https://api.chatkomodo.com/v1/crm/fields', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "fields": [
      {
        "key": "plan",
        "label": "Plan",
        "type": "select",
        "options": [
          "Free",
          "Pro"
        ]
      },
      {
        "key": "renewal",
        "label": "Renewal date",
        "type": "date"
      }
    ]
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.put(
    "https://api.chatkomodo.com/v1/crm/fields",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "fields": [
            {
                "key": "plan",
                "label": "Plan",
                "type": "select",
                "options": [
                    "Free",
                    "Pro",
                ],
            },
            {
                "key": "renewal",
                "label": "Renewal date",
                "type": "date",
            },
        ],
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('PUT', 'https://api.chatkomodo.com/v1/crm/fields', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'fields' => [
            [
                'key' => 'plan',
                'label' => 'Plan',
                'type' => 'select',
                'options' => [
                    'Free',
                    'Pro',
                ],
            ],
            [
                'key' => 'renewal',
                'label' => 'Renewal date',
                'type' => 'date',
            ],
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "fields": [
    {
      "key": "uploads/abc123.jpg",
      "label": "string",
      "type": "text",
      "options": [
        "string"
      ]
    }
  ]
}

List contact tags

GET/v1/crm/tags

All tags in use with the number of contacts carrying each.

Responses

  • 200Tags
    Response fields
    • tagsarray of objectrequired
    • tags[].tagstringrequired
    • tags[].countintegerrequired
Request
curl "https://api.chatkomodo.com/v1/crm/tags" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/crm/tags', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.get(
    "https://api.chatkomodo.com/v1/crm/tags",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/crm/tags', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "tags": [
    {
      "tag": "vip",
      "count": 12
    }
  ]
}

Tag or untag many contacts

POST/v1/crm/contacts/tags

Add and/or remove tags on many contacts, selected by ids or by a saved segment_id.

Request body

  • idsarray of string
    Up to 50 000 items
  • segment_idstring
  • addarray of string
    Up to 20 items
  • removearray of string
    Up to 20 items

Responses

  • 200Result
    Response fields
    • updatedintegerrequired
    • skippedintegerrequired
Request
curl -X POST "https://api.chatkomodo.com/v1/crm/contacts/tags" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "segment_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "add": [
    "spring-sale"
  ],
  "remove": [
    "new"
  ]
}'
const res = await fetch('https://api.chatkomodo.com/v1/crm/contacts/tags', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "segment_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
    "add": [
      "spring-sale"
    ],
    "remove": [
      "new"
    ]
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.post(
    "https://api.chatkomodo.com/v1/crm/contacts/tags",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "segment_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
        "add": [
            "spring-sale",
        ],
        "remove": [
            "new",
        ],
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/crm/contacts/tags', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'segment_id' => '01JA5V6Q8M2H7G4K9P3T0R1S2X',
        'add' => [
            'spring-sale',
        ],
        'remove' => [
            'new',
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "updated": 1,
  "skipped": 1
}

List segments

GET/v1/crm/segments

Saved segments (dynamic audiences) with their current size.

Responses

  • 200Segments
    Response fields
    • segmentsarray of objectrequired
    • segments[].idstringrequired
    • segments[].namestringrequired
    • segments[].filterobjectrequired
    • segments[].filter.matchstringrequired
      all = AND, any = OR
      One of: all, any
    • segments[].filter.rulesarray of objectrequired
      Up to 20 items
    • segments[].filter.rules[].fieldstringrequired
      name, phone, group, tag, consent, created or attr.<key>
    • segments[].filter.rules[].opstringrequired
      Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; tag takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte
      One of: is, is_not, contains, not_contains, starts_with, empty, not_empty, gt, gte, lt, lte, has, not_has, has_any, before, after, within_days
    • segments[].filter.rules[].valuestring | number | array of string
    • segments[].countinteger | null
      Number of matching contacts (list only; cached for about a minute). null while it has not been counted yet
    • segments[].count_pendingboolean
      true when count is null because the count is still being computed: only a few segments are counted per call, ask again shortly
    • segments[].created_atintegerrequired
    • segments[].updated_atintegerrequired
Request
curl "https://api.chatkomodo.com/v1/crm/segments" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/crm/segments', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.get(
    "https://api.chatkomodo.com/v1/crm/segments",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/crm/segments', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "segments": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "name": "VIP customers",
      "filter": {
        "match": "all",
        "rules": [
          {
            "field": null,
            "op": "is",
            "value": null
          }
        ]
      },
      "count": 12,
      "count_pending": true,
      "created_at": 1767225600000,
      "updated_at": 1767225600000
    }
  ]
}

Create a segment

POST/v1/crm/segments

Create a segment (max 200). Use its id as segment_id when creating a blast or listing contacts.

Request body

  • namestringrequired
    1–80 characters
  • filterobjectrequired
  • filter.matchstringrequired
    all = AND, any = OR
    One of: all, any
  • filter.rulesarray of objectrequired
    Up to 20 items
  • filter.rules[].fieldstringrequired
    name, phone, group, tag, consent, created or attr.<key>
  • filter.rules[].opstringrequired
    Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; tag takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte
    One of: is, is_not, contains, not_contains, starts_with, empty, not_empty, gt, gte, lt, lte, has, not_has, has_any, before, after, within_days
  • filter.rules[].valuestring | number | array of string

Responses

  • 201Created
    Response fields
    • idstringrequired
    • namestringrequired
    • filterobjectrequired
    • filter.matchstringrequired
      all = AND, any = OR
      One of: all, any
    • filter.rulesarray of objectrequired
      Up to 20 items
    • filter.rules[].fieldstringrequired
      name, phone, group, tag, consent, created or attr.<key>
    • filter.rules[].opstringrequired
      Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; tag takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte
      One of: is, is_not, contains, not_contains, starts_with, empty, not_empty, gt, gte, lt, lte, has, not_has, has_any, before, after, within_days
    • filter.rules[].valuestring | number | array of string
    • countinteger | null
      Number of matching contacts (list only; cached for about a minute). null while it has not been counted yet
    • count_pendingboolean
      true when count is null because the count is still being computed: only a few segments are counted per call, ask again shortly
    • created_atintegerrequired
    • updated_atintegerrequired
  • 400Invalid filter
  • 409LIMIT_REACHED
Request
curl -X POST "https://api.chatkomodo.com/v1/crm/segments" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Pro customers who are VIP",
  "filter": {
    "match": "all",
    "rules": [
      {
        "field": "attr.plan",
        "op": "is",
        "value": "Pro"
      },
      {
        "field": "tag",
        "op": "has",
        "value": "vip"
      }
    ]
  }
}'
const res = await fetch('https://api.chatkomodo.com/v1/crm/segments', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Pro customers who are VIP",
    "filter": {
      "match": "all",
      "rules": [
        {
          "field": "attr.plan",
          "op": "is",
          "value": "Pro"
        },
        {
          "field": "tag",
          "op": "has",
          "value": "vip"
        }
      ]
    }
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.post(
    "https://api.chatkomodo.com/v1/crm/segments",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Pro customers who are VIP",
        "filter": {
            "match": "all",
            "rules": [
                {
                    "field": "attr.plan",
                    "op": "is",
                    "value": "Pro",
                },
                {
                    "field": "tag",
                    "op": "has",
                    "value": "vip",
                },
            ],
        },
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/crm/segments', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Pro customers who are VIP',
        'filter' => [
            'match' => 'all',
            'rules' => [
                [
                    'field' => 'attr.plan',
                    'op' => 'is',
                    'value' => 'Pro',
                ],
                [
                    'field' => 'tag',
                    'op' => 'has',
                    'value' => 'vip',
                ],
            ],
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 201
{
  "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "name": "VIP customers",
  "filter": {
    "match": "all",
    "rules": [
      {
        "field": "tag",
        "op": "is",
        "value": "vip"
      }
    ]
  },
  "count": 12,
  "count_pending": true,
  "created_at": 1767225600000,
  "updated_at": 1767225600000
}

Preview a segment filter

POST/v1/crm/segments/preview

How many contacts a filter matches, plus a sample (max 50), without saving it.

Request body

  • filterobjectrequired
  • filter.matchstringrequired
    all = AND, any = OR
    One of: all, any
  • filter.rulesarray of objectrequired
    Up to 20 items
  • filter.rules[].fieldstringrequired
    name, phone, group, tag, consent, created or attr.<key>
  • filter.rules[].opstringrequired
    Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; tag takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte
    One of: is, is_not, contains, not_contains, starts_with, empty, not_empty, gt, gte, lt, lte, has, not_has, has_any, before, after, within_days
  • filter.rules[].valuestring | number | array of string
  • limitinteger
    From 1 to 50

Responses

  • 200Preview
    Response fields
    • countintegerrequired
    • samplearray of objectrequired
    • sample[].idstringrequired
    • sample[].phonestringrequired
    • sample[].namestring | nullrequired
    • sample[].var1string | nullrequired
    • sample[].var2string | nullrequired
    • sample[].var3string | nullrequired
    • sample[].var4string | nullrequired
    • sample[].var5string | nullrequired
    • sample[].group_idstring | nullrequired
    • sample[].tagsarray of stringrequired
    • sample[].attrsobjectrequired
      Custom attributes (attr.<key> in segments and message tokens)
    • sample[].created_atintegerrequired
    • sample[].updated_atintegerrequired
Request
curl -X POST "https://api.chatkomodo.com/v1/crm/segments/preview" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "filter": {
    "match": "any",
    "rules": [
      {
        "field": "created",
        "op": "within_days",
        "value": 30
      }
    ]
  },
  "limit": 10
}'
const res = await fetch('https://api.chatkomodo.com/v1/crm/segments/preview', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "filter": {
      "match": "any",
      "rules": [
        {
          "field": "created",
          "op": "within_days",
          "value": 30
        }
      ]
    },
    "limit": 10
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.post(
    "https://api.chatkomodo.com/v1/crm/segments/preview",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "filter": {
            "match": "any",
            "rules": [
                {
                    "field": "created",
                    "op": "within_days",
                    "value": 30,
                },
            ],
        },
        "limit": 10,
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/crm/segments/preview', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'filter' => [
            'match' => 'any',
            'rules' => [
                [
                    'field' => 'created',
                    'op' => 'within_days',
                    'value' => 30,
                ],
            ],
        ],
        'limit' => 10,
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "count": 12,
  "sample": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "phone": "+447700900123",
      "name": "VIP customers",
      "var1": "string",
      "var2": "string",
      "var3": "string",
      "var4": "string",
      "var5": "string",
      "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "tags": [
        "vip"
      ],
      "attrs": {},
      "created_at": 1767225600000,
      "updated_at": 1767225600000
    }
  ]
}

Update a segment

PUT/v1/crm/segments/{id}

Rename a segment and/or change its filter.

Path parameters

  • idstringrequired

Request body

  • namestring
    1–80 characters
  • filterobject
  • filter.matchstringrequired
    all = AND, any = OR
    One of: all, any
  • filter.rulesarray of objectrequired
    Up to 20 items
  • filter.rules[].fieldstringrequired
    name, phone, group, tag, consent, created or attr.<key>
  • filter.rules[].opstringrequired
    Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; tag takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte
    One of: is, is_not, contains, not_contains, starts_with, empty, not_empty, gt, gte, lt, lte, has, not_has, has_any, before, after, within_days
  • filter.rules[].valuestring | number | array of string

Responses

  • 200Updated
    Response fields
    • idstringrequired
    • namestringrequired
    • filterobjectrequired
    • filter.matchstringrequired
      all = AND, any = OR
      One of: all, any
    • filter.rulesarray of objectrequired
      Up to 20 items
    • filter.rules[].fieldstringrequired
      name, phone, group, tag, consent, created or attr.<key>
    • filter.rules[].opstringrequired
      Which operators apply depends on the field: text fields take is/is_not/contains/not_contains/starts_with/empty/not_empty; tag takes has/not_has/has_any; dates take before/after/within_days; numbers also gt/gte/lt/lte
      One of: is, is_not, contains, not_contains, starts_with, empty, not_empty, gt, gte, lt, lte, has, not_has, has_any, before, after, within_days
    • filter.rules[].valuestring | number | array of string
    • countinteger | null
      Number of matching contacts (list only; cached for about a minute). null while it has not been counted yet
    • count_pendingboolean
      true when count is null because the count is still being computed: only a few segments are counted per call, ask again shortly
    • created_atintegerrequired
    • updated_atintegerrequired
  • 404Segment not found
Request
curl -X PUT "https://api.chatkomodo.com/v1/crm/segments/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "VIP customers",
  "filter": {
    "match": "all",
    "rules": [
      {
        "field": "tag",
        "op": "has",
        "value": "vip"
      }
    ]
  }
}'
const res = await fetch('https://api.chatkomodo.com/v1/crm/segments/ID', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "VIP customers",
    "filter": {
      "match": "all",
      "rules": [
        {
          "field": "tag",
          "op": "has",
          "value": "vip"
        }
      ]
    }
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.put(
    "https://api.chatkomodo.com/v1/crm/segments/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "VIP customers",
        "filter": {
            "match": "all",
            "rules": [
                {
                    "field": "tag",
                    "op": "has",
                    "value": "vip",
                },
            ],
        },
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('PUT', 'https://api.chatkomodo.com/v1/crm/segments/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'VIP customers',
        'filter' => [
            'match' => 'all',
            'rules' => [
                [
                    'field' => 'tag',
                    'op' => 'has',
                    'value' => 'vip',
                ],
            ],
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "name": "VIP customers",
  "filter": {
    "match": "all",
    "rules": [
      {
        "field": "tag",
        "op": "is",
        "value": "vip"
      }
    ]
  },
  "count": 12,
  "count_pending": true,
  "created_at": 1767225600000,
  "updated_at": 1767225600000
}

Delete a segment

DEL/v1/crm/segments/{id}

Delete a segment (its contacts are not touched).

Path parameters

  • idstringrequired

Responses

  • 200Deleted
    Response fields
    • okbooleanrequired
  • 404Segment not found
Request
curl -X DELETE "https://api.chatkomodo.com/v1/crm/segments/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/crm/segments/ID', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.delete(
    "https://api.chatkomodo.com/v1/crm/segments/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('DELETE', 'https://api.chatkomodo.com/v1/crm/segments/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "ok": true
}

List the contacts of a segment

GET/v1/crm/segments/{id}/contacts

The contacts currently matching a segment.

Path parameters

  • idstringrequired

Query parameters

  • limitinteger | null
  • offsetinteger | null

Responses

  • 200Contacts
    Response fields
    • totalintegerrequired
    • contactsarray of objectrequired
    • contacts[].idstringrequired
    • contacts[].phonestringrequired
    • contacts[].namestring | nullrequired
    • contacts[].var1string | nullrequired
    • contacts[].var2string | nullrequired
    • contacts[].var3string | nullrequired
    • contacts[].var4string | nullrequired
    • contacts[].var5string | nullrequired
    • contacts[].group_idstring | nullrequired
    • contacts[].tagsarray of stringrequired
    • contacts[].attrsobjectrequired
      Custom attributes (attr.<key> in segments and message tokens)
    • contacts[].created_atintegerrequired
    • contacts[].updated_atintegerrequired
  • 404Segment not found
Request
curl "https://api.chatkomodo.com/v1/crm/segments/ID/contacts" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/crm/segments/ID/contacts', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.get(
    "https://api.chatkomodo.com/v1/crm/segments/ID/contacts",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/crm/segments/ID/contacts', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "total": 12,
  "contacts": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "phone": "+447700900123",
      "name": "VIP customers",
      "var1": "string",
      "var2": "string",
      "var3": "string",
      "var4": "string",
      "var5": "string",
      "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "tags": [
        "vip"
      ],
      "attrs": {},
      "created_at": 1767225600000,
      "updated_at": 1767225600000
    }
  ]
}

Get the contact card of a number

GET/v1/crm/contacts/by-phone/{phone}

The contact card of one number (used by the inbox sidebar): the saved contact, if any, and its consent state.

Path parameters

  • phonestringrequired
    International format +<country code><number> (URL-encode the +)

Responses

  • 200Contact card
    Response fields
    • phonestringrequired
    • contactobject | nullrequired
    • contact.idstringrequired
    • contact.phonestringrequired
    • contact.namestring | nullrequired
    • contact.var1string | nullrequired
    • contact.var2string | nullrequired
    • contact.var3string | nullrequired
    • contact.var4string | nullrequired
    • contact.var5string | nullrequired
    • contact.group_idstring | nullrequired
    • contact.tagsarray of stringrequired
    • contact.attrsobjectrequired
      Custom attributes (attr.<key> in segments and message tokens)
    • contact.created_atintegerrequired
    • contact.updated_atintegerrequired
    • consentobject | nullrequired
    • consent.statusstringrequired
      One of: opted_in, opted_out
    • consent.sourcestringrequired
    • consent.updated_atintegerrequired
  • 400INVALID_NUMBER
Request
curl "https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.get(
    "https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "phone": "+447700900123",
  "contact": {
    "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
    "phone": "+447700900123",
    "name": "Sam Carter",
    "var1": "string",
    "var2": "string",
    "var3": "string",
    "var4": "string",
    "var5": "string",
    "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
    "tags": [
      "vip"
    ],
    "attrs": {},
    "created_at": 1767225600000,
    "updated_at": 1767225600000
  },
  "consent": {
    "status": "opted_in",
    "source": "string",
    "updated_at": 1767225600000
  }
}

Create or update a contact by number

PUT/v1/crm/contacts/by-phone/{phone}

Create or update the contact for a number (upsert). tags replaces, add_tags / remove_tags modify. For attrs, an empty string removes a key; values are strings of up to 500 characters and typed attributes are validated against the field definitions.

Path parameters

  • phonestringrequired
    International format +<country code><number> (URL-encode the +)

Request body

  • namestring | null
    Up to 200 characters
  • group_idstring | null
  • tagsarray of string
  • add_tagsarray of string
  • remove_tagsarray of string
  • attrsobject

Responses

  • 200Existing contact updated
    Response fields
    • createdbooleanrequired
      One of: false
    • contactobjectrequired
    • contact.idstringrequired
    • contact.phonestringrequired
    • contact.namestring | nullrequired
    • contact.var1string | nullrequired
    • contact.var2string | nullrequired
    • contact.var3string | nullrequired
    • contact.var4string | nullrequired
    • contact.var5string | nullrequired
    • contact.group_idstring | nullrequired
    • contact.tagsarray of stringrequired
    • contact.attrsobjectrequired
      Custom attributes (attr.<key> in segments and message tokens)
    • contact.created_atintegerrequired
    • contact.updated_atintegerrequired
  • 201Contact created
    Response fields
    • createdbooleanrequired
      One of: true
    • contactobjectrequired
    • contact.idstringrequired
    • contact.phonestringrequired
    • contact.namestring | nullrequired
    • contact.var1string | nullrequired
    • contact.var2string | nullrequired
    • contact.var3string | nullrequired
    • contact.var4string | nullrequired
    • contact.var5string | nullrequired
    • contact.group_idstring | nullrequired
    • contact.tagsarray of stringrequired
    • contact.attrsobjectrequired
      Custom attributes (attr.<key> in segments and message tokens)
    • contact.created_atintegerrequired
    • contact.updated_atintegerrequired
  • 400Validation error
Request
curl -X PUT "https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sam Carter",
  "add_tags": [
    "vip"
  ],
  "attrs": {
    "plan": "Pro"
  }
}'
const res = await fetch('https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Sam Carter",
    "add_tags": [
      "vip"
    ],
    "attrs": {
      "plan": "Pro"
    }
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.put(
    "https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Sam Carter",
        "add_tags": [
            "vip",
        ],
        "attrs": {
            "plan": "Pro",
        },
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('PUT', 'https://api.chatkomodo.com/v1/crm/contacts/by-phone/447700900123', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Sam Carter',
        'add_tags' => [
            'vip',
        ],
        'attrs' => [
            'plan' => 'Pro',
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "created": false,
  "contact": {
    "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
    "phone": "+447700900123",
    "name": "Sam Carter",
    "var1": "string",
    "var2": "string",
    "var3": "string",
    "var4": "string",
    "var5": "string",
    "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
    "tags": [
      "vip"
    ],
    "attrs": {},
    "created_at": 1767225600000,
    "updated_at": 1767225600000
  }
}