Webhooks and events
GET/v1/webhook-endpoints
List webhook endpoints
Response 200
| Name | Type | Description |
|---|---|---|
data required | object[] | |
data[].id required | string | |
data[].url required | string | |
data[].events required | string[] | |
data[].status required | "active" | "disabled" | |
data[].livemode required | boolean | |
data[].created_at required | string |
Example
curl "https://api.kanutus.com/v1/webhook-endpoints" \
-H "Authorization: Bearer $KANUTUS_API_KEY"const res = await fetch("https://api.kanutus.com/v1/webhook-endpoints", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KANUTUS_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.request(
"GET",
"https://api.kanutus.com/v1/webhook-endpoints",
headers={"Authorization": f"Bearer {os.environ['KANUTUS_API_KEY']}"},
)
print(r.status_code, r.json())Try it
Runs from your browser against the real API. Use a kt_test_ key: nothing is charged. The key stays in this tab only.
GET /v1/webhook-endpointsResponses by status
200 Endpoints of this environment
{
"data": [
{
"id": "string",
"url": "string",
"events": [
"string"
],
"status": "active",
"livemode": true,
"created_at": "string"
}
]
}400 Invalid request
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 400,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}401 Missing, invalid, expired or revoked credential
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 401,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}403 Missing permission, blocked or not allowed
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 403,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}429 Rate limit exceeded (see RateLimit-* and Retry-After headers)
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 429,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}POST/v1/webhook-endpoints
Create a webhook endpoint (the signing secret is returned once)
Request body
| Name | Type | Description |
|---|---|---|
url required | string | length 0–2000 |
events required | "room.started" | "room.ended" | "room.participant_joined" | "room.participant_left" | "session.started" | "session.ended" | "call.ringing" | "call.answered" | "call.voicemail" | "call.completed" | "call.failed" | "call.blocked" | "call.ended" | "meeting.created" | "meeting.updated" | "meeting.canceled" | "booking.created" | "booking.rescheduled" | "booking.canceled" | "transcript.ready" | "summary.ready" | "voice.consent_completed" | "voice.ready" | "voice.failed" | "usage.threshold" | "credential.expiring"[] | |
description | string | length 0–200 |
Response 201
| Name | Type | Description |
|---|---|---|
id required | string | |
url required | string | |
events required | string[] | |
status required | "active" | "disabled" | |
livemode required | boolean | |
created_at required | string | |
secret required | string | whsec_… shown only now |
Example
curl -X POST "https://api.kanutus.com/v1/webhook-endpoints" \
-H "Authorization: Bearer $KANUTUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","events":["room.started"],"description":"string"}'const res = await fetch("https://api.kanutus.com/v1/webhook-endpoints", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KANUTUS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"url":"https://example.com","events":["room.started"],"description":"string"}),
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.request(
"POST",
"https://api.kanutus.com/v1/webhook-endpoints",
headers={"Authorization": f"Bearer {os.environ['KANUTUS_API_KEY']}"},
json={"url":"https://example.com","events":["room.started"],"description":"string"},
)
print(r.status_code, r.json())Try it
Runs from your browser against the real API. Use a kt_test_ key: nothing is charged. The key stays in this tab only.
POST /v1/webhook-endpointsResponses by status
201 Created
{
"id": "string",
"url": "string",
"events": [
"string"
],
"status": "active",
"livemode": true,
"created_at": "string",
"secret": "string"
}400 Invalid request
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 400,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}401 Missing, invalid, expired or revoked credential
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 401,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}403 Missing permission, blocked or not allowed
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 403,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}429 Rate limit exceeded (see RateLimit-* and Retry-After headers)
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 429,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}DELETE/v1/webhook-endpoints/{id}
Delete a webhook endpoint
Parameters
| Name | Type | Description |
|---|---|---|
id required | path · string |
Response 200
| Name | Type | Description |
|---|---|---|
id required | string | |
deleted required | true |
Example
curl -X DELETE "https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10" \
-H "Authorization: Bearer $KANUTUS_API_KEY"const res = await fetch("https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.KANUTUS_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.request(
"DELETE",
"https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10",
headers={"Authorization": f"Bearer {os.environ['KANUTUS_API_KEY']}"},
)
print(r.status_code, r.json())Try it
Runs from your browser against the real API. Use a kt_test_ key: nothing is charged. The key stays in this tab only.
DELETE /v1/webhook-endpoints/{id}Responses by status
200 Deleted
{
"id": "string",
"deleted": true
}400 Invalid request
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 400,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}401 Missing, invalid, expired or revoked credential
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 401,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}403 Missing permission, blocked or not allowed
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 403,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}404 Not found
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 404,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}429 Rate limit exceeded (see RateLimit-* and Retry-After headers)
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 429,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}POST/v1/webhook-endpoints/{id}/rotate-secret
Rotate the signing secret (both secrets sign for 24 h)
Parameters
| Name | Type | Description |
|---|---|---|
id required | path · string |
Response 200
| Name | Type | Description |
|---|---|---|
id required | string | |
secret required | string |
Example
curl -X POST "https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10/rotate-secret" \
-H "Authorization: Bearer $KANUTUS_API_KEY"const res = await fetch("https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10/rotate-secret", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KANUTUS_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.request(
"POST",
"https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10/rotate-secret",
headers={"Authorization": f"Bearer {os.environ['KANUTUS_API_KEY']}"},
)
print(r.status_code, r.json())Try it
Runs from your browser against the real API. Use a kt_test_ key: nothing is charged. The key stays in this tab only.
POST /v1/webhook-endpoints/{id}/rotate-secretResponses by status
200 New secret, shown once
{
"id": "string",
"secret": "string"
}400 Invalid request
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 400,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}401 Missing, invalid, expired or revoked credential
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 401,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}403 Missing permission, blocked or not allowed
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 403,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}404 Not found
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 404,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}429 Rate limit exceeded (see RateLimit-* and Retry-After headers)
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 429,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}POST/v1/webhook-endpoints/{id}/test
Send a signed test event now
Parameters
| Name | Type | Description |
|---|---|---|
id required | path · string |
Request body
| Name | Type | Description |
|---|---|---|
type | "room.started" | "room.ended" | "room.participant_joined" | "room.participant_left" | "session.started" | "session.ended" | "call.ringing" | "call.answered" | "call.voicemail" | "call.completed" | "call.failed" | "call.blocked" | "call.ended" | "meeting.created" | "meeting.updated" | "meeting.canceled" | "booking.created" | "booking.rescheduled" | "booking.canceled" | "transcript.ready" | "summary.ready" | "voice.consent_completed" | "voice.ready" | "voice.failed" | "usage.threshold" | "credential.expiring" | default "meeting.created" |
Response 200
| Name | Type | Description |
|---|---|---|
ok required | boolean | |
status required | number | null | |
latency_ms required | number | |
delivery_id required | string | |
error required | string | null |
Example
curl -X POST "https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10/test" \
-H "Authorization: Bearer $KANUTUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"meeting.created"}'const res = await fetch("https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KANUTUS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"type":"meeting.created"}),
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.request(
"POST",
"https://api.kanutus.com/v1/webhook-endpoints/8f14e45f-ceea-4c8e-9b3a-2f1d5a7c3b10/test",
headers={"Authorization": f"Bearer {os.environ['KANUTUS_API_KEY']}"},
json={"type":"meeting.created"},
)
print(r.status_code, r.json())Try it
Runs from your browser against the real API. Use a kt_test_ key: nothing is charged. The key stays in this tab only.
POST /v1/webhook-endpoints/{id}/testResponses by status
200 Delivery result
{
"ok": true,
"status": 0,
"latency_ms": 0,
"delivery_id": "string",
"error": "string"
}400 Invalid request
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 400,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}401 Missing, invalid, expired or revoked credential
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 401,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}403 Missing permission, blocked or not allowed
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 403,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}404 Not found
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 404,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}429 Rate limit exceeded (see RateLimit-* and Retry-After headers)
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 429,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}GET/v1/events
Recent events (to reconcile what a webhook missed; 30 days)
Parameters
| Name | Type | Description |
|---|---|---|
type | query · string | |
since | query · string | |
limit | query · integer |
Response 200
| Name | Type | Description |
|---|---|---|
data required | object[] | |
data[].id required | string | |
data[].type required | string | |
data[].created required | integer | |
data[].livemode required | boolean | |
data[].data required | object | |
data[].data.object required | object |
Example
curl "https://api.kanutus.com/v1/events" \
-H "Authorization: Bearer $KANUTUS_API_KEY"const res = await fetch("https://api.kanutus.com/v1/events", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KANUTUS_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.request(
"GET",
"https://api.kanutus.com/v1/events",
headers={"Authorization": f"Bearer {os.environ['KANUTUS_API_KEY']}"},
)
print(r.status_code, r.json())Try it
Runs from your browser against the real API. Use a kt_test_ key: nothing is charged. The key stays in this tab only.
GET /v1/eventsResponses by status
200 Events, newest first
{
"data": [
{
"id": "string",
"type": "string",
"created": 0,
"livemode": true,
"data": {
"object": {}
}
}
]
}400 Invalid request
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 400,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}401 Missing, invalid, expired or revoked credential
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 401,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}403 Missing permission, blocked or not allowed
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 403,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}429 Rate limit exceeded (see RateLimit-* and Retry-After headers)
{
"type": "https://docs.kanutus.com/errors",
"title": "<code>",
"status": 429,
"code": "<code>",
"message": "…",
"request_id": "req_…"
}