Integration · Webhooks and data

RevenueCat-compatible webhooks for subscription events

RevenueDot sends webhooks in RevenueCat's format, so a handler written for RevenueCat keeps working. It accepts all 21 RevenueCat event types as filters and sends 19 of them. Each POST is signed with an HMAC in X-RevenueCat-Webhook-Signature, can carry your Authorization header, and retries 5 times after a failure. Every delivery is logged.

What teams do with it

  • Grant or remove access in your own database when a purchase, renewal or expiration happens.
  • Send sandbox and production events to different URLs, and pick which event types each URL gets.
  • Reuse the webhook handler you wrote for RevenueCat without changes.
  • Find out why an event did not arrive from the delivery log, then resend it.

What RevenueDot sends

  • POST with Content-Type: application/json and the body {"api_version":"1.0","event":{...}}, with the field names and values of RevenueCat's webhooks.
  • X-RevenueCat-Webhook-Signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> keyed with your whsec_ signing secret. It is signed again on every attempt.
  • Your optional authorization_header, sent verbatim as the Authorization header. User-Agent is RevenueDot-Webhooks/1.0.
  • 19 of RevenueCat's 21 event types: TEST, INITIAL_PURCHASE, RENEWAL, CANCELLATION, UNCANCELLATION, NON_RENEWING_PURCHASE, SUBSCRIPTION_PAUSED, EXPIRATION, BILLING_ISSUE, PRODUCT_CHANGE, SUBSCRIPTION_EXTENDED, REFUND_REVERSED, TRANSFER, VIRTUAL_CURRENCY_TRANSACTION, EXPERIMENT_ENROLLMENT, PRICE_INCREASE_CONSENT_REQUIRED, PRICE_INCREASE_CONSENT_APPROVED, PURCHASE_REDEEMED and SUBSCRIBER_ALIAS (opt-in).
  • Opt-in funnel types of RevenueDot's own: FUNNEL_VIEWED, FUNNEL_STEP_COMPLETED and FUNNEL_PURCHASE. Only webhooks that name them in event_types get them.

Setup

How to connect Webhooks to RevenueDot

  1. 01

    Add a webhook

    In the dashboard, open Integrations → Webhooks → Add webhook, or call POST /v2/projects/{project_id}/integrations/webhooks.

  2. 02

    Set the URL and filters

    Enter the url of your endpoint, an optional authorization_header, the environment (production, sandbox or both) and, if you want, event_types and an app_id. Empty event_types means every type except the opt-in ones.

  3. 03

    Keep the signing secret

    Copy the whsec_ signing_secret. It is shown once, in the answer to the create call, so store it as a secret in your backend.

  4. 04

    Verify the signature

    In your handler, compute the HMAC-SHA256 of <t>.<raw body> with the secret and compare it with v1. Use the raw bytes, not re-serialized JSON.

  5. 05

    Answer 200 and send a test event

    Return HTTP 200 quickly and ignore events whose event.id you already handled. Click Send test event to receive a signed TEST event.

The Webhooks integration page in the RevenueDot dashboard, with its settings form
Captured from the RevenueDot dashboard with demo data.

The 21 event types and which ones RevenueDot sends

Event typeSentWhen it is sent
TESTYesThe Send test event button, or POST .../test
INITIAL_PURCHASEYesFirst purchase of a chain, paid or trial, and promotional grants
RENEWALYesA new period, a resubscription (win-back included), a trial conversion
CANCELLATIONYesAuto-renew off, a billing error, a refund (CUSTOMER_SUPPORT)
UNCANCELLATIONYesAuto-renew back on
NON_RENEWING_PURCHASEYesOne-time purchases
SUBSCRIPTION_PAUSEDYesA Google Play pause is scheduled
EXPIRATIONYesAccess ends
BILLING_ISSUEYesA charge failed
PRODUCT_CHANGEYesAn upgrade, downgrade or crossgrade
SUBSCRIPTION_EXTENDEDYesApple extend, Google defer, or a longer expiry in the same period
REFUND_REVERSEDYesApple reversed a refund
TRANSFERYesA purchase moves to another app user ID
VIRTUAL_CURRENCY_TRANSACTIONYesA product grant credits a balance
EXPERIMENT_ENROLLMENTYesA customer joins an offering experiment
PRICE_INCREASE_CONSENT_REQUIREDYesThe store asks the customer to accept a higher price
PRICE_INCREASE_CONSENT_APPROVEDYesThe customer accepted the higher price
PURCHASE_REDEEMEDYesA web purchase is redeemed in the app with a redemption link
SUBSCRIBER_ALIASOpt-inA new app user ID joins an existing customer; only webhooks that name it get it
TEMPORARY_ENTITLEMENT_GRANTNeverRevenueDot never grants access it has not verified
INVOICE_ISSUANCENeverRevenueDot has no billing engine that issues invoices

All 21 types are valid in a webhook's event filter. The two marked Never stay filters that never fire.

Verify the signature in Node.js

Hash the raw request body, not parsed JSON. Reject deliveries older than a few minutes so a captured request cannot be replayed.

Check X-RevenueCat-Webhook-Signaturejavascript
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifySignature(rawBody, header, secret, toleranceSeconds = 300) {
  const m = /(?:^|,)\s*t=(\d+)\s*,\s*v1=([0-9a-f]{64})\s*(?:,|$)/.exec(header ?? '');
  if (!m) return false;
  const t = Number(m[1]);
  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest();
  const received = Buffer.from(m[2], 'hex');
  return received.length === expected.length && timingSafeEqual(received, expected);
}

Retries, deduplication and the delivery log

  • Only HTTP 200 counts as delivered. A 201, 204 or redirect is a failure. RevenueCat documents the same rule for its webhooks (checked October 2026).
  • Retry schedule: after a failure RevenueDot retries after 5, 10, 20, 40 and 80 minutes, 6 attempts in all, then marks the delivery failed. Each attempt times out after 60 seconds.
  • At least once: a delivery can arrive twice, for example when your 200 is lost. Deduplicate on event.id. Order is not guaranteed.
  • Delivery log: GET /v2/projects/{project_id}/webhooks/{id}/deliveries?status=failed shows each attempt's HTTP status, duration and error. Retry one with POST .../deliveries/{delivery_id}/retry, or use the dashboard.
  • Filters and pause: filter by environment, event type and app, and pause a webhook without deleting it. Queued retries resume when you turn it back on.

FAQ

Webhooks and RevenueDot

How do I verify a RevenueDot webhook signature?

The X-RevenueCat-Webhook-Signature header is t=<unix seconds>,v1=<hex>. Compute the HMAC-SHA256 of <t>.<raw body> with your whsec_ signing secret, compare it with v1 in constant time, and refuse a t older than a few minutes. Use the raw request bytes.

Which event types does RevenueDot send in webhooks?

19 of RevenueCat's 21 types, including INITIAL_PURCHASE, RENEWAL, CANCELLATION, EXPIRATION and BILLING_ISSUE. TEMPORARY_ENTITLEMENT_GRANT and INVOICE_ISSUANCE are valid filters but never sent. SUBSCRIBER_ALIAS is opt-in.

Do my RevenueCat webhook handlers work with RevenueDot?

Yes. The payload shape, field names, signature header and retry schedule follow RevenueCat's webhooks (checked October 2026), so a handler written for RevenueCat works unchanged. Runnable receivers exist for Node.js, Next.js, Python and Go.

How many times does RevenueDot retry a failed webhook?

5 retries, after 5, 10, 20, 40 and 80 minutes, so 6 attempts in all. Only an HTTP 200 answer counts as delivered. After the last attempt the delivery is marked failed, and you can resend it from the delivery log.

Why is my webhook not arriving?

Check the delivery log for the HTTP status of each attempt. Common causes are an answer other than 200, a timeout over 60 seconds, a paused webhook, or an environment or event type filter that excludes the event. See the webhooks not arriving help page.

Sources: RevenueCat: webhooks · RevenueCat: webhook event types and fields. Webhooks is a trademark of its owner, used to describe compatibility. RevenueDot is not affiliated with or endorsed by it.

Get started

Send Webhooks every subscription event.

Start free on RevenueDot Cloud, free up to $10,000 a month in tracked revenue. Integrations are included on every plan.

Already have an account? Sign in · Prefer your own servers? Self-host free