Skip to main content

Webhooks Guide

Integrate dakkio with external services using webhooks to receive real-time notifications about events in your buckets.

What are Webhooks?

Webhooks are HTTP callbacks that dakkio sends to your specified URL when events occur. They enable real-time integrations with:

  • Slack, Discord, Microsoft Teams
  • Email services (SendGrid, Mailgun)
  • SMS providers (Twilio, Vonage)
  • Custom applications
  • Automation platforms (Zapier, Make)

Supported Events

EventDescriptionWhen Triggered
alert.triggeredAlert rule condition metAlert fires
data.receivedNew data point ingestedData arrives
bucket.updatedBucket configuration changedBucket modified

Creating a Webhook

Via Dashboard

  1. Navigate to your bucket
  2. Go to "Webhooks" tab
  3. Click "Create Webhook"
  4. Enter details:
    • URL: Your webhook endpoint
    • Events: Select events to receive
    • Secret: (Optional) For signature verification
  5. Click "Create"

Via API

curl -X POST https://api.dakkio.io/api/webhooks \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bucketId": "507f1f77bcf86cd799439011",
"url": "https://your-server.com/webhook",
"events": ["alert.triggered", "data.received"],
"secret": "your_webhook_secret"
}'

Webhook Payload

Alert Triggered Event

{
"event": "alert.triggered",
"timestamp": "2024-01-15T10:30:00Z",
"webhookId": "507f1f77bcf86cd799439016",
"bucketId": "507f1f77bcf86cd799439011",
"data": {
"alertId": "507f1f77bcf86cd799439015",
"alertName": "High Temperature Warning",
"dataSourceId": "507f1f77bcf86cd799439012",
"dataSourceName": "Temperature Sensor - Room A",
"condition": "Temperature exceeds 30°C for 5 minutes",
"currentValue": 31.5,
"threshold": 30,
"operator": ">",
"triggeredAt": "2024-01-15T10:30:00Z",
"dataPoint": {
"timestamp": "2024-01-15T10:30:00Z",
"values": {
"temperature": 31.5,
"humidity": 65
},
"metadata": {
"deviceId": "ESP32-001",
"location": "Room A"
}
}
}
}

Data Received Event

{
"event": "data.received",
"timestamp": "2024-01-15T10:30:00Z",
"webhookId": "507f1f77bcf86cd799439016",
"bucketId": "507f1f77bcf86cd799439011",
"data": {
"dataSourceId": "507f1f77bcf86cd799439012",
"dataSourceName": "Temperature Sensor - Room A",
"dataPoint": {
"_id": "507f1f77bcf86cd799439017",
"timestamp": "2024-01-15T10:30:00Z",
"values": {
"temperature": 22.5,
"humidity": 65
},
"metadata": {
"deviceId": "ESP32-001"
}
}
}
}

Bucket Updated Event

{
"event": "bucket.updated",
"timestamp": "2024-01-15T10:30:00Z",
"webhookId": "507f1f77bcf86cd799439016",
"bucketId": "507f1f77bcf86cd799439011",
"data": {
"changes": {
"retentionDays": {
"old": 30,
"new": 365
}
},
"updatedBy": "507f1f77bcf86cd799439010",
"updatedAt": "2024-01-15T10:30:00Z"
}
}

Security

Signature Verification

dakkio signs webhook requests using HMAC-SHA256. Always verify signatures to prevent spoofing.

Header:

X-Webhook-Signature: sha256=abc123def456...

Verification Examples:

Node.js

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(JSON.stringify(payload)).digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}

// Express endpoint
app.post('/webhook', express.json(), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = process.env.WEBHOOK_SECRET;

if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}

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

Python

import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
expected = 'sha256=' + hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()

return hmac.compare_digest(signature, expected)

# Flask endpoint
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature')
secret = os.environ['WEBHOOK_SECRET']
payload = request.get_data(as_text=True)

if not verify_webhook_signature(payload, signature, secret):
return 'Invalid signature', 401

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

Integration Examples

Slack

Send alerts to Slack channel:

// Slack webhook URL from: https://api.slack.com/messaging/webhooks
const webhookUrl = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXX';

// Transform dakkio webhook to Slack message
app.post('/webhook', express.json(), (req, res) => {
const { event, data } = req.body;

if (event === 'alert.triggered') {
const message = {
text: `🚨 Alert: ${data.alertName}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*${data.alertName}*\n${data.condition}`
}
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*Current Value:*\n${data.currentValue}`
},
{
type: 'mrkdwn',
text: `*Threshold:*\n${data.threshold}`
},
{
type: 'mrkdwn',
text: `*Data Source:*\n${data.dataSourceName}`
},
{
type: 'mrkdwn',
text: `*Time:*\n${new Date(data.triggeredAt).toLocaleString()}`
}
]
}
]
};

axios.post(webhookUrl, message);
}

res.status(200).send('OK');
});

Discord

Send notifications to Discord:

// Discord webhook URL from: Server Settings > Integrations > Webhooks
const webhookUrl = 'https://discord.com/api/webhooks/123456/abcdef';

app.post('/webhook', express.json(), (req, res) => {
const { event, data } = req.body;

if (event === 'alert.triggered') {
const embed = {
embeds: [{
title: data.alertName,
description: data.condition,
color: 0xff0000, // Red
fields: [
{
name: 'Current Value',
value: data.currentValue.toString(),
inline: true
},
{
name: 'Threshold',
value: data.threshold.toString(),
inline: true
},
{
name: 'Data Source',
value: data.dataSourceName,
inline: false
}
],
timestamp: data.triggeredAt
}]
};

axios.post(webhookUrl, embed);
}

res.status(200).send('OK');
});

Email (SendGrid)

Send email notifications:

const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

app.post('/webhook', express.json(), async (req, res) => {
const { event, data } = req.body;

if (event === 'alert.triggered') {
const msg = {
to: 'alerts@your-company.com',
from: 'noreply@your-company.com',
subject: `Alert: ${data.alertName}`,
html: `
<h2>${data.alertName}</h2>
<p><strong>Condition:</strong> ${data.condition}</p>
<p><strong>Current Value:</strong> ${data.currentValue}</p>
<p><strong>Threshold:</strong> ${data.threshold}</p>
<p><strong>Data Source:</strong> ${data.dataSourceName}</p>
<p><strong>Time:</strong> ${new Date(data.triggeredAt).toLocaleString()}</p>
`
};

await sgMail.send(msg);
}

res.status(200).send('OK');
});

SMS (Twilio)

Send SMS alerts:

const twilio = require('twilio');
const client = twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);

app.post('/webhook', express.json(), async (req, res) => {
const { event, data } = req.body;

if (event === 'alert.triggered') {
await client.messages.create({
body: `Alert: ${data.alertName} - Current value: ${data.currentValue}`,
from: process.env.TWILIO_PHONE_NUMBER,
to: '+1234567890'
});
}

res.status(200).send('OK');
});

Retry Logic

dakkio automatically retries failed webhook deliveries:

  • Max Retries: 3 attempts
  • Backoff: Exponential (1s, 2s, 4s)
  • Timeout: 30 seconds per request

Configure retry behavior:

{
"retryConfig": {
"maxRetries": 3,
"backoffMultiplier": 2
}
}

Best Practices

1. Respond Quickly

// ✅ GOOD: Return 200 immediately, process async
app.post('/webhook', express.json(), (req, res) => {
res.status(200).send('OK');

// Process webhook in background
processWebhook(req.body).catch(console.error);
});

// ❌ BAD: Process synchronously, slow response
app.post('/webhook', express.json(), async (req, res) => {
await longRunningTask(req.body);
res.status(200).send('OK');
});

2. Always Verify Signatures

// ✅ GOOD: Verify before processing
if (!verifySignature(payload, signature, secret)) {
return res.status(401).send('Invalid signature');
}

// ❌ BAD: No verification (security risk!)
app.post('/webhook', (req, res) => {
processWebhook(req.body);
});

3. Handle Idempotency

Webhooks may be delivered more than once. Use webhook ID to prevent duplicate processing:

const processedWebhooks = new Set();

app.post('/webhook', express.json(), (req, res) => {
const webhookId = req.body.webhookId;

if (processedWebhooks.has(webhookId)) {
return res.status(200).send('Already processed');
}

processWebhook(req.body);
processedWebhooks.add(webhookId);

res.status(200).send('OK');
});

4. Log Everything

app.post('/webhook', express.json(), (req, res) => {
console.log('Webhook received:', {
event: req.body.event,
webhookId: req.body.webhookId,
timestamp: req.body.timestamp
});

try {
processWebhook(req.body);
console.log('Webhook processed successfully');
} catch (error) {
console.error('Webhook processing failed:', error);
}

res.status(200).send('OK');
});

5. Use HTTPS

Always use HTTPS endpoints for webhooks:

✅ https://your-server.com/webhook
❌ http://your-server.com/webhook

Testing Webhooks

Local Testing with ngrok

# Install ngrok
npm install -g ngrok

# Start your webhook server
node server.js

# Expose to internet
ngrok http 3000

# Use ngrok URL in dakkio
https://abc123.ngrok.io/webhook

Manual Testing

Send test webhook payload:

curl -X POST https://your-server.com/webhook \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=test" \
-d '{
"event": "alert.triggered",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"alertName": "Test Alert",
"currentValue": 35,
"threshold": 30
}
}'

Monitoring Webhooks

Track webhook delivery status in the dashboard:

{
"stats": {
"totalDeliveries": 150,
"successfulDeliveries": 145,
"failedDeliveries": 5
},
"lastDeliveryAt": "2024-01-15T10:30:00Z",
"lastDeliveryStatus": "success"
}

Troubleshooting

Webhooks Not Receiving

  1. Check URL: Ensure it's publicly accessible
  2. Verify HTTPS: HTTP may be blocked
  3. Check firewall: Allow dakkio IPs
  4. Test manually: Send test request
  5. Check logs: Look for delivery attempts

Signature Verification Failing

  1. Check secret: Ensure it matches webhook configuration
  2. Raw body: Use raw request body, not parsed JSON
  3. Encoding: Use UTF-8 encoding
  4. Timing attacks: Use timingSafeEqual in Node.js

Slow Webhook Processing

  1. Return 200 immediately: Process asynchronously
  2. Use message queue: Queue webhooks for processing
  3. Optimize processing: Reduce external API calls
  4. Scale horizontally: Add more webhook processors

Next Steps

Need Help?