ChatKomodo Docs Home Sign in

API reference · Customers

Contacts

The phone book: import up to 10 000 contacts per call with tags and custom attributes, groups, update and delete.

List contacts

GET/v1/contacts

List contacts (max 10 000 per call; page with offset).

Query parameters

  • segmentstring
    Saved segment id
  • qstring
  • tagstring
  • group_idstring
  • limitinteger
    From 1 to 10 000
  • offsetinteger | null

Responses

  • 200Contacts
    Response fields
    • 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
    • totalintegerrequired
Request
curl "https://api.chatkomodo.com/v1/contacts?tag=vip&limit=100" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/contacts?tag=vip&limit=100', {
  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/contacts?tag=vip&limit=100",
    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/contacts?tag=vip&limit=100', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "contacts": [
    {
      "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
    }
  ],
  "total": 12
}

Import contacts

POST/v1/contacts

Import up to 10 000 contacts. A number that already exists is merged into the existing contact (tags are unioned) instead of duplicated. Rows that cannot be stored are returned in rejected (first 50).

Request body

  • contactsarray of objectrequired
    Up to 10 000 items
  • contacts[].phonestringrequired
    International format +<country code><number>; local-format numbers use the business country
  • contacts[].namestring
    Up to 200 characters
  • contacts[].varsarray of string
    Legacy columns {var1}…{var5}
    Up to 5 items
  • contacts[].group_idstring
  • contacts[].tagsarray of string
    Merged (union) with the tags the contact already has
  • contacts[].attrsobject
    Custom attributes, keys as defined in /v1/crm/fields
  • contacts[].opt_inboolean
    true records consent with source import (you state this person agreed to be messaged). It never overrides an existing opt-out.

Responses

  • 201Imported
    Response fields
    • idsarray of stringrequired
    • countintegerrequired
    • createdintegerrequired
    • updatedintegerrequired
    • rejectedarray of object
    • rejected[].indexintegerrequired
    • rejected[].errorstringrequired
  • 400Validation error
Request
curl -X POST "https://api.chatkomodo.com/v1/contacts" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "contacts": [
    {
      "phone": "+447700900123",
      "name": "Sam Carter",
      "tags": [
        "vip"
      ],
      "attrs": {
        "plan": "Pro",
        "city": "London"
      },
      "opt_in": true
    }
  ]
}'
const res = await fetch('https://api.chatkomodo.com/v1/contacts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "contacts": [
      {
        "phone": "+447700900123",
        "name": "Sam Carter",
        "tags": [
          "vip"
        ],
        "attrs": {
          "plan": "Pro",
          "city": "London"
        },
        "opt_in": true
      }
    ]
  }),
});
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/contacts",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "contacts": [
            {
                "phone": "+447700900123",
                "name": "Sam Carter",
                "tags": [
                    "vip",
                ],
                "attrs": {
                    "plan": "Pro",
                    "city": "London",
                },
                "opt_in": True,
            },
        ],
    },
    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/contacts', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'contacts' => [
            [
                'phone' => '+447700900123',
                'name' => 'Sam Carter',
                'tags' => [
                    'vip',
                ],
                'attrs' => [
                    'plan' => 'Pro',
                    'city' => 'London',
                ],
                'opt_in' => true,
            ],
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 201
{
  "ids": [
    "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  ],
  "count": 12,
  "created": 1,
  "updated": 1,
  "rejected": [
    {
      "index": 1,
      "error": "string"
    }
  ]
}

Update a contact

PATCH/v1/contacts/{id}

Update one contact. attrs is merged: an empty string removes a key.

Path parameters

  • idstringrequired

Request body

  • phonestring
  • namestring | null
  • varsarray of string | null
    Up to 5 items
  • group_idstring | null
  • tagsarray of string
  • attrsobject

Responses

  • 200Updated
    Response fields
    • okbooleanrequired
    • 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
  • 404Contact or group not found
Request
curl -X PATCH "https://api.chatkomodo.com/v1/contacts/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sam Carter-Jones",
  "tags": [
    "vip",
    "newsletter"
  ],
  "attrs": {
    "city": "Bristol"
  }
}'
const res = await fetch('https://api.chatkomodo.com/v1/contacts/ID', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Sam Carter-Jones",
    "tags": [
      "vip",
      "newsletter"
    ],
    "attrs": {
      "city": "Bristol"
    }
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.patch(
    "https://api.chatkomodo.com/v1/contacts/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Sam Carter-Jones",
        "tags": [
            "vip",
            "newsletter",
        ],
        "attrs": {
            "city": "Bristol",
        },
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('PATCH', 'https://api.chatkomodo.com/v1/contacts/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Sam Carter-Jones',
        'tags' => [
            'vip',
            'newsletter',
        ],
        'attrs' => [
            'city' => 'Bristol',
        ],
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "ok": true,
  "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
  }
}

Delete a contact

DEL/v1/contacts/{id}

Delete one contact.

Path parameters

  • idstringrequired

Responses

  • 200Deleted
    Response fields
    • okbooleanrequired
Request
curl -X DELETE "https://api.chatkomodo.com/v1/contacts/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/contacts/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/contacts/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/contacts/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

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

Delete or move many contacts

POST/v1/contacts/bulk

Delete or move many contacts at once.

Request body

  • idsarray of stringrequired
    1–10 000 items
  • actionstringrequired
    One of: delete, move
  • group_idstring | null
    Target group for move (null = no group)

Responses

  • 200Result: {deleted} or {moved}
    Response fields
    • deletedinteger
    • movedinteger
Request
curl -X POST "https://api.chatkomodo.com/v1/contacts/bulk" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ids": [
    "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  ],
  "action": "move",
  "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
}'
const res = await fetch('https://api.chatkomodo.com/v1/contacts/bulk', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "ids": [
      "01JA5V6Q8M2H7G4K9P3T0R1S2X"
    ],
    "action": "move",
    "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  }),
});
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/contacts/bulk",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "ids": [
            "01JA5V6Q8M2H7G4K9P3T0R1S2X",
        ],
        "action": "move",
        "group_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
    },
    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/contacts/bulk', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'ids' => [
            '01JA5V6Q8M2H7G4K9P3T0R1S2X',
        ],
        'action' => 'move',
        'group_id' => '01JA5V6Q8M2H7G4K9P3T0R1S2X',
    ],
]);

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

List contact groups

GET/v1/contact-groups

Phone-book groups of the tenant, ordered by name, with the number of contacts in each.

Responses

  • 200Groups
    Response fields
    • groupsarray of objectrequired
    • groups[].idstringrequired
    • groups[].api_key_idstringrequired
      Owning tenant
    • groups[].namestringrequired
    • groups[].created_atintegerrequired
      Unix time in milliseconds
    • groups[].contact_countintegerrequired
  • 401Missing, invalid or expired credentials
  • 403Forbidden for an API key restricted to specific sessions (KEY_SCOPE_FORBIDDEN)
Request
curl "https://api.chatkomodo.com/v1/contact-groups" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/contact-groups', {
  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/contact-groups",
    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/contact-groups', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "groups": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "api_key_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "name": "VIP customers",
      "created_at": 1767225600000,
      "contact_count": 12
    }
  ]
}

Create a contact group

POST/v1/contact-groups

Creates a phone-book group. The handler does not validate name, but it is required.

Request body

  • namestringrequired
    Group name

Responses

  • 201Created
    Response fields
    • idstringrequired
  • 401Missing, invalid or expired credentials
  • 403Forbidden for a customer-service agent login (Agent access is not allowed for this operation) and an API key restricted to specific sessions (KEY_SCOPE_FORBIDDEN)
Request
curl -X POST "https://api.chatkomodo.com/v1/contact-groups" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "VIP customers"
}'
const res = await fetch('https://api.chatkomodo.com/v1/contact-groups', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "VIP customers"
  }),
});
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/contact-groups",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "VIP customers",
    },
    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/contact-groups', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'VIP customers',
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 201
{
  "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
}

Delete a contact group

DEL/v1/contact-groups/{id}

Deletes the group. Its contacts are kept and simply lose their group.

Path parameters

  • idstringrequired
    Group id

Responses

  • 200Deleted
    Response fields
    • okbooleanrequired
  • 401Missing, invalid or expired credentials
  • 403Forbidden for a customer-service agent login (Agent access is not allowed for this operation) and an API key restricted to specific sessions (KEY_SCOPE_FORBIDDEN)
  • 404Group not found (no code)
Request
curl -X DELETE "https://api.chatkomodo.com/v1/contact-groups/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/contact-groups/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/contact-groups/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/contact-groups/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

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