Webhooks
Receive real-time event notifications from Keva
Webhooks allow your application to receive real-time notifications when events occur in Keva. Configure webhook endpoints to integrate Keva with your own systems.
Setting Up Webhooks
- Go to Settings > Webhooks in your dashboard
- Click Add Webhook Endpoint
- Enter your endpoint URL
- Select the events you want to receive
- Copy the signing secret for verification
Webhook Payload
All webhook requests are POST requests with a JSON body:
{
"id": "evt_abc123",
"type": "ticket.created",
"createdAt": "2024-01-15T14:30:00Z",
"data": {
"ticketId": "tkt_xyz789",
"subject": "Cannot access my account",
"customerEmail": "customer@example.com"
}
}Common Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique event ID |
type | string | Event type |
createdAt | string | ISO 8601 timestamp |
data | object | Event-specific payload |
Event Types
Ticket Events
ticket.created
Fired when a new ticket is created.
{
"type": "ticket.created",
"data": {
"ticketId": "tkt_abc123",
"subject": "Order not delivered",
"customerEmail": "customer@example.com",
"customerName": "John Doe",
"source": "email",
"priority": "medium"
}
}ticket.updated
Fired when a ticket is updated (status, priority, assignment).
{
"type": "ticket.updated",
"data": {
"ticketId": "tkt_abc123",
"changes": {
"status": { "from": "open", "to": "pending" },
"assignedTo": { "from": null, "to": "user_xyz" }
}
}
}ticket.closed
Fired when a ticket is closed or solved.
{
"type": "ticket.closed",
"data": {
"ticketId": "tkt_abc123",
"resolution": "solved",
"resolutionType": "ai_autonomous"
}
}Message Events
message.created
Fired when a new message is added to a ticket.
{
"type": "message.created",
"data": {
"messageId": "msg_abc123",
"ticketId": "tkt_xyz789",
"direction": "inbound",
"senderEmail": "customer@example.com",
"body": "Any update on my order?"
}
}Approval Events
approval.created
Fired when the AI creates an approval request.
{
"type": "approval.created",
"data": {
"approvalId": "apr_abc123",
"ticketId": "tkt_xyz789",
"actionType": "send_reply",
"expiresAt": "2024-01-16T14:30:00Z"
}
}approval.decided
Fired when an approval is approved or rejected.
{
"type": "approval.decided",
"data": {
"approvalId": "apr_abc123",
"decision": "approved",
"reviewerId": "user_abc",
"decidedAt": "2024-01-15T15:00:00Z"
}
}Customer Events
customer.created
Fired when a new customer profile is created.
{
"type": "customer.created",
"data": {
"customerId": "cust_abc123",
"email": "newcustomer@example.com",
"name": "Jane Smith"
}
}Signature Verification
All webhook requests include a signature header for verification:
X-Keva-Signature: sha256=abc123...Verifying the Signature
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Express example
app.post('/webhooks/keva', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-keva-signature'];
const payload = req.body.toString();
if (!verifyWebhook(payload, signature, process.env.KEVA_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
// Handle event...
res.status(200).send('OK');
});Retry Policy
Failed webhook deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the webhook is marked as failed and no further retries occur.
Best Practices
- Return 2xx quickly - Process webhooks asynchronously
- Handle duplicates - Use the event
idfor idempotency - Verify signatures - Always validate the
X-Keva-Signatureheader - Monitor failures - Check webhook delivery status in the dashboard
- Use HTTPS - Webhook URLs must use HTTPS
Testing Webhooks
Use the Test button in Settings > Webhooks to send a test event to your endpoint. Test events have test: true in the payload.