DOCS/WEBHOOKS

Webhooks

Receive HTTP POST notifications when blockchain events occur. Push-based notifications eliminate the need for polling and ensure you never miss an event.

Quick Setup

  1. Go to Dashboard → Webhooks
  2. Click "Create Webhook"
  3. Enter your HTTPS endpoint URL
  4. Select the events you want to receive
  5. Optionally configure filters (addresses, currencies, etc.)
  6. Save and securely store your webhook signing secret

Event Types

Subscribe to any combination of these events:

Block Events

block.new

Triggered when a new block is confirmed on the chain

block.reorganization

Triggered when a chain reorganization occurs (rare but important)

Transaction Events

transaction.confirmed

Triggered when a transaction reaches your specified confirmation threshold

transaction.large

Triggered when a transaction exceeds your configured value threshold

Address Events

address.received

Triggered when a watched address receives any currency

address.sent

Triggered when a watched address sends any currency

address.balance_changed

Triggered on any balance change (combines received + sent)

Identity Events

identity.created

Triggered when a new VerusID is registered

identity.updated

Triggered when a VerusID's addresses or content map changes

identity.revoked

Triggered when a VerusID is revoked

Currency Events

currency.conversion

Triggered when a currency conversion occurs in a basket currency

currency.price_change

Triggered when a currency's price changes by more than your threshold

Payload Format

All webhooks share a consistent payload structure:

{
  "id": "evt_abc123def456",
  "type": "address.received",
  "chainId": "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq",
  "timestamp": "2024-02-07T12:00:00.000Z",
  "data": {
    "address": "RXL3YXG2ceaB6C5hfJcN4fvmLH2C34knhA",
    "txid": "abc123...",
    "amount": "1000.00000000",
    "currencyId": "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq",
    "currencyName": "VRSCTEST",
    "blockHeight": 689000,
    "confirmations": 1
  }
}

Block Event Payload

{
  "id": "evt_block_789",
  "type": "block.new",
  "chainId": "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq",
  "timestamp": "2024-02-07T12:01:00.000Z",
  "data": {
    "height": 689001,
    "hash": "000000000012abc456def...",
    "previousHash": "000000000011xyz...",
    "time": 1707307260,
    "txCount": 5,
    "size": 4521,
    "difficulty": 1234567890.12
  }
}

Identity Event Payload

{
  "id": "evt_identity_456",
  "type": "identity.updated",
  "chainId": "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq",
  "timestamp": "2024-02-07T12:02:00.000Z",
  "data": {
    "name": "mike",
    "fullyQualifiedName": "mike@",
    "identityAddress": "iAddress...",
    "operation": "update",
    "version": 2,
    "blockHeight": 689002,
    "txid": "def456...",
    "changes": ["primaryAddresses", "contentMap"]
  }
}

Webhook Headers

Every webhook request includes these headers:

X-ChainVue-Signature

HMAC-SHA256 signature of the payload

X-ChainVue-Event-ID

Unique event identifier (for deduplication)

X-ChainVue-Timestamp

Unix timestamp when the webhook was sent

Content-Type

Always application/json

Verifying Signatures

Always verify webhook signatures to ensure the request is authentic and hasn't been tampered with.

Node.js Example

import crypto from 'crypto';

function verifyWebhook(payload, signature, timestamp, secret) {
  // Prevent replay attacks - reject webhooks older than 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (now - parseInt(timestamp) > 300) {
    throw new Error('Webhook timestamp too old');
  }

  // Calculate expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Constant-time comparison to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express.js middleware example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-chainvue-signature'];
  const timestamp = req.headers['x-chainvue-timestamp'];

  if (!verifyWebhook(req.body, signature, timestamp, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  // Process event...

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

Python Example

import hmac
import hashlib
import time

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
    # Prevent replay attacks
    if time.time() - int(timestamp) > 300:
        raise ValueError("Webhook timestamp too old")

    # Calculate expected signature
    signed_payload = f"{timestamp}.{payload.decode()}"
    expected = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Constant-time comparison
    return hmac.compare_digest(signature, expected)

# Flask example
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-ChainVue-Signature')
    timestamp = request.headers.get('X-ChainVue-Timestamp')

    if not verify_webhook(request.data, signature, timestamp, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = request.json
    # Process event...

    return 'OK', 200

Retry Policy

If your endpoint returns a non-2xx status code or times out, we automatically retry with exponential backoff:

AttemptDelayTotal Time
1Immediate0
21 minute1 min
35 minutes6 min
430 minutes36 min
52 hours~2.5 hours

After 5 failed attempts, the webhook is marked as failed. View delivery logs in your dashboard to debug issues.

Best Practices

  • Always verify signatures — Never process unsigned webhooks
  • Respond quickly — Return 200 within 5 seconds, process async
  • Handle duplicates — Use event IDs for idempotency
  • Use HTTPS only — We reject non-TLS endpoints
  • Monitor delivery logs — Check the dashboard for failures
  • Set up alerting — Get notified when webhooks fail

Webhooks vs Subscriptions

Choose the right approach for your use case:

Use Webhooks when:

  • • You need server-to-server notifications
  • • Your backend should react to events
  • • You want guaranteed delivery with retries
  • • You're building notification systems

Use Subscriptions when:

  • • You need real-time UI updates
  • • Building client-side applications
  • • You want lowest possible latency
  • • You're building block explorers