Feature

Subscription webhooks for iOS and Android purchases in RevenueCat's format

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.

RevenueCat's format

The same field names and values, so existing handlers keep working.

HMAC signature

Verify X-RevenueCat-Webhook-Signature against the raw body. A timestamp stops replays.

Retries and a log

Six attempts in all, each with its status, duration and error, and a Retry button.

Test events

Send a TEST event, or make real events with Test Store scenarios such as renewal, cancel and refund.

Steps

How to receive and verify a RevenueDot webhook

  1. 01

    Add a webhook

    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.

  2. 02

    Keep the signing secret

    The whsec_ signing secret is shown once, in the answer. Store it as a secret in your backend.

  3. 03

    Verify the signature

    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.

  4. 04

    Answer 200 and deduplicate

    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.

  5. 05

    Send a test event

    Use Send test event in the dashboard. It sends a purchase-shaped TEST event signed like the others.

What does a webhook delivery look like?

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.

A delivery (shortened)HTTP
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"}}

How do you verify the signature in Node.js?

Runnable receivers for Node.js with Express, Next.js, Python with FastAPI and Go are in the examples repository.

verify.jsNode.js
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);
}

Which webhook events does RevenueDot send?

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.

  • Lifecycle: INITIAL_PURCHASE, RENEWAL, CANCELLATION, UNCANCELLATION, NON_RENEWING_PURCHASE, SUBSCRIPTION_PAUSED, EXPIRATION, BILLING_ISSUE, PRODUCT_CHANGE, SUBSCRIPTION_EXTENDED, REFUND_REVERSED and TRANSFER.
  • Price increases: PRICE_INCREASE_CONSENT_REQUIRED and PRICE_INCREASE_CONSENT_APPROVED.
  • More: VIRTUAL_CURRENCY_TRANSACTION, EXPERIMENT_ENROLLMENT, PURCHASE_REDEEMED and TEST. SUBSCRIBER_ALIAS goes only to webhooks whose filter names it.
  • Opt-in RevenueDot types for web funnels: FUNNEL_VIEWED, FUNNEL_STEP_COMPLETED and FUNNEL_PURCHASE.
  • Never sent: TEMPORARY_ENTITLEMENT_GRANT, because RevenueDot never grants access it has not verified, and INVOICE_ISSUANCE, which only RevenueCat Billing issues.

FAQ

Webhooks: questions people ask

How do I verify a RevenueDot webhook signature?

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.

Do RevenueCat webhook handlers work with RevenueDot?

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.

What happens when my webhook endpoint is down?

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.

Can I receive each event only once?

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.

How do I test webhooks without a real purchase?

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

Run subscriptions without the revenue share.

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