. Create an API key under API keys.API reference · Integrations
Sign-in security
The owner's dashboard sign-in: signed-in devices and signing them out, an authenticator code at every sign-in, passkeys. Dashboard login only.
Sign-in security status
/v1/securityWhether the authenticator app is set up and required at every sign-in, how many passkeys and signed-in sessions there are.
Responses
- 200Status
Response fields
totp_enabledbooleanrequiredrequire_totpbooleanrequiredpasskeysintegerrequiredsessionsintegerrequired
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key
curl "https://api.chatkomodo.com/v1/security" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/security', {
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/security",
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/security', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"totp_enabled": true,
"require_totp": true,
"passkeys": 1,
"sessions": 1
}Require the authenticator code at every sign-in
/v1/securityWith require_totp: true, signing in with the WhatsApp code (or the access code) asks for the authenticator code as well (/auth/verify-mfa). Needs the authenticator app set up first. Recorded as security.require_totp.
Request body
require_totpbooleanrequired
Responses
- 200Saved
Response fields
okbooleanrequiredrequire_totpbooleanrequired
- 400Body is not valid JSON (
INVALID_JSON) or fails validation (VALIDATION_FAILED) - 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key - 404Dashboard login not found, or it belongs to another tenant (
NOT_FOUND) - 409
TOTP_NOT_ENABLED: set up the authenticator app first
curl -X PUT "https://api.chatkomodo.com/v1/security" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"require_totp": true
}'const res = await fetch('https://api.chatkomodo.com/v1/security', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"require_totp": 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.put(
"https://api.chatkomodo.com/v1/security",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"require_totp": True,
},
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/security', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'require_totp' => true,
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"require_totp": true
}Signed-in devices
/v1/security/sessionsDashboard sessions that are still signed in (owner first, then agents), with device, address and country. A sign-in from a device not seen for that person in 180 days notifies the owner (push, and WhatsApp for the owner's own sign-ins) and is recorded as auth.new_device.
Responses
- 200Sessions
Response fields
sessionsarray of objectrequiredsessions[].idstringrequiredShort id forPOST /v1/security/sessions/{id}/revokesessions[].rolestringrequiredsessions[].agent_idstring | nullrequiredsessions[].agent_namestring | nullrequiredsessions[].methodstringrequiredHow this session signed insessions[].ipstring | nullrequiredsessions[].countrystring | nullrequiredISO code as Cloudflare reports itsessions[].devicestringrequiredBrowser and operating system, e.g.Chrome on Windowssessions[].created_atintegerrequiredUnix time in millisecondssessions[].last_seen_atintegerrequiredSign-in or last token refreshsessions[].currentbooleanrequiredThe session making this request
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key
curl "https://api.chatkomodo.com/v1/security/sessions" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/security/sessions', {
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/security/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('GET', 'https://api.chatkomodo.com/v1/security/sessions', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"sessions": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"role": "admin",
"agent_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"agent_name": "string",
"method": "otp",
"ip": "string",
"country": "GB",
"device": "YOUR_DEVICE_ID",
"created_at": 1767225600000,
"last_seen_at": 1767225600000,
"current": true
}
]
}Sign one device out
/v1/security/sessions/{id}/revokeEnds a session: its access token stops working at once and its refresh token is deleted. Recorded as security.sign_out.
Path parameters
idstringrequiredSession id
Responses
- 200Signed out
Response fields
okbooleanrequiredcurrentbooleanrequiredtruewhen it was the calling session
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key - 404Session not found, or it belongs to another tenant (
NOT_FOUND)
curl -X POST "https://api.chatkomodo.com/v1/security/sessions/ID/revoke" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/security/sessions/ID/revoke', {
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/security/sessions/ID/revoke",
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/security/sessions/ID/revoke', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"current": true
}Sign out every other device
/v1/security/sessions/revoke-allEnds every owner session except the calling one; with include_agents: true also every agent session. Recorded as security.sign_out_all.
Request body
include_agentsboolean
Responses
- 200Signed out
Response fields
okbooleanrequiredsigned_outintegerrequired
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key
curl -X POST "https://api.chatkomodo.com/v1/security/sessions/revoke-all" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"include_agents": true
}'const res = await fetch('https://api.chatkomodo.com/v1/security/sessions/revoke-all', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"include_agents": 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/security/sessions/revoke-all",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"include_agents": 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/security/sessions/revoke-all', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'include_agents' => true,
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"signed_out": 1
}List passkeys
/v1/security/passkeysThe owner's passkeys (sign-in without a code, phishing-resistant).
Responses
- 200Passkeys
Response fields
passkeysarray of objectrequiredpasskeys[].idstringrequiredpasskeys[].namestringrequiredpasskeys[].created_atintegerrequiredUnix time in millisecondspasskeys[].last_used_atinteger | nullrequiredLast sign-in with it
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key
curl "https://api.chatkomodo.com/v1/security/passkeys" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/security/passkeys', {
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/security/passkeys",
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/security/passkeys', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"passkeys": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example",
"created_at": 1767225600000,
"last_used_at": 1767225600000
}
]
}Add a passkey
/v1/security/passkeysVerifies the browser's registration response and stores the passkey. Recorded as security.passkey_add.
Request body
challenge_idstringrequiredresponseobjectrequiredRegistrationResponseJSONnamestringLabel, e.g. "MacBook" (defaultPasskey)
Responses
- 201Added
Response fields
okbooleanrequiredpasskeyobjectrequiredpasskey.idstringrequiredpasskey.namestringrequired
- 400Missing fields, expired request (
CHALLENGE_EXPIRED) or failed verification (PASSKEY_INVALID) - 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key
curl -X POST "https://api.chatkomodo.com/v1/security/passkeys" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"challenge_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"response": {},
"name": "Example"
}'const res = await fetch('https://api.chatkomodo.com/v1/security/passkeys', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"challenge_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"response": {},
"name": "Example"
}),
});
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/security/passkeys",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"challenge_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"response": {},
"name": "Example",
},
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/security/passkeys', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'challenge_id' => '01JA5V6Q8M2H7G4K9P3T0R1S2X',
'response' => [],
'name' => 'Example',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"passkey": {
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"name": "Example"
}
}Start adding a passkey
/v1/security/passkeys/optionsWebAuthn registration options (resident key required). Pass options to the browser (navigator.credentials.create / @simplewebauthn/browser startRegistration), then send the result to POST /v1/security/passkeys within 5 minutes. At most 10 passkeys.
Responses
- 200Options
Response fields
challenge_idstringrequiredoptionsobjectrequiredPublicKeyCredentialCreationOptionsJSON
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key - 409
LIMIT_REACHED: 10 passkeys already
curl -X POST "https://api.chatkomodo.com/v1/security/passkeys/options" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/security/passkeys/options', {
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/security/passkeys/options",
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/security/passkeys/options', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"challenge_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"options": {}
}Rename a passkey
/v1/security/passkeys/{id}Changes the label shown in Settings → Security.
Path parameters
idstringrequiredPasskey id
Request body
namestringrequired
Responses
- 200Renamed
Response fields
okbooleanrequired
- 400Body is not valid JSON (
INVALID_JSON) or fails validation (VALIDATION_FAILED) - 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key - 404Passkey not found, or it belongs to another tenant (
NOT_FOUND)
curl -X PATCH "https://api.chatkomodo.com/v1/security/passkeys/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Example"
}'const res = await fetch('https://api.chatkomodo.com/v1/security/passkeys/ID', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"name": "Example"
}),
});
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/security/passkeys/ID",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"name": "Example",
},
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/security/passkeys/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'name' => 'Example',
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}Remove a passkey
/v1/security/passkeys/{id}It can no longer sign in. Recorded as security.passkey_remove.
Path parameters
idstringrequiredPasskey id
Responses
- 200Removed
Response fields
okbooleanrequired
- 401Missing, invalid or expired credentials
- 403Forbidden (
FORBIDDEN): only the business owner's dashboard login, not an agent or an API key - 404Passkey not found, or it belongs to another tenant (
NOT_FOUND)
curl -X DELETE "https://api.chatkomodo.com/v1/security/passkeys/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/security/passkeys/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/security/passkeys/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/security/passkeys/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}