RevenueCat's format
The same field names and values, so existing handlers keep working.
Feature
RevenueDot sends each subscription event to your URL as JSON in RevenueCat's webhook format, so handlers written for RevenueCat work unchanged. It sends 19 of RevenueCat's 21 event types. Each delivery carries an HMAC signature, is retried after 5, 10, 20, 40 and 80 minutes, and shows in a delivery log with a Retry button.
Free up to $10,000 a month in tracked revenue. Works with the RevenueCat SDK you already ship.
The same field names and values, so existing handlers keep working.
Verify X-RevenueCat-Webhook-Signature against the raw body. A timestamp stops replays.
Six attempts in all, each with its status, duration and error, and a Retry button.
Send a TEST event, or make real events with Test Store scenarios such as renewal, cancel and refund.
Steps
In the dashboard open Integrations, then Webhooks, then Add webhook, or call POST /v2/projects/{project_id}/integrations/webhooks. Optionally set an authorization header, an environment and an event filter.
The whsec_ signing secret is shown once, in the answer. Store it as a secret in your backend.
Read the raw body bytes. The header is t=<unix seconds>,v1=<hex>, where the hex is HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with the secret. Refuse the request if t is more than 5 minutes from your clock.
Only an HTTP 200 counts as delivered. A delivery can arrive twice, so ignore an event.id you have handled. Keep the handler fast, because RevenueDot waits at most 60 seconds.
Use Send test event in the dashboard. It sends a purchase-shaped TEST event signed like the others.
RevenueDot signs again on every attempt, so t is the attempt's time. Every field of every event type is on the webhook events reference.
POST /webhooks/revenuedot HTTP/1.1
Content-Type: application/json
User-Agent: RevenueDot-Webhooks/1.0
Authorization: Bearer my-shared-token
X-RevenueCat-Webhook-Signature: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
{"api_version":"1.0","event":{"id":"66339910-3BFF-49F4-B873-D1283D673DE2","type":"INITIAL_PURCHASE","app_user_id":"user_1","product_id":"pro_monthly","entitlement_ids":["pro"],"period_type":"NORMAL","environment":"SANDBOX","store":"TEST_STORE","price":9.99,"currency":"USD","presented_offering_id":"default"}} Runnable receivers for Node.js with Express, Next.js, Python with FastAPI and Go are in the examples repository.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifySignature(rawBody, header, secret, { now = new Date(), toleranceSeconds = 300 } = {}) {
const match = /(?:^|,)\s*t=(\d+)\s*,\s*v1=([0-9a-f]{64})\s*(?:,|$)/.exec(header ?? "");
if (!match) return false;
const timestamp = Number(match[1]);
// Refuse old deliveries so a captured request cannot be replayed.
if (Math.abs(Math.floor(now.getTime() / 1000) - timestamp) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(timestamp + ".").update(rawBody).digest();
const received = Buffer.from(match[2], "hex");
return received.length === expected.length && timingSafeEqual(received, expected);
} Each webhook can filter by environment, app and event type. Two webhooks each receive every event that matches. Order is not guaranteed, so use the timestamps in the event or fetch the customer's current state.
INITIAL_PURCHASE, RENEWAL, CANCELLATION, UNCANCELLATION, NON_RENEWING_PURCHASE, SUBSCRIPTION_PAUSED, EXPIRATION, BILLING_ISSUE, PRODUCT_CHANGE, SUBSCRIPTION_EXTENDED, REFUND_REVERSED and TRANSFER.PRICE_INCREASE_CONSENT_REQUIRED and PRICE_INCREASE_CONSENT_APPROVED.VIRTUAL_CURRENCY_TRANSACTION, EXPERIMENT_ENROLLMENT, PURCHASE_REDEEMED and TEST. SUBSCRIBER_ALIAS goes only to webhooks whose filter names it.FUNNEL_VIEWED, FUNNEL_STEP_COMPLETED and FUNNEL_PURCHASE.TEMPORARY_ENTITLEMENT_GRANT, because RevenueDot never grants access it has not verified, and INVOICE_ISSUANCE, which only RevenueCat Billing issues.FAQ
Read the raw request body, parse t and v1 from the X-RevenueCat-Webhook-Signature header, refuse the request if t is more than 5 minutes from your clock, then compute HMAC-SHA256 of t, a dot and the raw body with your whsec_ secret and compare it to v1 in constant time.
Yes. The payload follows RevenueCat's webhook format field for field, and RevenueDot sends 19 of the 21 event types. Only the endpoint URL and the signature secret change. The authorization header setting works the same way.
RevenueDot retries after 5, 10, 20, 40 and 80 minutes, six attempts in all, then marks the delivery failed. Only HTTP 200 counts as delivered, and each attempt waits at most 60 seconds. The delivery log shows every attempt, and you can retry a delivery by hand.
Delivery is at least once, so a retry or a lost 200 can send an event twice. Deduplicate on event.id. Order is not guaranteed either, so use the event's timestamps when order matters.
Select Send test event in the dashboard for a signed TEST event, or call the test_purchases endpoint with a scenario such as renewal, cancel or refund to produce the matching real events from the Test Store.
Get started
Start free on RevenueDot Cloud, free up to $10,000 a month in tracked revenue, or move an existing RevenueCat app with one line of code.
Already have an account? Sign in · Prefer your own servers? Self-host free