Developers
Webhooks Receive real-time event notifications via HTTP callbacks
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.
Configure an endpoint -- Register a URL in your dashboard
Event occurs -- Something happens (payment, subscription change, etc.)
RevKeen sends POST -- We send a JSON payload to your URL
You respond -- Return 200 OK to acknowledge receipt
We retry if needed -- Failed deliveries are retried automatically
All webhooks have a consistent structure:
{
"id" : "evt_xxxxxxxx" ,
"type" : "invoice.paid" ,
"created" : "2026-01-15T10:30:00Z" ,
"data" : {
"id" : "inv_xxxxxxxx" ,
"customerId" : "cus_xxxxxxxx" ,
"status" : "paid" ,
"totalMinor" : 9900 ,
"currency" : "USD"
}
}
Field Type Description idstring Unique event identifier typestring Event type (e.g., invoice.paid) createdstring ISO 8601 timestamp dataobject Event payload (varies by type)
Go to Settings > Webhooks in your dashboard
Click Add Endpoint
Enter your endpoint URL
Select events to subscribe to
Copy the signing secret for verification
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"]
}'
const webhook = await client.webhooks. create ({
url: 'https://yourapp.com/webhooks/revkeen' ,
events: [
'invoice.paid' ,
'subscription.created' ,
'subscription.cancelled' ,
],
});
// Store the signing secret securely
console. log (webhook.data.secret);
webhook = client.webhooks.create(
url = "https://yourapp.com/webhooks/revkeen" ,
events = [
"invoice.paid" ,
"subscription.created" ,
"subscription.cancelled"
]
)
# Store the signing secret securely
print (webhook.data.secret)
Always verify webhook signatures to ensure requests are from RevKeen.
Every webhook includes a X-RevKeen-Signature header:
X-RevKeen-Signature: t=1673856000,v1=5d2c67...abc123
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 }` );
}
});
import hmac
import hashlib
import time
import json
from fastapi import FastAPI, Request, HTTPException
def verify_webhook (payload: str , signature: str , secret: str ) -> bool :
parts = dict (p.split( '=' ) for p in signature.split( ',' ))
timestamp = parts[ 't' ]
sig = parts[ 'v1' ]
# Reject old timestamps (>5 minutes)
age = time.time() - int (timestamp)
if age > 300 :
raise ValueError ( 'Webhook timestamp too old' )
# Compute expected signature
expected = hmac.new(
secret.encode(),
f ' { timestamp } . { payload } ' .encode(),
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(sig, expected)
@app.post ( '/webhooks/revkeen' )
async def webhook_handler (request: Request):
signature = request.headers.get( 'x-revkeen-signature' )
payload = await request.body()
if not verify_webhook(payload.decode(), signature, WEBHOOK_SECRET ):
raise HTTPException( status_code = 401 , detail = 'Invalid signature' )
event = json.loads(payload)
if event[ 'type' ] == 'invoice.paid' :
await handle_invoice_paid(event[ 'data' ])
elif event[ 'type' ] == 'subscription.cancelled' :
await handle_subscription_cancelled(event[ 'data' ])
return { 'received' : True }
Respond quickly -- Return 200 OK as soon as possible. Process events asynchronously using a queue.
Handle duplicates -- Events may be delivered multiple times. Use the event id for idempotency.
Verify signatures -- Always verify X-RevKeen-Signature to ensure webhooks are authentic.
Use HTTPS -- Only register HTTPS endpoints. HTTP endpoints will be rejected.
Log everything -- Log incoming webhooks for debugging and audit purposes.
Test with CLI -- Use revkeen webhook test to send test events to your local endpoint.
If your endpoint does not return 2xx:
Attempt Delay 1 Immediate 2 5 minutes 3 30 minutes 4 2 hours 5 8 hours 6 24 hours
After 6 failed attempts, the event is marked as failed. View failed events in your dashboard.
Events follow the pattern: resource.action
customer.created -- A customer was created
invoice.paid -- An invoice was paid
subscription.canceled -- A subscription was canceled
You can subscribe to event patterns like invoice.* to receive all invoice events, or * to receive all events.
Event Description Action Required customer.createdCustomer was created Sync to CRM customer.updatedCustomer details changed Sync changes customer.deletedCustomer was deleted Clean up data
Event Description Action Required invoice.createdInvoice created - invoice.updatedInvoice modified - invoice.finalizedInvoice locked for payment - invoice.sentInvoice emailed - invoice.viewedCustomer viewed invoice - invoice.paidInvoice fully paid Provision service invoice.payment_failedPayment attempt failed Notify customer invoice.past_dueInvoice past due date Send reminders invoice.voidedInvoice voided Cancel fulfillment invoice.approvedInvoice approved Send to customer invoice.partially_paidPartial payment received - invoice.due_soonDue date approaching Send reminder invoice.uncollectibleMarked as bad debt Write off
Event Description Action Required subscription.createdSubscription created Wait for activation subscription.activatedSubscription active Grant access subscription.updatedSubscription changed Update access level subscription.renewedSubscription renewed Extend access subscription.canceledSubscription canceled Access until period end subscription.pausedSubscription paused Suspend access subscription.resumedSubscription resumed Restore access subscription.trial_will_endTrial ends in 3 days Notify customer subscription.past_duePayment overdue Start dunning subscription.unpaidAll retries failed Suspend access subscription.expiredTerm ended without renewal Revoke access
Event Description Action Required payment.succeededPayment captured Provision service payment.failedPayment failed Retry or notify payment.refundedPayment refunded Update fulfillment payment.capturedAuthorization captured - payment.disputedChargeback received Submit evidence payment.refund_failedRefund failed Manual intervention
Event Description Action Required payment_method.createdCard saved - payment_method.updatedCard updated (ACU) - payment_method.deletedCard removed - payment_method.expiringCard expiring soon Request new card payment_method.expiredCard expired Notify customer payment_method.failedCard validation failed Request new card
Event Description Action Required payment_link.createdPayment link created - payment_link.viewedLink viewed - payment_link.completedLink completed Fulfill order payment_link.expiredLink expired -
Event Description Action Required checkout.session.createdSession initiated - checkout.session.completedCheckout completed Fulfill order checkout.session.expiredSession expired Cart recovery
Event Description Action Required void.createdVoid initiated - void.succeededVoid completed Update records void.failedVoid failed Try refund instead
Event Description Action Required order.createdOrder created - order.paidOrder paid Begin fulfillment order.fulfilledAll items delivered - order.partially_fulfilledSome items delivered - order.canceledOrder canceled Stop fulfillment order.updatedOrder modified -
Event Description Action Required product.createdProduct created Sync catalog product.updatedProduct updated Sync changes product.deletedProduct deleted Remove from catalog
Event Description Action Required price.createdPrice created Update pricing price.updatedPrice updated Update pricing price.deletedPrice deleted Remove pricing
Event Description Action Required discount.createdDiscount created - discount.updatedDiscount updated - discount.deletedDiscount deleted - discount.redeemedDiscount used Track usage
Category Count Customer 3 Invoice 13 Subscription 11 Payment 6 Payment Method 6 Payment Link 4 Checkout Session 3 Void 3 Order 6 Product 3 Price 3 Discount 4 Total 65