Documentation Index
Fetch the complete documentation index at: https://drippi.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Overview
All Drippi webhook requests include a signature in the X-Drippi-Signature header that you must verify to ensure the request authentically came from Drippi and hasn’t been tampered with.
The signature is a HMAC-SHA256 hash of the request body using your webhook’s signing secret:
X-Drippi-Signature: sha256=<signature>
Getting Your Signing Secret
When you create a webhook endpoint, Drippi generates a unique signing secret for that endpoint. This secret is returned in the API response and should be stored securely in your application.
Verification Examples
Node.js
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
// Remove 'sha256=' prefix from signature
const receivedSignature = signature.replace('sha256=', '');
// Calculate expected signature
const expectedSignature = crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex');
// Compare signatures using timing-safe comparison
return crypto.timingSafeEqual(Buffer.from(receivedSignature, 'hex'), Buffer.from(expectedSignature, 'hex'));
}
// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-drippi-signature'];
const payload = req.body;
const secret = process.env.DRIPPI_WEBHOOK_SECRET;
if (!verifyWebhookSignature(payload, signature, secret)) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
// Process the webhook
const event = JSON.parse(payload);
console.log('Received webhook:', event.event);
res.status(200).send('OK');
});
Python
import hashlib
import hmac
import json
from flask import Flask, request, abort
app = Flask(__name__)
def verify_webhook_signature(payload, signature, secret):
"""Verify the webhook signature"""
# Remove 'sha256=' prefix from signature
received_signature = signature.replace('sha256=', '')
# Calculate expected signature
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
# Compare signatures using timing-safe comparison
return hmac.compare_digest(received_signature, expected_signature)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Drippi-Signature')
payload = request.get_data()
secret = os.environ.get('DRIPPI_WEBHOOK_SECRET')
if not verify_webhook_signature(payload, signature, secret):
print('Invalid webhook signature')
abort(401)
# Process the webhook
event = json.loads(payload)
print(f'Received webhook: {event["event"]}')
return 'OK', 200
PHP
<?php
function verifyWebhookSignature($payload, $signature, $secret) {
// Remove 'sha256=' prefix from signature
$receivedSignature = str_replace('sha256=', '', $signature);
// Calculate expected signature
$expectedSignature = hash_hmac('sha256', $payload, $secret);
// Compare signatures using timing-safe comparison
return hash_equals($receivedSignature, $expectedSignature);
}
// Get the webhook data
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_DRIPPI_SIGNATURE'] ?? '';
$secret = $_ENV['DRIPPI_WEBHOOK_SECRET'];
if (!verifyWebhookSignature($payload, $signature, $secret)) {
error_log('Invalid webhook signature');
http_response_code(401);
exit('Unauthorized');
}
// Process the webhook
$event = json_decode($payload, true);
error_log('Received webhook: ' . $event['event']);
http_response_code(200);
echo 'OK';
?>
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
)
func verifyWebhookSignature(payload []byte, signature, secret string) bool {
// Remove 'sha256=' prefix from signature
receivedSignature := strings.TrimPrefix(signature, "sha256=")
// Calculate expected signature
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expectedSignature := hex.EncodeToString(mac.Sum(nil))
// Compare signatures using timing-safe comparison
return hmac.Equal([]byte(receivedSignature), []byte(expectedSignature))
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
signature := r.Header.Get("X-Drippi-Signature")
secret := os.Getenv("DRIPPI_WEBHOOK_SECRET")
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusBadRequest)
return
}
if !verifyWebhookSignature(payload, signature, secret) {
log.Println("Invalid webhook signature")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Process the webhook
var event map[string]interface{}
if err := json.Unmarshal(payload, &event); err != nil {
http.Error(w, "Error parsing JSON", http.StatusBadRequest)
return
}
log.Printf("Received webhook: %s", event["event"])
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "OK")
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Security Best Practices
1. Always Verify Signatures
Never process webhook payloads without first verifying the signature. This prevents malicious actors from sending fake webhook requests.
2. Use Timing-Safe Comparison
Use timing-safe comparison functions (like crypto.timingSafeEqual() in Node.js) to prevent timing attacks when comparing signatures.
3. Store Secrets Securely
Store your webhook signing secrets in environment variables or a secure key management system. Never hardcode them in your application.
4. Use HTTPS
Always use HTTPS endpoints for your webhooks to prevent man-in-the-middle attacks.
5. Validate Payload Structure
After verifying the signature, validate that the payload structure matches the expected webhook format.
Troubleshooting
Common Issues
Signature Mismatch
- Ensure you’re using the correct signing secret for the webhook endpoint
- Verify you’re using the raw request body (not parsed JSON) for signature calculation
- Check that you’re removing the ‘sha256=’ prefix from the signature header
Missing Signature Header
- Ensure your webhook endpoint URL is correctly configured
- Check that you’re reading the
X-Drippi-Signature header (case-sensitive)
Encoding Issues
- Use UTF-8 encoding when calculating the HMAC signature
- Ensure consistent encoding between signature calculation and verification
Testing Your Implementation
You can test your webhook verification by:
- Creating a test webhook endpoint in the Drippi dashboard
- Triggering test events from your automations
- Logging successful and failed verification attempts
- Using webhook testing tools to send sample payloads
Next Steps
- Webhook Events - Learn about all available webhook events and their payloads