DGuardAPI Docs

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.

POST/webhooks

Create Webhook Request

json
{
  "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)

json
{
  "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

POST/webhooks
GET/webhooks
GET/webhooks/{webhook_id}
PATCH/webhooks/{webhook_id}
DELETE/webhooks/{webhook_id}
POST/webhooks/{webhook_id}/rotate-secret
POST/webhooks/{webhook_id}/test

Webhook 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

EventDescriptionTrigger
fraud.detectedFraudulent transaction identifiedReal-time detection flags a transaction
fraud.resolvedFraud alert marked as resolvedManual review or automated resolution
fraud.escalatedFraud case escalated for reviewScore threshold exceeded or pattern match
fraud.score_updatedTransaction fraud score recalculatedNew data available for existing transaction

Refund Events

EventDescriptionTrigger
refund.createdNew refund request submittedPOST /refund/request called
refund.processingRefund is being processedPayment network processing started
refund.completedRefund successfully completedFunds credited to beneficiary
refund.failedRefund processing failedPayment network rejection
refund.cancelledRefund cancelled before processingPOST /refund/{id}/cancel called
refund.voidedRefund administratively voidedAdmin action or compliance review

Security Events

EventDescriptionTrigger
darkweb.leak_detectedCredentials found on dark webMonitoring detected exposed data
phishing.email_detectedPhishing email identifiedEmail scanning flagged threat
url.malicious_detectedMalicious URL identifiedURL scan detected threat
spam.call_blockedSpam call blockedCall 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)

json
{
  "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)

json
{
  "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

HeaderDescription
X-DGuard-SignatureHMAC-SHA256 signature of the payload
X-DGuard-TimestampUnix timestamp when the webhook was sent
X-DGuard-Event-IDUnique event identifier for deduplication
X-DGuard-Webhook-IDID of the webhook endpoint

Signature Verification (Node.js)

typescript
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)

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', 200

Retry Logic

If your endpoint fails to respond with a 2xx status code, DGuard will retry the delivery using exponential backoff.

Retry Schedule

Attempt 1ImmediateTotal elapsed: 0 seconds
Attempt 230 secondsTotal elapsed: 30 seconds
Attempt 32 minutesTotal elapsed: 2 min 30 sec
Attempt 410 minutesTotal elapsed: 12 min 30 sec
Attempt 530 minutesTotal elapsed: 42 min 30 sec
Attempt 61 hourTotal elapsed: 1 hr 42 min
Attempt 72 hoursTotal elapsed: 3 hr 42 min
Attempt 84 hoursTotal elapsed: 7 hr 42 min

After 8 failed attempts (approximately 8 hours), the event is marked as failed and no more retries are attempted.

What Counts as a Failure?

TimeoutWill Retry

No response within 30 seconds

5xxWill Retry

Server error responses

Connection ErrorWill Retry

DNS failure, connection refused, TLS errors

4xx (except 410)No Retry

Client errors

410 GoneNo Retry

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.

POST/webhooks/{webhook_id}/test

Request

json
{
  "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.