Webhooks Overview

Receive real-time notifications when events occur in RevKeen

Webhooks enable your application to receive real-time HTTP notifications when events occur in your RevKeen account. Instead of polling the API, webhooks push data to you.

How Webhooks Work

  1. Configure an endpoint — Register a URL in your dashboard
  2. Event occurs — Something happens (payment, subscription change, etc.)
  3. RevKeen sends POST — We send a JSON payload to your URL
  4. You respond — Return 200 OK to acknowledge receipt
  5. We retry if needed — Failed deliveries are retried automatically

Webhook Payload

All webhooks have a consistent structure:

{
  "id": "evt_xxxxxxxx",
  "type": "invoice.paid",
  "created": "2025-01-15T10:30:00Z",
  "data": {
    "id": "inv_xxxxxxxx",
    "customerId": "cus_xxxxxxxx",
    "status": "paid",
    "totalMinor": 9900,
    "currency": "USD"
  }
}
FieldTypeDescription
idstringUnique event identifier
typestringEvent type
createdstringISO 8601 timestamp
dataobjectEvent payload (varies by type)

Setting Up Webhooks

Via Dashboard

  1. Go to Settings → Webhooks in your dashboard
  2. Click Add Endpoint
  3. Enter your endpoint URL
  4. Select events to subscribe to
  5. Copy the signing secret for verification

Via API

curl -X POST "https://api.revkeen.com/v1/webhooks" \
  -H "Authorization: Bearer rk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/revkeen",
    "events": ["invoice.paid", "subscription.created", "subscription.cancelled"]
  }'

Verifying Signatures

Always verify webhook signatures to ensure requests are from RevKeen:

Signature Header

Every webhook includes a X-RevKeen-Signature header:

X-RevKeen-Signature: t=1673856000,v1=5d2c67...abc123

TypeScript Verification

import crypto from 'crypto';

function verifyWebhook(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const [timestamp, sig] = signature.split(',').map(s => s.split('=')[1]);
  
  // Reject old timestamps (>5 minutes)
  const age = Date.now() - parseInt(timestamp) * 1000;
  if (age > 300000) {
    throw new Error('Webhook timestamp too old');
  }
  
  // Compute expected signature
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');
  
  // Constant-time comparison
  return crypto.timingSafeEqual(
    Buffer.from(sig),
    Buffer.from(expected)
  );
}

// Express.js example
app.post('/webhooks/revkeen', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-revkeen-signature'] as string;
  const payload = req.body.toString();
  
  try {
    if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET!)) {
      return res.status(401).send('Invalid signature');
    }
    
    const event = JSON.parse(payload);
    
    switch (event.type) {
      case 'invoice.paid':
        handleInvoicePaid(event.data);
        break;
      case 'subscription.cancelled':
        handleSubscriptionCancelled(event.data);
        break;
    }
    
    res.json({ received: true });
  } catch (err) {
    res.status(400).send(`Webhook Error: ${err.message}`);
  }
});

Best Practices

Return 200 OK as soon as possible. Process events asynchronously using a queue.
Events may be delivered multiple times. Use the event id for idempotency.
Always verify X-RevKeen-Signature to ensure webhooks are authentic.
Only register HTTPS endpoints. HTTP endpoints will be rejected.
Log incoming webhooks for debugging and audit purposes.
Use revkeen webhook test to send test events to your local endpoint.

Retry Policy

If your endpoint doesn't return 2xx:

AttemptDelay
1Immediate
25 minutes
330 minutes
42 hours
58 hours
624 hours
After 6 failed attempts, the event is marked as failed. View failed events in your dashboard.

Related Resources