ChatKomodo Docs Home Sign in

API reference · Messaging

Scheduled messages

One-off messages sent at a chosen time (scheduled_at, Unix milliseconds).

List scheduled messages

GET/v1/sessions/{sessionId}/scheduled

All scheduled messages of the session, sent or not, ordered by due time (oldest first).

Path parameters

  • sessionIdstringrequired
    Session (device) id

Responses

  • 200Scheduled messages
    Response fields
    • scheduledarray of objectrequired
    • scheduled[].idstringrequired
    • scheduled[].session_idstringrequired
    • scheduled[].api_key_idstringrequired
      Owning tenant
    • scheduled[].chat_idstringrequired
      Recipient JID as it was submitted
    • scheduled[].message_typestringrequired
    • scheduled[].bodystring | nullrequired
    • scheduled[].media_r2_keystring | nullrequired
    • scheduled[].captionstring | nullrequired
    • scheduled[].scheduled_atintegerrequired
      Time the message is due (Unix epoch, milliseconds)
    • scheduled[].statusstringrequired
      pending, sending, sent or failed
    • scheduled[].message_idstring | nullrequired
      Gateway message id of the sent message, once sent
    • scheduled[].errorstring | nullrequired
      Failure reason (for example opted_out)
    • scheduled[].created_atintegerrequired
      Creation time (Unix epoch, milliseconds)
  • 401Missing or invalid credentials
  • 403The API key is restricted to other sessions
  • 404Unknown session, or a session of another tenant
Request
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));
Response 200
{
  "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

POST/v1/sessions/{sessionId}/scheduled

Queues 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

  • sessionIdstringrequired
    Session (device) id

Request body

  • chat_idstringrequired
    Recipient JID, e.g. [email protected]; phone numbers are not normalised
  • message_typestring
    Default text
  • bodystring
    Message text
  • media_r2_keystring
    Media key from /v1/media/upload-url
  • captionstring
  • scheduled_atintegerrequired
    Delivery time (Unix epoch, milliseconds)

Responses

  • 201Scheduled
    Response fields
    • idstringrequired
    • statusstringrequired
      One of: pending
  • 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)
Request
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));
Response 201
{
  "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "status": "pending"
}

Cancel a scheduled message

DEL/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

  • sessionIdstringrequired
    Session (device) id
  • msgIdstringrequired
    Scheduled message id (id from 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 (no code); unknown session, or a session of another tenant
Request
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));
Response 200
{
  "ok": true
}

List scheduled messages

GET/v1/scheduled

All scheduled messages of the tenant across devices, earliest first, with their current status.

Responses

  • 200Scheduled messages
    Response fields
    • messagesarray of objectrequired
    • messages[].idstringrequired
    • messages[].session_idstringrequired
    • messages[].api_key_idstringrequired
      Owning tenant
    • messages[].chat_idstringrequired
    • messages[].message_typestringrequired
    • messages[].bodystring | nullrequired
    • messages[].media_r2_keystring | nullrequired
    • messages[].captionstring | nullrequired
    • messages[].scheduled_atintegerrequired
      When to send, Unix time in milliseconds
    • messages[].statusstringrequired
      pending, sending, sent or failed
    • messages[].message_idstring | nullrequired
      Id of the sent message once sent
    • messages[].errorstring | nullrequired
      Failure reason when failed, e.g. opted_out
    • messages[].created_atintegerrequired
      Unix time in milliseconds
  • 401Missing, invalid or expired credentials
Request
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));
Response 200
{
  "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

POST/v1/scheduled

Queues 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_idstringrequired
    Device to send from; must belong to the tenant
  • chat_idstringrequired
    Recipient JID (stored as given, not normalised)
  • scheduled_atintegerrequired
    When to send, Unix time in milliseconds
  • message_typestring
    text (default), image, document or audio
  • bodystring
    Text content
  • media_r2_keystring
    Key of an uploaded media file

Responses

  • 201Scheduled
    Response fields
    • idstringrequired
    • statusstringrequired
      One of: pending
  • 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)
Request
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));
Response 201
{
  "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "status": "pending"
}

Cancel a scheduled message

DEL/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

  • idstringrequired
    Scheduled 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)
Request
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));
Response 200
{
  "ok": true
}