Webhooks
Webhooks allow you to receive real-time notifications when events occur in DGuard. Instead of polling the API for updates, webhooks push data to your server as soon as events happen.
Overview
Real-time Delivery
Events delivered within seconds of occurrence
Secure Signatures
HMAC-SHA256 signature verification
Automatic Retries
Failed deliveries retried with backoff
Setting Up Webhooks
Configure webhooks through the DGuard Dashboard or via the API. Each webhook endpoint can subscribe to specific event types.
/webhooksCreate Webhook Request
{
"url": "https://your-server.com/webhooks/dguard",
"events": [
"fraud.detected",
"fraud.resolved",
"refund.created",
"refund.completed",
"refund.failed"
],
"secret": null,
"description": "Production fraud alerts",
"metadata": {
"environment": "production"
}
}If secret is null, DGuard will generate a secure secret for you. Store this secret safely - it's only shown once.
Response (201 Created)
{
"webhook_id": "wh_abc123xyz",
"url": "https://your-server.com/webhooks/dguard",
"events": [
"fraud.detected",
"fraud.resolved",
"refund.created",
"refund.completed",
"refund.failed"
],
"secret": "whsec_a1b2c3d4e5f6g7h8i9j0...",
"status": "active",
"created_at": "2025-01-15T10:00:00Z"
}Webhook Management Endpoints
/webhooks/webhooks/webhooks/{webhook_id}/webhooks/{webhook_id}/webhooks/{webhook_id}/webhooks/{webhook_id}/rotate-secret/webhooks/{webhook_id}/testWebhook Events
Subscribe to specific events based on your integration needs. Each event type corresponds to a specific action or state change in the system.
Fraud Events
| Event | Description | Trigger |
|---|---|---|
| fraud.detected | Fraudulent transaction identified | Real-time detection flags a transaction |
| fraud.resolved | Fraud alert marked as resolved | Manual review or automated resolution |
| fraud.escalated | Fraud case escalated for review | Score threshold exceeded or pattern match |
| fraud.score_updated | Transaction fraud score recalculated | New data available for existing transaction |
Refund Events
| Event | Description | Trigger |
|---|---|---|
| refund.created | New refund request submitted | POST /refund/request called |
| refund.processing | Refund is being processed | Payment network processing started |
| refund.completed | Refund successfully completed | Funds credited to beneficiary |
| refund.failed | Refund processing failed | Payment network rejection |
| refund.cancelled | Refund cancelled before processing | POST /refund/{id}/cancel called |
| refund.voided | Refund administratively voided | Admin action or compliance review |
Security Events
| Event | Description | Trigger |
|---|---|---|
| darkweb.leak_detected | Credentials found on dark web | Monitoring detected exposed data |
| phishing.email_detected | Phishing email identified | Email scanning flagged threat |
| url.malicious_detected | Malicious URL identified | URL scan detected threat |
| spam.call_blocked | Spam call blocked | Call identified and blocked |
Webhook Payload Structure
All webhook payloads follow a consistent structure, making it easy to parse and route events in your application.
Example Payload (fraud.detected)
{
"id": "evt_abc123xyz",
"type": "fraud.detected",
"api_version": "2025-01-15",
"created_at": "2025-01-15T14:30:00Z",
"data": {
"object": "fraud_alert",
"transaction_id": "txn_abc123",
"user_id": "usr_123456",
"fraud_score": 0.92,
"fraud_level": "critical",
"amount": 5234.00,
"currency": "EUR",
"risk_factors": [
"Unusual transaction velocity",
"New device fingerprint",
"High-risk merchant category"
],
"recommendation": "deny",
"detected_at": "2025-01-15T14:29:58Z"
},
"metadata": {
"webhook_id": "wh_abc123xyz",
"delivery_attempt": 1
}
}Example Payload (refund.completed)
{
"id": "evt_def456xyz",
"type": "refund.completed",
"api_version": "2025-01-15",
"created_at": "2025-01-15T14:31:00Z",
"data": {
"object": "refund",
"refund_id": "ref_abc123xyz",
"transaction_id": "txn_abc123",
"amount": 5234.00,
"currency": "EUR",
"status": "completed",
"beneficiary": {
"name": "Juan García López",
"account_masked": "JO94****0302"
},
"completed_at": "2025-01-15T14:30:58Z",
"processing_time_seconds": 18,
"reference": "CLIQ-20250115-789012"
},
"metadata": {
"webhook_id": "wh_abc123xyz",
"delivery_attempt": 1
}
}Verifying Webhook Signatures
Security Critical
Always verify webhook signatures before processing events. Failing to verify signatures leaves your application vulnerable to spoofed events.
DGuard signs all webhook payloads using HMAC-SHA256 with your webhook secret. The signature is included in the X-DGuard-Signature header.
Webhook Headers
| Header | Description |
|---|---|
| X-DGuard-Signature | HMAC-SHA256 signature of the payload |
| X-DGuard-Timestamp | Unix timestamp when the webhook was sent |
| X-DGuard-Event-ID | Unique event identifier for deduplication |
| X-DGuard-Webhook-ID | ID of the webhook endpoint |
Signature Verification (Node.js)
import crypto from 'crypto';
interface WebhookHeaders {
'x-dguard-signature': string;
'x-dguard-timestamp': string;
'x-dguard-event-id': string;
}
function verifyWebhookSignature(
payload: string,
headers: WebhookHeaders,
secret: string,
toleranceSeconds: number = 300 // 5 minutes
): boolean {
const signature = headers['x-dguard-signature'];
const timestamp = headers['x-dguard-timestamp'];
// 1. Check timestamp to prevent replay attacks
const currentTime = Math.floor(Date.now() / 1000);
const webhookTime = parseInt(timestamp, 10);
if (Math.abs(currentTime - webhookTime) > toleranceSeconds) {
console.error('Webhook timestamp outside tolerance window');
return false;
}
// 2. Compute expected signature
// Signature is computed over: timestamp + '.' + payload
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// 3. Compare signatures using timing-safe comparison
const signatureBuffer = Buffer.from(signature, 'hex');
const expectedBuffer = Buffer.from(expectedSignature, 'hex');
if (signatureBuffer.length !== expectedBuffer.length) {
return false;
}
return crypto.timingSafeEqual(signatureBuffer, expectedBuffer);
}
// Usage in Express.js
app.post('/webhooks/dguard', express.raw({ type: 'application/json' }), (req, res) => {
const payload = req.body.toString();
const headers = req.headers as unknown as WebhookHeaders;
if (!verifyWebhookSignature(payload, headers, process.env.DGUARD_WEBHOOK_SECRET!)) {
console.error('Invalid webhook signature');
return res.status(401).send('Invalid signature');
}
// Process the verified webhook
const event = JSON.parse(payload);
console.log('Received verified event:', event.type);
// Always respond quickly with 200
res.status(200).send('OK');
// Process event asynchronously
processWebhookEvent(event);
});Signature Verification (Python)
import hmac
import hashlib
import time
def verify_webhook_signature(
payload: bytes,
signature: str,
timestamp: str,
secret: str,
tolerance_seconds: int = 300
) -> bool:
# 1. Check timestamp to prevent replay attacks
current_time = int(time.time())
webhook_time = int(timestamp)
if abs(current_time - webhook_time) > tolerance_seconds:
print("Webhook timestamp outside tolerance window")
return False
# 2. Compute expected signature
signed_payload = f"{timestamp}.{payload.decode('utf-8')}"
expected_signature = hmac.new(
secret.encode('utf-8'),
signed_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# 3. Compare signatures using timing-safe comparison
return hmac.compare_digest(signature, expected_signature)
# Usage in Flask
@app.route('/webhooks/dguard', methods=['POST'])
def handle_webhook():
payload = request.get_data()
signature = request.headers.get('X-DGuard-Signature')
timestamp = request.headers.get('X-DGuard-Timestamp')
if not verify_webhook_signature(payload, signature, timestamp, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = json.loads(payload)
print(f"Received verified event: {event['type']}")
# Process event asynchronously (use Celery, etc.)
process_webhook_event.delay(event)
return 'OK', 200Retry Logic
If your endpoint fails to respond with a 2xx status code, DGuard will retry the delivery using exponential backoff.
Retry Schedule
After 8 failed attempts (approximately 8 hours), the event is marked as failed and no more retries are attempted.
What Counts as a Failure?
No response within 30 seconds
Server error responses
DNS failure, connection refused, TLS errors
Client errors
Endpoint explicitly disabled
Best Practices
Respond Quickly
Return a 200 response as quickly as possible (within 5 seconds). Process the event asynchronously using a job queue to avoid timeouts.
Implement Idempotency
Use the X-DGuard-Event-ID header to deduplicate events. Store processed event IDs and skip duplicates to handle retries safely.
Use HTTPS
Webhook URLs must use HTTPS with a valid SSL certificate. HTTP URLs and self-signed certificates are not supported.
Rotate Secrets Periodically
Rotate your webhook secret periodically using the POST /webhooks/{webhook_id}/rotate-secret endpoint. During rotation, both old and new secrets are valid for 24 hours.
Monitor Webhook Health
Check the DGuard Dashboard for webhook delivery metrics. Webhooks with high failure rates will automatically be disabled after 7 consecutive days of failures.
Testing Webhooks
Use the test endpoint to send sample events to your webhook URL without affecting real data.
/webhooks/{webhook_id}/testRequest
{
"event_type": "fraud.detected"
}Sandbox Environment
In the sandbox environment, all events are test events by default. Use the metadata.test_mode: true field in production to identify test events.