Webhooks are available on the Business and Scale plans. Business workspaces can register up to 10 endpoints, Scale up to 30. Starter and Growth plans can’t create webhook endpoints.
Create an endpoint
Fill in the endpoint
- Label — a name so it’s easy to identify (
prod-orders-pipeline,slack-relay, …). - URL — the
https://URL that should receive POSTs. Plain HTTP is rejected. - Events — pick the events you want this endpoint to receive.
Copy the signing secret
On creation Keloa shows a signing secret — format
whsec_ followed by 40 random characters. It is shown exactly once. Copy it into your receiver immediately; you’ll need it to verify each request.Lost the secret? You can reveal it again or rotate it later from the endpoint’s actions menu. Rotating invalidates the old secret — update your receiver before deliveries start failing.
Events
Keloa delivers five conversation lifecycle events:| Event | Fires when |
|---|---|
conversation.opened | A new conversation is created. |
conversation.solved | A conversation is marked solved. |
conversation.closed | A conversation is closed. |
conversation.escalated | A conversation is escalated to a human. Carries escalation fields. |
conversation.rated | A customer submits a CSAT rating. Carries CSAT fields. |
The payload
Every delivery is a JSON object with a stable top-level envelope (id, type, created_at, account_id, data). The data object holds the conversation the event is about.
Event-specific fields
Two events add extra fields insidedata.conversation:
conversation.escalated
| Field | Description |
|---|---|
escalated_to_id | UUID of the human the conversation was escalated to. |
escalation_reason | Why the conversation was escalated. |
escalated_at | Timestamp of the escalation. |
conversation.rated
| Field | Description |
|---|---|
csat_score | The CSAT rating (integer). |
csat_comment | The optional comment the customer left. |
rated_at | Timestamp of the rating. |
Request headers
Keloa sends these headers on every delivery:| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | Keloa-Webhooks/1 |
X-Keloa-Event | The event type, e.g. conversation.solved. |
X-Keloa-Delivery-Id | A per-event UUID. Use this as your idempotency key — retries reuse the same value. |
X-Keloa-Webhook-Id | The id of the endpoint the delivery was sent to. |
X-Keloa-Signature | t=<unix>,v1=<hmac> — see Verifying the signature. |
Verifying the signature
TheX-Keloa-Signature header looks like this:
t— the Unix timestamp (seconds) when the delivery was signed.v1— anHMAC-SHA256hash, lowercase hex, keyed with the endpoint’s signing secret.
- Recompute the HMAC over
"<t>.<body>"using the signing secret and compare it withv1using a constant-time comparison function. - Reject the request if
tis more than ~5 minutes (300 seconds) old — this is your replay defence.
Idempotency
Delivery is at-least-once — a retry can re-deliver an event you’ve already processed. Every delivery of the same event carries the sameX-Keloa-Delivery-Id, so dedupe on that header: record the IDs you’ve handled and skip a delivery whose ID you’ve already seen.
Retries & auto-disable
Delivery is queued. A non-2xx response or a timeout (10 seconds) is retried up to 5 attempts with exponential backoff:| Attempt | Delay after previous |
|---|---|
| 1 | ≈1 minute |
| 2 | ≈5 minutes |
| 3 | ≈30 minutes |
| 4 | ≈2 hours |
| 5 | ≈6 hours |
If an endpoint’s URL becomes unsafe or unresolvable, Keloa’s delivery-time security re-check fails that delivery without retrying it.
Security
- Endpoint URLs must be
https://. Plain HTTP is rejected. - Private, loopback, link-local, and reserved IP addresses are rejected at creation and re-checked at delivery time, so an endpoint can’t be repointed at internal infrastructure after the fact.
- Always verify the signature on every payload — never trust an unsigned or unverifiable POST.
Delivery log
Each endpoint keeps a delivery log in the UI showing the most recent deliveries — event type, status, HTTP response code, and attempt count. Use it to spot a silent or failing receiver. Records are retained for 30 days.Best practices
- Respond fast. Acknowledge with a
2xximmediately, then process the payload asynchronously. Don’t run heavy work inside the request — you have 10 seconds before a delivery is counted as failed. - Verify every payload. Reject anything that fails signature or timestamp checks.
- Idempotent handlers. Dedupe on
X-Keloa-Delivery-Id. - One endpoint per consumer. A separate endpoint per system is easier to monitor, pause, and rotate independently.
Related
Developers overview
The full planned API surface.
API tokens
Auth for the planned API.