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
- Go to Settings > Webhooks
- Click Add Endpoint
- Enter your endpoint URL (must be HTTPS)
- Select events to receive
- Copy the signing secret for verification
- Save the endpoint
Building Your Endpoint
Your webhook endpoint needs to:
- Accept POST requests
- Verify the signature
- Process the event
- 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 hereEvent 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
| Practice | Why |
|---|---|
| Always verify signatures | Prevents spoofed requests from reaching your system |
| Return 200 quickly | Process asynchronously to avoid timeout errors |
| Handle duplicates | The same event may be delivered more than once |
| Use idempotency | Ensure it is safe to process the same event twice |
| Log events | Helps with debugging integration issues |
| Queue long tasks | Don't block the webhook response with slow operations |
Testing Your Webhook
- Deploy your endpoint to a publicly accessible URL
- Add it to RevKeen webhook settings
- Click Send Test Event
- Select an event type
- 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