Webhooks
Receive real-time notifications when events happen in your workspace. Configure webhook endpoints to receive HTTP POST requests whenever a ticket is created, a message is sent, an SLA is breached, or any other supported event occurs. Webhooks let you integrate Keme with your own infrastructure without polling the API.
Each delivery is an HTTP POST to your configured URL containing a signed JSON payload. Keme includes an x-keme-signature header on every request so you can verify that the delivery originated from Keme and has not been tampered with in transit.
Events
The following event types are available for subscription. Subscribe to one or more events when creating a webhook endpoint. Wildcard subscriptions (*) are not supported — you must enumerate the specific events you want to receive.
| Event Type | Trigger |
|---|---|
| ticket.created | A new support ticket is created in your workspace. |
| ticket.assigned | A ticket is assigned or reassigned to an agent. |
| ticket.resolved | A ticket status is changed to resolved. |
| ticket.closed | A ticket is closed, either manually or by automation. |
| message.created | A new message is added to a ticket by a player or agent. |
| player.created | A new player profile is registered in your workspace. |
| sla.breached | A ticket has exceeded its SLA response or resolution deadline. |
Webhook Payload
Every webhook delivery uses a standard envelope regardless of the event type. The data object contains the resource-specific fields for the event. The id field is unique per delivery and can be used for deduplication in your handler.
1{2 "id": "evt_xxxxxxxxxxxxxxxxxxxx",3 "event": "ticket.created",4 "workspaceId": "ws_xxxxxxxxxxxx",5 "timestamp": "2026-06-15T10: 23: 45Z",6 "data": {7 "ticketId": "tkt_xxxxxxxxxxxx",8 "title": "Purchase failed",9 "status": "open",10 "priority": "high",11 "playerId": "player_12345"12 }13}
data varies by event type. For ticket.* events it contains the ticket object; for message.created it contains the message object; for player.created it contains the player profile; and for sla.breached it includes the ticket ID, breached policy name, and breach timestamp.Signature Verification
All webhook deliveries include an x-keme-signature header containing an HMAC-SHA256 hex digest of the raw request body, signed with your webhook's shared secret. You must verify this signature before processing any event to protect your endpoint from spoofed requests.
The shared secret is returned once when you create the webhook endpoint (in the secret field). Store it securely — it is not retrievable again via the API. If you lose it, rotate by deleting and recreating the webhook.
crypto.timingSafeEqual) when comparing signatures. Standard string equality is vulnerable to timing attacks and must not be used.1const crypto = require('crypto');23function verifyWebhookSignature(payload, signature, secret) {4 const expected = crypto5 .createHmac('sha256', secret)6 .update(payload, 'utf8')7 .digest('hex');8 return crypto.timingSafeEqual(9 Buffer.from(signature),10 Buffer.from(expected)11 );12}1314// Express.js example15app.post('/webhooks/keme', express.raw({ type: 'application/json' }), (req, res) => {16 const sig = req.headers['x-keme-signature'];17 if (!verifyWebhookSignature(req.body, sig, process.env.KEME_WEBHOOK_SECRET)) {18 return res.status(401).send('Invalid signature');19 }20 const event = JSON.parse(req.body);21 // Handle event...22 res.status(200).send('OK');23});
express.raw() (or the equivalent raw body parser in your framework) rather than express.json() when reading the request body for signature verification. Parsing the body as JSON before verifying will modify the byte representation and cause all signature checks to fail.Create Webhook Endpoint
/v1/webhooks🔒 Auth requiredRegisters a new webhook endpoint for your workspace. The response includes a secret field containing the HMAC signing key. This secret is shown only once — store it securely immediately after creation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| workspaceId | string | Required | The workspace this webhook belongs to. All events will be scoped to this workspace. |
| url | string | Required | The HTTPS URL that Keme will POST events to. Must be publicly reachable and use HTTPS. |
| events | string[] | Required | List of event types to receive (e.g. ["ticket.created", "sla.breached"]). At least one event is required. |
| description | string | Optional | Human-readable label for this endpoint. Useful when managing multiple webhooks in the same workspace. |
| active | boolean | Optional | Whether the endpoint should receive events immediately. Defaults to true. Set false to register the endpoint in a paused state. |
1{2 "id": "wh_xxxx",3 "workspaceId": "ws_xxxxxxxxxxxx",4 "url": "https://yourserver.com/webhooks",5 "events": ["ticket.created", "ticket.resolved"],6 "description": "Production support events",7 "active": true,8 "secret": "whsec_xxxx",9 "createdAt": "2026-06-15T10: 23: 45Z"10}
List Webhooks
/v1/webhooks🔒 Auth requiredReturns all webhook endpoints registered for the specified workspace. The secret field is omitted from list responses — it is only returned at creation time.
| Parameter | Type | Required | Description |
|---|---|---|---|
| workspaceId | string | Required | The workspace whose webhook endpoints you want to retrieve. |
1{2 "total": 2,3 "data": [4 {5 "id": "wh_xxxx",6 "workspaceId": "ws_xxxxxxxxxxxx",7 "url": "https://yourserver.com/webhooks",8 "events": ["ticket.created", "ticket.resolved"],9 "description": "Production support events",10 "active": true,11 "createdAt": "2026-06-15T10: 23: 45Z"12 },13 {14 "id": "wh_yyyy",15 "workspaceId": "ws_xxxxxxxxxxxx",16 "url": "https://staging.yourserver.com/webhooks",17 "events": ["ticket.created", "message.created", "sla.breached"],18 "description": "Staging — all ticket and SLA events",19 "active": false,20 "createdAt": "2026-06-14T08: 00: 00Z"21 }22 ]23}
Delete Webhook
/v1/webhooks/:id🔒 Auth requiredPermanently removes a webhook endpoint. No further events will be delivered to the associated URL. Returns 204 No Content on success. In-flight deliveries that are already queued may still arrive at your endpoint shortly after deletion.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The unique identifier of the webhook endpoint to delete (e.g. wh_xxxx). |
Retry Policy
If your endpoint returns a non-2xx HTTP status code, times out (30-second limit), or is unreachable, Keme will retry the delivery using exponential backoff. The retry schedule is:
- Attempt 1: Immediately after the initial failure.
- Attempt 2: 5 minutes after attempt 1.
- Attempt 3: 30 minutes after attempt 2.
- Attempt 4: 2 hours after attempt 3.
- Attempt 5: 8 hours after attempt 4.
After 5 failed attempts the delivery is marked as permanently failed. The total retry window is approximately 24 hours. Failed and succeeded deliveries are retained in the delivery log for 7 days and are viewable from your workspace settings.
Testing
Keme provides two tools to help you develop and validate your webhook handler before going live.
Test mode: Enable test mode on any webhook endpoint from Workspace Settings → Integrations → Webhooks. In test mode, Keme sends synthetic event payloads to your endpoint using real envelope structure and valid HMAC signatures, but no live workspace data is involved. This is safe to use against a local development server.
Event simulator: The event simulator lets you manually fire a specific event type to a registered endpoint on demand. Navigate to your webhook endpoint in workspace settings and click Send test event. Select the event type, optionally customise the payload fields, and click Send. The delivery result and response code are shown immediately in the UI.
ngrok or cloudflared tunnel to expose your local server via a public HTTPS URL. Keme requires HTTPS for all webhook endpoints — plain HTTP URLs will be rejected at registration time.