. Create an API key under API keys.API reference · Integrations
Webhooks
Endpoints that manage your outbound webhooks. The webhooks guide explains events, signatures and retries.
List webhooks
/v1/webhooksList webhooks for the calling tenant.
Responses
- 200List
Response fields
webhooksarray of objectrequiredwebhooks[].idstringrequiredwebhooks[].urlstring (uri)requiredwebhooks[].eventsarray of stringrequiredwebhooks[].created_atintegerrequiredwebhooks[].disabled_atinteger | nullrequired
curl "https://api.chatkomodo.com/v1/webhooks" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/webhooks', {
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/webhooks",
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/webhooks', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"webhooks": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"url": "https://example.com/file.jpg",
"events": [
"message.received"
],
"created_at": 1767225600000,
"disabled_at": 1767225600000
}
]
}Create a webhook
/v1/webhooksRegister an outbound webhook. URL must be HTTPS, secret must be ≥32 chars. HMAC-SHA256 signed.
Request body
urlstring (uri)requiredsecretstringrequiredeventsarray of stringrequired
Responses
- 201Webhook registered
Response fields
idstringrequiredurlstring (uri)requiredeventsarray of stringrequiredcreated_atintegerrequireddisabled_atinteger | nullrequired
- 400Validation error
curl -X POST "https://api.chatkomodo.com/v1/webhooks" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/chatkomodo",
"secret": "a-long-random-secret-of-32-characters-or-more",
"events": [
"message.received",
"message.failed",
"session.disconnected"
]
}'const res = await fetch('https://api.chatkomodo.com/v1/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"url": "https://example.com/webhooks/chatkomodo",
"secret": "a-long-random-secret-of-32-characters-or-more",
"events": [
"message.received",
"message.failed",
"session.disconnected"
]
}),
});
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/webhooks",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"url": "https://example.com/webhooks/chatkomodo",
"secret": "a-long-random-secret-of-32-characters-or-more",
"events": [
"message.received",
"message.failed",
"session.disconnected",
],
},
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/webhooks', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'url' => 'https://example.com/webhooks/chatkomodo',
'secret' => 'a-long-random-secret-of-32-characters-or-more',
'events' => [
'message.received',
'message.failed',
'session.disconnected',
],
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"url": "https://example.com/file.jpg",
"events": [
"message.received"
],
"created_at": 1767225600000,
"disabled_at": 1767225600000
}List webhook deliveries
/v1/webhooks/deliveriesDelivery log, newest first: one row per event and webhook, updated on every attempt. Use it to see why an endpoint is not receiving events.
Query parameters
limitintegerRows to return; values outside 1-200 are clampedwebhook_idstringOnly deliveries of this webhook
Responses
- 200Deliveries
Response fields
deliveriesarray of objectrequireddeliveries[].idstringrequireddeliveries[].webhook_idstringrequireddeliveries[].urlstringrequiredEndpoint URL at the time of deliverydeliveries[].eventstringrequiredEvent type, e.g.message.receivedorwebhook.testdeliveries[].statusstringrequiredretryingwhile attempts remain,failedonce they are exhausteddeliveries[].attemptsintegerrequireddeliveries[].response_statusinteger | nullrequiredHTTP status returned by the endpoint on the last attempt, or null if it never answereddeliveries[].errorstring | nullrequiredShort reason for the last failure:HTTP <status>,timeout,blocked_destination,dns_error,connection_refusedornetwork_errordeliveries[].created_atintegerrequiredUnix time in millisecondsdeliveries[].delivered_atinteger | nullrequiredWhen the delivery succeeded, or null
- 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)
curl "https://api.chatkomodo.com/v1/webhooks/deliveries?limit=50" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/webhooks/deliveries?limit=50', {
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/webhooks/deliveries?limit=50",
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/webhooks/deliveries?limit=50', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"deliveries": [
{
"id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"webhook_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
"url": "https://example.com/file.jpg",
"event": "message.received",
"status": "delivered",
"attempts": 1,
"response_status": 1,
"error": "string",
"created_at": 1767225600000,
"delivered_at": 1767225600000
}
]
}Update a webhook
/v1/webhooks/{id}Changes the URL, secret, event list and/or the paused state of a webhook. A new URL goes through the same HTTPS and private-address checks as on creation. Send only the fields to change.
Path parameters
idstringrequiredWebhook id
Request body
urlstring (uri)secretstringNew signing secret, at least 32 characters. Write-only: it is never returnedeventsarray of stringEvent types to deliver (1-50 entries, each up to 64 characters)disabledbooleantruepauses deliveries,falseresumes them
Responses
- 200Updated
Response fields
okbooleanrequired
- 400Validation failed (
VALIDATION_FAILED,INVALID_JSON) or the URL points at a blocked destination (WEBHOOK_URL_BLOCKED) - 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) - 404Webhook not found, or it belongs to another tenant (
NOT_FOUND)
curl -X PATCH "https://api.chatkomodo.com/v1/webhooks/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
"message.received",
"message.failed"
],
"disabled": false
}'const res = await fetch('https://api.chatkomodo.com/v1/webhooks/ID', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"events": [
"message.received",
"message.failed"
],
"disabled": false
}),
});
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/webhooks/ID",
headers={
"Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
},
json={
"events": [
"message.received",
"message.failed",
],
"disabled": False,
},
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/webhooks/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
'json' => [
'events' => [
'message.received',
'message.failed',
],
'disabled' => false,
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}Delete a webhook
/v1/webhooks/{id}Permanently deletes a webhook; no further events are delivered to it.
Path parameters
idstringrequiredWebhook 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) - 404Webhook not found, or it belongs to another tenant (
NOT_FOUND)
curl -X DELETE "https://api.chatkomodo.com/v1/webhooks/ID" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/webhooks/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/webhooks/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/webhooks/ID', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true
}Send a test event to a webhook
/v1/webhooks/{id}/testSends a signed webhook.test event to the webhook URL (10 second timeout) and reports the outcome. The attempt is recorded in the delivery log and is never retried. An unreachable or failing endpoint still returns HTTP 200 with ok: false.
Path parameters
idstringrequiredWebhook id
Responses
- 200Outcome of the delivery attempt
Response fields
okbooleanrequiredtruewhen the endpoint answered with a 2xx statusstatusinteger | nullrequiredHTTP status returned by the endpoint, or null when it could not be reachedmessagestringrequiredHuman-readable result, e.g.DeliveredorEndpoint responded HTTP 500
- 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) - 404Webhook not found, or it belongs to another tenant (
NOT_FOUND)
curl -X POST "https://api.chatkomodo.com/v1/webhooks/ID/test" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY"const res = await fetch('https://api.chatkomodo.com/v1/webhooks/ID/test', {
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/webhooks/ID/test",
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/webhooks/ID/test', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
],
]);
print_r(json_decode((string) $res->getBody(), true));{
"ok": true,
"status": 1,
"message": "Hi Sam, your order has shipped."
}