. Create an API key under API keys.API reference · Messaging
Scheduled messages
One-off messages sent at a chosen time (scheduled_at, Unix milliseconds).
List scheduled messages
/v1/sessions/{sessionId}/scheduledAll scheduled messages of the session, sent or not, ordered by due time (oldest first).
Path parameters
sessionIdstringrequiredSession (device) id
Responses
- 200Scheduled messages
Response fields
scheduledarray of objectrequiredscheduled[].idstringrequiredscheduled[].session_idstringrequiredscheduled[].api_key_idstringrequiredOwning tenantscheduled[].chat_idstringrequiredRecipient JID as it was submittedscheduled[].message_typestringrequiredscheduled[].bodystring | nullrequiredscheduled[].media_r2_keystring | nullrequiredscheduled[].captionstring | nullrequiredscheduled[].scheduled_atintegerrequiredTime the message is due (Unix epoch, milliseconds)scheduled[].statusstringrequiredpending,sending,sentorfailedscheduled[].message_idstring | nullrequiredGateway message id of the sent message, once sentscheduled[].errorstring | nullrequiredFailure reason (for exampleopted_out)scheduled[].created_atintegerrequiredCreation time (Unix epoch, milliseconds)
- 401Missing or invalid credentials
- 403The API key is restricted to other sessions
- 404Unknown session, or a session of another tenant
curl "https://api.chatkomodo.com/v1/sessions/YOUR_DEVICE_ID/scheduled" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/sessions/YOUR_DEVICE_ID/scheduled', {
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/sessions/YOUR_DEVICE_ID/scheduled",
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/sessions/YOUR_DEVICE_ID/scheduled', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"scheduled": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"session_id": "YOUR_DEVICE_ID",
"api_key_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"chat_id": "+447700900123",
"message_type": "Hi Sam, your order has shipped.",
"body": "Hi Sam, your order has shipped.",
"media_r2_key": "uploads/abc123.jpg",
"caption": "Hi Sam, your order has shipped.",
"scheduled_at": 1767225600000,
"status": "active",
"message_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"error": "string",
"created_at": 1767225600000
}
]
}Schedule a message
/v1/sessions/{sessionId}/scheduledQueues a message for later delivery. A worker checks every 10 seconds and sends due messages once the session is connected; the bulk send window does not apply. A recipient who opted out before delivery makes the message failed with error opted_out. The body is not validated: chat_id is stored as given and must be a full JID, and a missing scheduled_at ends in a 500. Limited to 20 calls per minute per key.
Path parameters
sessionIdstringrequiredSession (device) id
Request body
chat_idstringrequiredRecipient JID, e.g.[email protected]; phone numbers are not normalisedmessage_typestringDefaulttextbodystringMessage textmedia_r2_keystringMedia key from/v1/media/upload-urlcaptionstringscheduled_atintegerrequiredDelivery time (Unix epoch, milliseconds)
Responses
- 201Scheduled
Response fields
idstringrequiredstatusstringrequired
- 401Missing or invalid credentials
- 403The API key is restricted to other sessions, or the operation is not available to CS-agent logins
- 404Unknown session, or a session of another tenant
- 429Strict rate limit exceeded: 20 requests per minute, shared with
POST /v1/scheduled(RATE_LIMITED)
curl -X POST "https://api.chatkomodo.com/v1/sessions/YOUR_DEVICE_ID/scheduled" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "+447700900123",
"message_type": "text",
"body": "Reminder: your appointment is tomorrow at 10:00.",
"scheduled_at": 1767258000000
}'const res = await fetch('https://api.chatkomodo.com/v1/sessions/YOUR_DEVICE_ID/scheduled', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"chat_id": "+447700900123",
"message_type": "text",
"body": "Reminder: your appointment is tomorrow at 10:00.",
"scheduled_at": 1767258000000
}),
});
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/sessions/YOUR_DEVICE_ID/scheduled",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"chat_id": "+447700900123",
"message_type": "text",
"body": "Reminder: your appointment is tomorrow at 10:00.",
"scheduled_at": 1767258000000,
},
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/sessions/YOUR_DEVICE_ID/scheduled', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'chat_id' => '+447700900123',
'message_type' => 'text',
'body' => 'Reminder: your appointment is tomorrow at 10:00.',
'scheduled_at' => 1767258000000,
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"status": "pending"
}Cancel a scheduled message
/v1/sessions/{sessionId}/scheduled/{msgId}Deletes a scheduled message that has not been sent yet. Messages that are already sending, sent or failed cannot be removed.
Path parameters
sessionIdstringrequiredSession (device) idmsgIdstringrequiredScheduled message id (idfrom the list)
Responses
- 200Removed
Response fields
okbooleanrequired
- 401Missing or invalid credentials
- 403The API key is restricted to other sessions, or the operation is not available to CS-agent logins
- 404Not found, not yours, or no longer
pending(nocode); unknown session, or a session of another tenant
curl -X DELETE "https://api.chatkomodo.com/v1/sessions/YOUR_DEVICE_ID/scheduled/MSG_ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/sessions/YOUR_DEVICE_ID/scheduled/MSG_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/sessions/YOUR_DEVICE_ID/scheduled/MSG_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/sessions/YOUR_DEVICE_ID/scheduled/MSG_ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}List scheduled messages
/v1/scheduledAll scheduled messages of the tenant across devices, earliest first, with their current status.
Responses
- 200Scheduled messages
Response fields
messagesarray of objectrequiredmessages[].idstringrequiredmessages[].session_idstringrequiredmessages[].api_key_idstringrequiredOwning tenantmessages[].chat_idstringrequiredmessages[].message_typestringrequiredmessages[].bodystring | nullrequiredmessages[].media_r2_keystring | nullrequiredmessages[].captionstring | nullrequiredmessages[].scheduled_atintegerrequiredWhen to send, Unix time in millisecondsmessages[].statusstringrequiredpending,sending,sentorfailedmessages[].message_idstring | nullrequiredId of the sent message oncesentmessages[].errorstring | nullrequiredFailure reason whenfailed, e.g.opted_outmessages[].created_atintegerrequiredUnix time in milliseconds
- 401Missing, invalid or expired credentials
curl "https://api.chatkomodo.com/v1/scheduled" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/scheduled', {
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/scheduled",
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/scheduled', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"messages": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"session_id": "YOUR_DEVICE_ID",
"api_key_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"chat_id": "+447700900123",
"message_type": "Hi Sam, your order has shipped.",
"body": "Hi Sam, your order has shipped.",
"media_r2_key": "uploads/abc123.jpg",
"caption": "Hi Sam, your order has shipped.",
"scheduled_at": 1767225600000,
"status": "active",
"message_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"error": "string",
"created_at": 1767225600000
}
]
}Schedule a message
/v1/scheduledQueues one message to be sent through a device at scheduled_at. The server sends it once the time has passed and the device is connected; a recipient who has opted out by then is skipped and the message is marked failed. Limited to 20 requests per minute per key. The handler validates only that the device is yours: chat_id, scheduled_at and the content fields are stored as given, so send a full JID and a millisecond timestamp.
Request body
session_idstringrequiredDevice to send from; must belong to the tenantchat_idstringrequiredRecipient JID (stored as given, not normalised)scheduled_atintegerrequiredWhen to send, Unix time in millisecondsmessage_typestringtext(default),image,documentoraudiobodystringText contentmedia_r2_keystringKey of an uploaded media file
Responses
- 201Scheduled
Response fields
idstringrequiredstatusstringrequired
- 401Missing, invalid or expired credentials
- 403Forbidden for a customer-service agent login (
Agent access is not allowed for this operation) - 404Device not found or not yours (no
code) - 429More than 20 scheduling requests in a minute (
RATE_LIMITED)
curl -X POST "https://api.chatkomodo.com/v1/scheduled" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "YOUR_DEVICE_ID",
"chat_id": "+447700900123",
"message_type": "text",
"body": "Reminder: your appointment is tomorrow at 10:00.",
"scheduled_at": 1767258000000
}'const res = await fetch('https://api.chatkomodo.com/v1/scheduled', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"session_id": "YOUR_DEVICE_ID",
"chat_id": "+447700900123",
"message_type": "text",
"body": "Reminder: your appointment is tomorrow at 10:00.",
"scheduled_at": 1767258000000
}),
});
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/scheduled",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"session_id": "YOUR_DEVICE_ID",
"chat_id": "+447700900123",
"message_type": "text",
"body": "Reminder: your appointment is tomorrow at 10:00.",
"scheduled_at": 1767258000000,
},
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/scheduled', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'session_id' => 'YOUR_DEVICE_ID',
'chat_id' => '+447700900123',
'message_type' => 'text',
'body' => 'Reminder: your appointment is tomorrow at 10:00.',
'scheduled_at' => 1767258000000,
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"status": "pending"
}Cancel a scheduled message
/v1/scheduled/{id}Deletes a scheduled message that has not been sent yet. A message that is already sending, sent or failed cannot be deleted.
Path parameters
idstringrequiredScheduled message 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) - 404No pending scheduled message with this id for the tenant (no
code)
curl -X DELETE "https://api.chatkomodo.com/v1/scheduled/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/scheduled/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/scheduled/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/scheduled/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}