Skip to content

Webhook Notifications

Webhooks enable programmatic integration with your systems, sending HTTP POST requests when events occur.

Setup

Adding a Webhook

  1. Go to Settings → Notifications → Add Channel
  2. Select Webhook
  3. Enter your webhook URL
  4. Click Save
  5. Copy the HMAC secret for signature verification

Webhook URL Requirements

  • HTTPS is recommended
  • Must return 2xx status code within 30 seconds
  • Should be publicly accessible

Local / HTTP development

In some self-hosted or development setups, you may be able to use plain HTTP depending on server configuration. For production, use HTTPS.

Webhook Payload

Request Format

POST /your-webhook-endpoint HTTP/1.1
Host: your-server.com
Content-Type: application/json
X-BtcWatch-Signature: sha256=abc123...
X-BtcWatch-Timestamp: 1705312496

{
  "event_type": "transaction.deposit",
  "severity": "info",
  "title": "Deposit detected",
  "message": "A deposit was detected in the mempool",
  "user_id": 123,
  "wallet_id": 456,
  "event_data": {},
  "timestamp": "2024-01-15T12:34:56Z"
}

Common Payload Fields

Field Type Description
event_type string Event type
timestamp ISO 8601 When event occurred
severity string Severity (e.g. info, warning, critical)
title string Short human-readable title
message string Human-readable message
user_id number User ID
wallet_id number Wallet ID (may be null)
event_data object Event-specific structured payload

Event-Specific Fields

See Event Types for fields specific to each event.

HMAC Signature Verification

Webhook requests may be signed using HMAC-SHA256. If a secret is configured for the channel, verify signatures to ensure requests are from Vigil.

Signature Header

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

Timestamp Header

X-BtcWatch-Timestamp: 1705312496

Verification Algorithm

import hmac
import hashlib

def verify_signature(timestamp: str, payload: bytes, signature: str, secret: str) -> bool:
    received_sig = signature.replace("sha256=", "")

    mac = hmac.new(secret.encode(), digestmod=hashlib.sha256)
    mac.update(timestamp.encode())
    mac.update(b".")
    mac.update(payload)
    expected_sig = mac.hexdigest()

    # Constant-time comparison
    return hmac.compare_digest(received_sig, expected_sig)

Example: Node.js Verification

const crypto = require('crypto');

function verifySignature(timestamp, payload, signature, secret) {
  const receivedSig = signature.replace('sha256=', '');
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.`)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(receivedSig),
    Buffer.from(expectedSig)
  );
}

Example: Python Verification

import hmac
import hashlib

def verify_webhook(request, secret):
    signature = request.headers.get('X-BtcWatch-Signature')
    timestamp = request.headers.get('X-BtcWatch-Timestamp')
    payload = request.get_data()

    if not signature:
        return False

    return verify_signature(timestamp, payload, signature, secret)

Replay Prevention

Use the timestamp header to prevent replay attacks:

import time

def is_request_fresh(timestamp_header: str, tolerance_seconds: int = 300) -> bool:
    request_time = int(timestamp_header)
    current_time = int(time.time())

    return abs(current_time - request_time) < tolerance_seconds

Retry Policy

If your endpoint returns an error or times out:

Attempt Delay
1 Immediate
2 1 minute
3 5 minutes
4 30 minutes
5 2 hours

After repeated failures, the channel may be marked as failing.

Response Requirements

Your endpoint must:

  • Return HTTP 2xx status code
  • Respond within 30 seconds
  • Accept application/json content type
{
  "status": "received"
}

Webhook Management

Viewing Delivery History

  1. Go to Settings → Notifications
  2. Click on your webhook channel
  3. View Delivery History

May show:

  • Recent delivery attempts
  • Response codes
  • Failures and retries

Regenerating Secret

If your secret is compromised:

  1. Go to webhook settings
  2. Click Regenerate Secret
  3. Update your server with new secret
  4. Old secret is invalidated

Pausing a Webhook

Temporarily disable without deleting:

  1. Toggle the webhook off
  2. No webhooks will be sent
  3. Toggle back on to resume

Testing Webhooks

Test Endpoint

Use a service like webhook.site for testing:

  1. Go to webhook.site
  2. Copy your unique URL
  3. Add as webhook in Vigil
  4. Click the Test button
  5. View the test payload on webhook.site

Local Development

For local testing, use ngrok or similar:

ngrok http 3000
# Use the https URL from ngrok

Security Best Practices

  • Always verify HMAC signatures
  • Use HTTPS only
  • Check timestamp freshness
  • Store secret securely (environment variable)
  • Log webhook deliveries for debugging
  • Set up monitoring for webhook failures

Next: Nostr Notifications →