Back to the RevenueCat homepage
RevenueCat SDKGoogle Play Billing

Chapter 10: Webhooks

Receiving subscription event notifications from scratch means setting up a Cloud Pub/Sub topic, creating a push subscription, deploying an endpoint to receive messages, decoding base64 payloads, dispatching by notification type, and implementing idempotency to handle duplicate deliveries. There are 22 different notification types, each mapping to a specific subscription state change.

RevenueCat webhooks replace all of that. You get one webhook endpoint to configure, and one normalized event schema across all stores.

Setting Up Webhooks

In the RevenueCat dashboard, go to Integrations → Webhooks. Add your endpoint URL. RevenueCat sends an HTTP POST with a JSON payload for every relevant event.

There is no Cloud Pub/Sub to configure. No base64 decoding. No service account credentials for your endpoint. RevenueCat sends standard HTTPS POST requests with a X-RevenueCat-Signature header for verification.

The Event Schema

Every RevenueCat webhook has this shape:

json
{
  "api_version": "1.0",
  "event": {
    "id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
    "type": "INITIAL_PURCHASE",
    "app_user_id": "user_12345",
    "aliases": ["$RCAnonymousID:abc123"],
    "product_id": "premium_monthly",
    "period_type": "NORMAL",
    "purchased_at_ms": 1700000000000,
    "expiration_at_ms": 1702592000000,
    "store": "PLAY_STORE",
    "environment": "PRODUCTION",
    "entitlement_ids": ["pro_access"],
    "transaction_id": "GPA.1234-5678-9012-34567",
    ...
  }
}

The key field is entitlement_ids, RevenueCat has already mapped the product to your entitlement. Your webhook handler does not need to know which product maps to which access level. It just checks which entitlements were affected.

Event Types

RevenueCat event

Equivalent Google RTDN

INITIAL_PURCHASE

SUBSCRIPTION_PURCHASED (4)

RENEWAL

SUBSCRIPTION_RENEWED (2)

CANCELLATION

SUBSCRIPTION_CANCELED (3)

UNCANCELLATION

SUBSCRIPTION_RESTARTED (7)

BILLING_ISSUE

SUBSCRIPTION_IN_GRACE_PERIOD (6) or SUBSCRIPTION_ON_HOLD (5)

SUBSCRIBER_ALIAS

N/A (identity merge)

EXPIRATION

SUBSCRIPTION_EXPIRED (13)

PRODUCT_CHANGE

SUBSCRIPTION_ITEMS_CHANGED (17)

TRANSFER

N/A (user ID transfer)

A Minimal Webhook Handler

kotlin
// Server-side (example in Kotlin/Ktor)
post("/revenuecat/webhook") {
    val body = call.receiveText()
    val signature = call.request.headers["X-RevenueCat-Signature"]

    if (!verifySignature(body, signature, webhookSecret)) {
        call.respond(HttpStatusCode.Unauthorized)
        return@post
    }

    val event = Json.decodeFromString<RevenueCatEvent>(body)

    when (event.type) {
        "INITIAL_PURCHASE", "RENEWAL", "UNCANCELLATION" ->
            db.grantEntitlements(event.appUserId, event.entitlementIds)
        "EXPIRATION" ->
            // Subscription has ended, revoke access immediately
            db.revokeEntitlements(event.appUserId, event.entitlementIds)
        "CANCELLATION" ->
            // User has opted out of renewal but still has access until expirationAtMs.
            // Schedule revocation, do NOT revoke immediately.
            db.scheduleEntitlementRevocation(event.appUserId, event.entitlementIds, event.expirationAtMs)
        "BILLING_ISSUE" ->
            db.flagBillingIssue(event.appUserId)
    }

    call.respond(HttpStatusCode.OK)
}

Idempotency

RevenueCat may deliver the same event more than once. Use event.id as an idempotency key. If you have already processed an event with that ID, return 200 without re-processing.

Retries

RevenueCat retries failed webhook deliveries with exponential backoff. Return a 2xx response immediately if you received the event successfully, even if your processing is asynchronous. If your handler takes too long, RevenueCat may time out and retry.

Prefer building from scratch?

The Google Play Billing Handbook covers the same topics with raw BillingClient, Developer API, and RTDNs.

Related chapters

  • Chapter 9: Backend Architecture

    You don't need to build a verification server. RevenueCat already is your verification server.

    Learn more
  • Chapter 11: Subscription States

    Seven complex subscription states are resolved to just one simple boolean check: isActive.

    Learn more
  • Chapter 12: Payment Recovery

    In-app messages are shown automatically by default. Just two lines needed to detect grace period.

    Learn more
Webhooks | RevenueCat