Custom Webhooks
Build your own integrations using RevKeen webhooks
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, etc.
- Fulfillment - Trigger order fulfillment in your warehouse system
- Access control - Grant/revoke access in your application
- Analytics - Send events to your analytics platform
- Accounting - Sync transactions to QuickBooks, Xero
- Custom notifications - Build custom alert systems
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 200 status quickly
Example Implementation
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.
}Event Payload Structure
All webhooks follow this structure:
{
"id": "evt_abc123",
"type": "payment.succeeded",
"created_at": "2025-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 |
| Return 200 quickly | Process async to avoid timeouts |
| Handle duplicates | Same event may be sent twice |
| Use idempotency | Safe to process same event twice |
| Log events | Helps with debugging |
| Queue long tasks | Don't block the webhook response |
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 subscription is created, grant access to your product:
- Listen for
subscription.created - Create user account if needed
- Set subscription tier/permissions
- Send welcome email
Revoking Access
When subscription ends, revoke access:
- Listen for
subscription.canceled - Check if subscription has ended (vs scheduled to end)
- Downgrade to free tier or disable access
- Optionally send win-back campaign