ChatKomodo Docs Home Sign in

API reference · Conversations

Agents & assignment

Customer-service agents who work the shared inbox, the queue of unassigned chats, assignment and resolving.

List customer-service agents

GET/v1/cs/agents

Agents of the tenant, ordered by name. Secrets (access code hash, authenticator secret) are never included.

Responses

  • 200Agents
    Response fields
    • agentsarray of objectrequired
    • agents[].idstringrequired
    • agents[].api_key_idstringrequired
      Owning tenant
    • agents[].namestringrequired
    • agents[].rolestringrequired
      Stored as given at creation (default agent)
    • agents[].is_onlineintegerrequired
      1 or 0
    • agents[].max_chatsinteger | nullrequired
      Concurrent chat cap
    • agents[].active_chatsinteger | nullrequired
      Chats currently assigned and unresolved
    • agents[].created_atintegerrequired
      Unix time in milliseconds
    • agents[].phonestring | nullrequired
      Login number, digits with country code
    • agents[].has_access_codebooleanrequired
      Whether an access code is set. The code and its hash are never returned
  • 401Missing, invalid or expired credentials
Request
curl "https://api.chatkomodo.com/v1/cs/agents" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/cs/agents', {
  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/cs/agents",
    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/cs/agents', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "agents": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "api_key_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "name": "Sam Carter",
      "role": "agent",
      "is_online": 1,
      "max_chats": 1,
      "active_chats": 1,
      "created_at": 1767225600000,
      "phone": "+447700900123",
      "has_access_code": true
    }
  ]
}

Create a customer-service agent

POST/v1/cs/agents

Adds an agent who can sign in to the dashboard and work the shared inbox. An agent signs in with a WhatsApp number plus one-time code (phone), or with an access_code. The phone number must not already belong to another agent or to a tenant owner login.

Request body

  • namestringrequired
    Display name; must contain a non-blank value
  • rolestring
    Stored as given (default agent)
  • max_chatsinteger
    Concurrent chat cap (default 10)
  • access_codestring
    Optional login code. Stored only as a salted hash and never returned
  • phonestring
    WhatsApp number used for one-time-code login; international format, or local format resolved against the business country

Responses

  • 201Created
    Response fields
    • idstringrequired
    • phonestring | nullrequired
      Normalised login number (digits), or null when none was given
  • 400name is missing or blank (VALIDATION_FAILED)
  • 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)
  • 409The number is already used by another account (PHONE_IN_USE)
Request
curl -X POST "https://api.chatkomodo.com/v1/cs/agents" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sam Carter",
  "phone": "+447700900123",
  "max_chats": 10
}'
const res = await fetch('https://api.chatkomodo.com/v1/cs/agents', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Sam Carter",
    "phone": "+447700900123",
    "max_chats": 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/cs/agents",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Sam Carter",
        "phone": "+447700900123",
        "max_chats": 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/cs/agents', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Sam Carter',
        'phone' => '+447700900123',
        'max_chats' => 10,
    ],
]);

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

Edit a customer-service agent

PATCH/v1/cs/agents/{id}

Changes the name, online flag, chat cap and/or access code of one of the tenant's agents. Send only the fields to change. An id that does not exist (or belongs to another tenant) changes nothing but still returns ok: true. The phone number cannot be changed here.

Path parameters

  • idstringrequired
    Agent id

Request body

  • namestring
  • is_onlineboolean
  • max_chatsinteger
    Concurrent chat cap
  • access_codestring
    New login code (ignored when empty). Stored only as a salted hash

Responses

  • 200Updated
    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)
Request
curl -X PATCH "https://api.chatkomodo.com/v1/cs/agents/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sam Carter",
  "is_online": true,
  "max_chats": 1,
  "access_code": "482913"
}'
const res = await fetch('https://api.chatkomodo.com/v1/cs/agents/ID', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Sam Carter",
    "is_online": true,
    "max_chats": 1,
    "access_code": "482913"
  }),
});
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/cs/agents/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Sam Carter",
        "is_online": True,
        "max_chats": 1,
        "access_code": "482913",
    },
    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/cs/agents/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Sam Carter',
        'is_online' => true,
        'max_chats' => 1,
        'access_code' => '482913',
    ],
]);

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

Delete a customer-service agent

DEL/v1/cs/agents/{id}

Deletes the agent. Chats assigned to them become unassigned.

Path parameters

  • idstringrequired
    Agent 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)
  • 404Agent not found (no code)
Request
curl -X DELETE "https://api.chatkomodo.com/v1/cs/agents/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/cs/agents/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/cs/agents/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/cs/agents/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

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

Sign an agent out everywhere

POST/v1/cs/agents/{id}/revoke-sessions

Deletes all refresh tokens of the agent so they can no longer renew their login. An access token that was already issued stays valid until it expires. Recorded in the audit log as agent.revoke_sessions.

Path parameters

  • idstringrequired
    Agent id

Responses

  • 200Revoked
    Response fields
    • okbooleanrequired
    • revokedintegerrequired
      Number of refresh tokens deleted
  • 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)
  • 404Agent not found (no code)
Request
curl -X POST "https://api.chatkomodo.com/v1/cs/agents/ID/revoke-sessions" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/cs/agents/ID/revoke-sessions', {
  method: 'POST',
  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.post(
    "https://api.chatkomodo.com/v1/cs/agents/ID/revoke-sessions",
    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('POST', 'https://api.chatkomodo.com/v1/cs/agents/ID/revoke-sessions', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

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

List unassigned open chats

GET/v1/cs/queue

Open chats on the tenant's devices that no agent is assigned to yet, most recent activity first. Available to agents as well as the owner.

Responses

  • 200Queue
    Response fields
    • queuearray of objectrequired
    • queue[].idstringrequired
    • queue[].session_idstringrequired
    • queue[].chat_idstringrequired
    • queue[].chat_namestring | nullrequired
    • queue[].last_messagestring | nullrequired
    • queue[].last_message_atinteger | nullrequired
    • queue[].unread_countinteger | nullrequired
    • queue[].is_groupinteger | nullrequired
      1 = group chat
    • queue[].agent_idany | nullrequired
      Always null in the queue
    • queue[].statusstringrequired
      open
  • 401Missing, invalid or expired credentials
Request
curl "https://api.chatkomodo.com/v1/cs/queue" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/cs/queue', {
  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/cs/queue",
    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/cs/queue', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "queue": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "session_id": "YOUR_DEVICE_ID",
      "chat_id": "+447700900123",
      "chat_name": "string",
      "last_message": "Hi Sam, your order has shipped.",
      "last_message_at": 1767225600000,
      "unread_count": 12,
      "is_group": 1,
      "agent_id": null,
      "status": "active"
    }
  ]
}

Assign a chat to an agent

POST/v1/cs/assign

Assigns a chat to one of the tenant's agents (replacing any earlier assignment) and updates the agent's active-chat count. Available to agents as well as the owner. The body is not validated beyond ownership of agent_id and session_id.

Request body

  • chat_idstringrequired
    Chat JID
  • session_idstringrequired
    Device the chat belongs to
  • agent_idstringrequired
    Agent to assign

Responses

  • 200Assigned
    Response fields
    • okbooleanrequired
  • 401Missing, invalid or expired credentials
  • 403The agent or the device is not the tenant's (message Forbidden, no code)
Request
curl -X POST "https://api.chatkomodo.com/v1/cs/assign" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "session_id": "YOUR_DEVICE_ID",
  "chat_id": "[email protected]",
  "agent_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
}'
const res = await fetch('https://api.chatkomodo.com/v1/cs/assign', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "session_id": "YOUR_DEVICE_ID",
    "chat_id": "[email protected]",
    "agent_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/cs/assign",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "session_id": "YOUR_DEVICE_ID",
        "chat_id": "[email protected]",
        "agent_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/cs/assign', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'session_id' => 'YOUR_DEVICE_ID',
        'chat_id' => '[email protected]',
        'agent_id' => '01JA5V6Q8M2H7G4K9P3T0R1S2X',
    ],
]);

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

Mark a chat as resolved

POST/v1/cs/resolve

Marks a chat resolved, which removes it from the open queue, and frees one slot of the assigned agent. Available to agents as well as the owner.

Request body

  • chat_idstringrequired
    Chat JID
  • session_idstringrequired
    Device the chat belongs to

Responses

  • 200Resolved
    Response fields
    • okbooleanrequired
  • 401Missing, invalid or expired credentials
  • 403The device is not the tenant's (message Forbidden, no code)
Request
curl -X POST "https://api.chatkomodo.com/v1/cs/resolve" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "session_id": "YOUR_DEVICE_ID",
  "chat_id": "[email protected]"
}'
const res = await fetch('https://api.chatkomodo.com/v1/cs/resolve', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "session_id": "YOUR_DEVICE_ID",
    "chat_id": "[email protected]"
  }),
});
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/cs/resolve",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "session_id": "YOUR_DEVICE_ID",
        "chat_id": "[email protected]",
    },
    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/cs/resolve', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'session_id' => 'YOUR_DEVICE_ID',
        'chat_id' => '[email protected]',
    ],
]);

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