RevKeen Docs
Integrations

Custom Webhooks

Build custom integrations with RevKeen webhook events

Custom webhooks allow you to build your own integrations with RevKeen. Receive real-time notifications about events and trigger actions in your systems.

When to Use Custom Webhooks

Custom webhooks are ideal for:

  • CRM integration -- Update customer records in Salesforce, HubSpot, or other CRMs
  • Fulfillment -- Trigger order fulfillment in your warehouse system
  • Access control -- Grant or revoke access in your application when subscriptions change
  • Analytics -- Send events to your analytics platform
  • Accounting -- Sync transactions to QuickBooks, Xero, or other accounting software
  • Custom notifications -- Build custom alert systems beyond what built-in integrations provide

Setting Up a Custom Webhook

  1. Go to Settings > Webhooks
  2. Click Add Endpoint
  3. Enter your endpoint URL (must be HTTPS)
  4. Select events to receive
  5. Copy the signing secret for verification
  6. Save the endpoint

Building Your Endpoint

Your webhook endpoint needs to:

  1. Accept POST requests
  2. Verify the signature
  3. Process the event
  4. Return a 200 status quickly

Example Implementation

Node.js (Express):

import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.REVKEEN_WEBHOOK_SECRET;

function verifySignature(payload: string, signature: string): boolean {
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}

app.post('/webhooks/revkeen', (req, res) => {
  // Verify signature
  const signature = req.headers['x-revkeen-signature'] as string;
  if (!verifySignature(JSON.stringify(req.body), signature)) {
    return res.status(401).send('Invalid signature');
  }

  // Get the event
  const event = req.body;

  // Process based on event type
  switch (event.type) {
    case 'payment.succeeded':
      handlePaymentSucceeded(event.data);
      break;
    case 'subscription.created':
      handleSubscriptionCreated(event.data);
      break;
    case 'subscription.canceled':
      handleSubscriptionCanceled(event.data);
      break;
    // Add more event handlers
  }

  // Return 200 quickly
  res.status(200).send('OK');
});

function handlePaymentSucceeded(data: any) {
  console.log('Payment received:', data.amount);
  // Update your database, send to analytics, etc.
}

function handleSubscriptionCreated(data: any) {
  console.log('New subscription:', data.id);
  // Grant access, update CRM, etc.
}

function handleSubscriptionCanceled(data: any) {
  console.log('Subscription canceled:', data.id);
  // Revoke access, send win-back email, etc.
}

Python (Flask):

import hmac
import hashlib
import os
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get('REVKEEN_WEBHOOK_SECRET')

def verify_signature(payload: bytes, signature: str) -> bool:
    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

@app.route('/webhooks/revkeen', methods=['POST'])
def handle_webhook():
    # Verify signature
    signature = request.headers.get('X-Revkeen-Signature')
    if not verify_signature(request.data, signature):
        return 'Invalid signature', 401

    event = request.json

    # Process event
    if event['type'] == 'payment.succeeded':
        handle_payment_succeeded(event['data'])
    elif event['type'] == 'subscription.created':
        handle_subscription_created(event['data'])
    elif event['type'] == 'subscription.canceled':
        handle_subscription_canceled(event['data'])

    return 'OK', 200

def handle_payment_succeeded(data):
    print(f"Payment received: {data['amount']}")
    # Your logic here

def handle_subscription_created(data):
    print(f"New subscription: {data['id']}")
    # Your logic here

def handle_subscription_canceled(data):
    print(f"Subscription canceled: {data['id']}")
    # Your logic here

Event Payload Structure

All webhooks follow this structure:

{
  "id": "evt_abc123",
  "type": "payment.succeeded",
  "created_at": "2026-01-15T10:30:00Z",
  "data": {
    "id": "pay_xyz789",
    "amount": 9900,
    "currency": "USD",
    "customer": {
      "id": "cus_def456",
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "invoice_id": "inv_ghi012"
  },
  "entities": {
    "customer_id": "cus_def456",
    "invoice_id": "inv_ghi012"
  }
}

Best Practices

PracticeWhy
Always verify signaturesPrevents spoofed requests from reaching your system
Return 200 quicklyProcess asynchronously to avoid timeout errors
Handle duplicatesThe same event may be delivered more than once
Use idempotencyEnsure it is safe to process the same event twice
Log eventsHelps with debugging integration issues
Queue long tasksDon't block the webhook response with slow operations

Testing Your Webhook

  1. Deploy your endpoint to a publicly accessible URL
  2. Add it to RevKeen webhook settings
  3. Click Send Test Event
  4. Select an event type
  5. Check your logs for the received event

For local development, use a tool like ngrok to expose your local server with a public URL.

Common Integration Patterns

Granting Access

When a subscription is created, grant access to your product:

  • Listen for subscription.created
  • Create a user account if needed
  • Set subscription tier and permissions
  • Send a welcome email

Revoking Access

When a subscription ends, revoke access:

  • Listen for subscription.canceled
  • Check if the subscription has ended (vs scheduled to end in the future)
  • Downgrade to free tier or disable access
  • Optionally trigger a win-back campaign

On this page