The Webhook system allows external services to receive real-time updates whenever specific events occur within the Kivly platform. This enables seamless automation and synchronization across systems without the need for polling.
Webhook Documentation - Kivly Backend
General Description
Kivly's webhook system allows you to receive real-time notifications when specific events occur in your application. This allows you to keep your external systems synchronized with Kivly data.
Webhook Configuration
To configure webhooks, you must set the webhook_url
and webhook_secret
fields in your user profile.
Configuration Fields
Field | Type | Required | Description |
---|---|---|---|
webhook_url | string | Yes | URL where webhooks will be sent |
webhook_secret | string | Yes | Secret to verify authenticity |
Configuration Example via GraphQL
mutation {
userMutation {
updateUser(inputData: {
webhookUrl: "https://your-domain.com/webhooks/kivly"
webhookSecret: "your_super_secure_secret_here"
}) {
success
message
}
}
}
Available Events
Customer Events
customer.created
- Customer createdcustomer.updated
- Customer updatedcustomer.deleted
- Customer deleted
Establishment Events
establishment.created
- Establishment createdestablishment.updated
- Establishment updatedestablishment.deleted
- Establishment deleted
Point Transaction Events
point_transaction.created
- Point transaction createdpoint_transaction.deleted
- Point transaction deleted
Redemption Events
redemption.created
- Redemption createdredemption.redeemed
- Redemption redeemedredemption.cancelled
- Redemption cancelledredemption.failed
- Redemption failed
Reward Events
reward.created
- Reward createdreward.updated
- Reward updatedreward.deleted
- Reward deletedreward.expired
- Reward expired
Payload Structure
Headers
Content-Type: application/json
User-Agent: Kivly-Webhook/1.0
X-Kivly-Event: customer.created
X-Kivly-Signature: sha256=5d41402abc4b2a76b9719d911017c592
X-Kivly-Timestamp: 1640995200
Body
{
"id": "unique-event-id",
"event": "customer.created",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"id": "customer_id",
"name": "Juan Pérez",
"email": "[email protected]",
"phone": "+34600123456",
"owner": "user_id",
"establishment": "establishment_id",
"loyalty_points": 0,
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
"owner_id": "user_id",
"establishment_id": "establishment_id",
"customer_id": "customer_id",
"metadata": {
"entity": "customer",
"action": "created"
}
}
Signature Verification
To verify that the webhook comes from Kivly, you must validate the HMAC-SHA256 signature:
Python Example
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
expected_signature = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
expected_signature = f"sha256={expected_signature}"
return hmac.compare_digest(expected_signature, signature)
# Usage
payload = request.body # Raw JSON string
signature = request.headers.get('X-Kivly-Signature')
secret = 'your_super_secure_secret_here'
if verify_webhook_signature(payload, signature, secret):
# Valid webhook
process_webhook(payload)
else:
# Invalid webhook
return 401
Node.js Example
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
const expectedSignatureWithPrefix = `sha256=${expectedSignature}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignatureWithPrefix)
);
}
API Endpoints
Get Available Events
GET /api/v1/webhooks/events
Authorization: Bearer {token}
Response:
{
"events": {
"customer": [
"customer.created",
"customer.updated",
"customer.deleted"
],
"establishment": [
"establishment.created",
"establishment.updated",
"establishment.deleted"
],
"point_transaction": [
"point_transaction.created",
"point_transaction.deleted"
],
"redemption": [
"redemption.created",
"redemption.redeemed",
"redemption.cancelled",
"redemption.failed"
],
"reward": [
"reward.created",
"reward.updated",
"reward.deleted",
"reward.expired"
]
}
}
Test Webhook
POST /api/v1/webhooks/test
Authorization: Bearer {token}
Content-Type: application/json
{
"url": "https://your-domain.com/webhooks/kivly",
"secret": "your_super_secure_secret_here"
}
Response:
{
"success": true,
"message": "Webhook configured correctly. Test event sent successfully."
}
GraphQL Queries
Get Available Events
query {
webhookQuery {
webhookEvents {
entity
events
}
}
}
Get Current Configuration
query {
webhookQuery {
webhookConfig {
webhookUrl
webhookSecret
hasWebhookConfigured
}
}
}
Test Webhook
mutation {
webhookMutation {
testWebhook(webhookInput: {
url: "https://your-domain.com/webhooks/kivly"
secret: "your_super_secure_secret_here"
}) {
success
message
}
}
}
Event Payload Examples
Customer Created
{
"id": "evt_12345",
"event": "customer.created",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"id": "cust_67890",
"name": "María García",
"email": "[email protected]",
"phone": "+34600987654",
"owner": "user_123",
"establishment": "est_456",
"loyalty_points": 0,
"is_active": true,
"created_at": "2024-01-15T10:30:00Z"
},
"owner_id": "user_123",
"establishment_id": "est_456",
"customer_id": "cust_67890"
}
Point Transaction Created
{
"id": "evt_54321",
"event": "point_transaction.created",
"timestamp": "2024-01-15T11:00:00Z",
"data": {
"id": "tx_98765",
"customer_id": "cust_67890",
"amount": 100,
"type": "earn",
"point_type": "point",
"description": "Store purchase",
"owner": "user_123",
"created_at": "2024-01-15T11:00:00Z"
},
"owner_id": "user_123",
"establishment_id": "est_456",
"customer_id": "cust_67890"
}
Redemption Redeemed
{
"id": "evt_11111",
"event": "redemption.redeemed",
"timestamp": "2024-01-15T12:00:00Z",
"data": {
"id": "red_22222",
"customer_id": "cust_67890",
"reward_id": "rew_33333",
"points_required": 500,
"status": "redeemed",
"owner": "user_123",
"redeemed_at": "2024-01-15T12:00:00Z"
},
"owner_id": "user_123",
"establishment_id": "est_456",
"customer_id": "cust_67890"
}
Error Handling and Retries
- Webhooks will be automatically retried up to 3 times
- The interval between retries is 60 seconds
- Any response with HTTP 2xx status code is considered successful
- Webhooks fail permanently after 3 attempts
Timeout
- Default timeout: 30 seconds
- If your endpoint doesn't respond within 30 seconds, it will be considered failed
Best Practices
- Always verify signature: Never process webhooks without verifying the HMAC signature
- Idempotency: Process each event only once using the
id
field - Quick response: Respond with 200 OK as soon as possible
- Asynchronous processing: If you need heavy processing, do it in the background
- Logs: Keep detailed logs of all received webhooks
- Error handling: Implement robust error handling
Complete Implementation Example
Flask (Python)
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
app = Flask(__name__)
WEBHOOK_SECRET = 'your_super_secure_secret_here'
def verify_signature(payload, signature):
expected = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@app.route('/webhooks/kivly', methods=['POST'])
def handle_webhook():
payload = request.get_data()
signature = request.headers.get('X-Kivly-Signature')
if not verify_signature(payload, signature):
return jsonify({'error': 'Invalid signature'}), 401
event_data = request.get_json()
event_type = event_data['event']
# Process event
if event_type == 'customer.created':
handle_customer_created(event_data)
elif event_type == 'point_transaction.created':
handle_point_transaction_created(event_data)
# ... more events
return jsonify({'status': 'success'}), 200
def handle_customer_created(event_data):
customer = event_data['data']
print(f"New customer: {customer['name']} ({customer['email']})")
# Your logic here
if __name__ == '__main__':
app.run(port=5000)
Express.js (Node.js)
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = 'your_super_secure_secret_here';
app.use('/webhooks/kivly', express.raw({type: 'application/json'}));
function verifySignature(payload, signature) {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
app.post('/webhooks/kivly', (req, res) => {
const payload = req.body;
const signature = req.headers['x-kivly-signature'];
if (!verifySignature(payload, signature)) {
return res.status(401).json({error: 'Invalid signature'});
}
const eventData = JSON.parse(payload);
const eventType = eventData.event;
// Process event
switch(eventType) {
case 'customer.created':
handleCustomerCreated(eventData);
break;
case 'point_transaction.created':
handlePointTransactionCreated(eventData);
break;
// ... more events
}
res.json({status: 'success'});
});
function handleCustomerCreated(eventData) {
const customer = eventData.data;
console.log(`New customer: ${customer.name} (${customer.email})`);
// Your logic here
}
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
OpenAPI Specification
openapi: 3.0.0
info:
title: Kivly Webhooks API
description: API to configure and test webhooks in Kivly
version: 1.0.0
servers:
- url: https://api.kivly.com/api/v1
description: Production server
paths:
/webhooks/events:
get:
summary: Get available events
description: Returns all available webhook events organized by entity
security:
- bearerAuth: []
responses:
'200':
description: List of available events
content:
application/json:
schema:
type: object
properties:
events:
type: object
additionalProperties:
type: array
items:
type: string
example:
events:
customer: ["customer.created", "customer.updated", "customer.deleted"]
establishment: ["establishment.created", "establishment.updated", "establishment.deleted"]
/webhooks/test:
post:
summary: Test webhook configuration
description: Sends a test event to the configured webhook
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- url
- secret
properties:
url:
type: string
format: uri
description: Webhook URL to test
secret:
type: string
description: Secret to verify signature
example:
url: "https://your-domain.com/webhooks/kivly"
secret: "your_super_secure_secret_here"
responses:
'200':
description: Test result
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
message:
type: string
example:
success: true
message: "Webhook configured correctly. Test event sent successfully."
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
WebhookPayload:
type: object
properties:
id:
type: string
description: Unique event ID
event:
type: string
description: Event type
enum:
- customer.created
- customer.updated
- customer.deleted
- establishment.created
- establishment.updated
- establishment.deleted
- point_transaction.created
- point_transaction.updated
- point_transaction.deleted
- redemption.created
- redemption.redeemed
- redemption.cancelled
- redemption.failed
- reward.created
- reward.updated
- reward.deleted
- reward.expired
timestamp:
type: string
format: date-time
description: Event timestamp
data:
type: object
description: Event data (varies by type)
owner_id:
type: string
description: Owner ID
establishment_id:
type: string
description: Establishment ID (optional)
nullable: true
customer_id:
type: string
description: Customer ID (optional)
nullable: true
metadata:
type: object
description: Additional metadata
nullable: true
required:
- id
- event
- timestamp
- data
- owner_id