Skip to main content

Webhooks

Webhooks allow you to receive real-time notifications when events occur in ClawBook. Instead of polling the API, configure a webhook URL and ClawBook will send HTTP POST requests to your server.

Setting Up Webhooks​

Via Dashboard​

  1. Go to Settings → API → Webhooks
  2. Click Add Webhook
  3. Enter your endpoint URL
  4. Select events to subscribe to
  5. Click Create

Via API​

curl -X POST "https://your-domain.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"events": ["message.received", "message.sent", "integration.disconnected"],
"secret": "your-webhook-secret"
}'

Webhook Events​

Message Events​

EventDescription
message.receivedNew inbound message received
message.sentOutbound message sent
message.deliveredMessage delivery confirmed
message.failedMessage delivery failed

Integration Events​

EventDescription
integration.connectedPlatform connected
integration.disconnectedPlatform disconnected
integration.errorIntegration error occurred

AI Events​

EventDescription
ai.requestAI request sent
ai.responseAI response received
ai.errorAI provider error
ai.rate_limitedRate limit hit

System Events​

EventDescription
system.healthHealth status change
backup.completedBackup finished
backup.failedBackup failed

Webhook Payload​

All webhooks follow this structure:

{
"id": "evt_abc123",
"type": "message.received",
"created_at": "2026-01-30T14:30:00Z",
"data": {
// Event-specific data
}
}

Example: message.received​

{
"id": "evt_abc123",
"type": "message.received",
"created_at": "2026-01-30T14:30:00Z",
"data": {
"message_id": "msg_xyz789",
"conversation_id": "conv_abc456",
"platform": "telegram",
"sender": {
"id": "123456789",
"name": "John Doe",
"username": "@johndoe"
},
"content": "Hello, how are you?",
"timestamp": "2026-01-30T14:29:58Z"
}
}

Example: message.sent​

{
"id": "evt_def456",
"type": "message.sent",
"created_at": "2026-01-30T14:30:02Z",
"data": {
"message_id": "msg_response123",
"conversation_id": "conv_abc456",
"platform": "telegram",
"recipient": {
"id": "123456789",
"name": "John Doe"
},
"content": "I'm doing well, thank you for asking!",
"ai_model": "claude-3-5-sonnet",
"tokens": {
"input": 45,
"output": 15
},
"latency_ms": 1250
}
}

Example: integration.disconnected​

{
"id": "evt_ghi789",
"type": "integration.disconnected",
"created_at": "2026-01-30T14:35:00Z",
"data": {
"platform": "whatsapp",
"reason": "Phone went offline",
"last_connected_at": "2026-01-30T12:00:00Z",
"auto_reconnect": true
}
}

Webhook Security​

Verifying Signatures​

All webhooks include a signature header for verification:

X-ClawBook-Signature: sha256=abc123...

Verify in your code:

Node.js:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return `sha256=${expected}` === signature;
}

// In your handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-clawbook-signature'];
const payload = JSON.stringify(req.body);

if (!verifySignature(payload, signature, 'your-webhook-secret')) {
return res.status(401).send('Invalid signature');
}

// Process webhook
console.log('Event:', req.body.type);
res.status(200).send('OK');
});

Python:

import hmac
import hashlib

def verify_signature(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return f"sha256={expected}" == signature

# In your handler
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-ClawBook-Signature')
payload = request.get_data(as_text=True)

if not verify_signature(payload, signature, 'your-webhook-secret'):
return 'Invalid signature', 401

event = request.json
print(f"Event: {event['type']}")
return 'OK', 200

PHP:

function verifySignature($payload, $signature, $secret) {
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}

// In your handler
$signature = $_SERVER['HTTP_X_CLAWBOOK_SIGNATURE'];
$payload = file_get_contents('php://input');

if (!verifySignature($payload, $signature, 'your-webhook-secret')) {
http_response_code(401);
exit('Invalid signature');
}

$event = json_decode($payload, true);
error_log("Event: " . $event['type']);
http_response_code(200);

IP Allowlisting​

ClawBook webhooks come from these IP ranges:

203.0.113.0/24
198.51.100.0/24

Configure your firewall to only accept webhook requests from these IPs.

Webhook Delivery​

Retry Policy​

Failed webhooks are retried with exponential backoff:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours

After 5 failures, the webhook is marked as failed and you'll receive an email notification.

Expected Response​

Your endpoint should:

  • Return 200 OK within 30 seconds
  • Return 2xx status codes for success
  • Return 4xx for permanent failures (won't retry)
  • Return 5xx for temporary failures (will retry)

Timeout​

Webhooks timeout after 30 seconds. For long-running tasks:

  1. Return 200 OK immediately
  2. Process asynchronously
  3. Use the API to acknowledge later if needed

Managing Webhooks​

List Webhooks​

curl "https://your-domain.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY"

Get Webhook Details​

curl "https://your-domain.com/api/v1/webhooks/wh_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"

Response includes recent delivery attempts:

{
"id": "wh_abc123",
"url": "https://your-server.com/webhook",
"events": ["message.received", "message.sent"],
"status": "active",
"recent_deliveries": [
{
"id": "del_xyz789",
"event_id": "evt_abc123",
"status": "success",
"response_code": 200,
"response_time_ms": 150,
"timestamp": "2026-01-30T14:30:00Z"
}
]
}

Update Webhook​

curl -X PATCH "https://your-domain.com/api/v1/webhooks/wh_abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["message.received"],
"enabled": true
}'

Delete Webhook​

curl -X DELETE "https://your-domain.com/api/v1/webhooks/wh_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"

Test Webhook​

Send a test event to your endpoint:

curl -X POST "https://your-domain.com/api/v1/webhooks/wh_abc123/test" \
-H "Authorization: Bearer YOUR_API_KEY"

Webhook Logs​

View delivery logs in dashboard:

Settings → API → Webhooks → Select webhook → Delivery Logs

Or via API:

curl "https://your-domain.com/api/v1/webhooks/wh_abc123/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY"

Best Practices​

  1. Always verify signatures - Prevent spoofed requests
  2. Respond quickly - Return 200 before processing
  3. Handle duplicates - Use event IDs for idempotency
  4. Monitor delivery - Check for failed deliveries
  5. Use HTTPS - Secure your endpoint
  6. Implement retry logic - In case you miss events

Example: Full Webhook Handler​

Node.js/Express:

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.CLAWBOOK_WEBHOOK_SECRET;
const processedEvents = new Set();

function verifySignature(payload, signature) {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
return `sha256=${expected}` === signature;
}

app.post('/webhook/clawbook', (req, res) => {
// Verify signature
const signature = req.headers['x-clawbook-signature'];
if (!verifySignature(JSON.stringify(req.body), signature)) {
return res.status(401).send('Invalid signature');
}

const event = req.body;

// Idempotency check
if (processedEvents.has(event.id)) {
return res.status(200).send('Already processed');
}
processedEvents.add(event.id);

// Return immediately
res.status(200).send('OK');

// Process asynchronously
processEvent(event).catch(console.error);
});

async function processEvent(event) {
switch (event.type) {
case 'message.received':
console.log('New message from:', event.data.sender.name);
// Your logic here
break;

case 'integration.disconnected':
console.log('Platform disconnected:', event.data.platform);
// Send alert
break;

default:
console.log('Unhandled event:', event.type);
}
}

app.listen(3000);

Next Steps​