What does the RevenueDot webhook event SUBSCRIPTION_PAUSED mean?

The RevenueDot webhook event SUBSCRIPTION_PAUSED means a Google Play subscription is set to pause and will not renew at the end of the current period.

Quick facts#

Event SUBSCRIPTION_PAUSED
Where RevenueDot webhook (event.type), RevenueCat's webhook format
Sent to Every enabled webhook whose filters match
Fields that are specific to it auto_resume_at_ms
What it means A Google Play subscription is scheduled to pause. It will not renew at the end of the period.

When it is sent#

  • The customer chose to pause in Google Play. It happens only on Google Play.
  • auto_resume_at_ms holds when the subscription resumes.
  • The customer still has access until the period ends. EXPIRATION with expiration_reason: SUBSCRIPTION_PAUSED follows.

What your server should do#

  1. Keep access until expiration_at_ms.
  2. Show the resume date from auto_resume_at_ms.
  3. Expect a RENEWAL when it resumes.
  4. Deduplicate on event.id: deliveries can repeat, because anything but HTTP 200 is retried after 5, 10, 20, 40 and 80 minutes.

Example#

JavaScript
import { createHmac, timingSafeEqual } from "node:crypto";

// X-RevenueCat-Webhook-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
function verify(rawBody, header, secret, toleranceSeconds = 300) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? "");
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > toleranceSeconds) return false;
  const expected = createHmac("sha256", secret).update(`${m[1]}.${rawBody}`).digest();
  return timingSafeEqual(expected, Buffer.from(m[2], "hex"));
}

// Sign a sample delivery the way RevenueDot does, then handle it.
const secret = "whsec_test_secret";
const rawBody = JSON.stringify({ api_version: "1.0", event: {"id":"66339910-3BFF-49F4-B873-D1283D673DE2","type":"SUBSCRIPTION_PAUSED","app_user_id":"user_1","product_id":"pro_monthly","auto_resume_at_ms":1793392914000} });
const t = Math.floor(Date.now() / 1000);
const header = `t=${t},v1=${createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")}`;

const seen = new Set(); // use a database table in production

function handle(rawBody, header) {
  if (!verify(rawBody, header, secret)) return "401 bad signature";
  const { event } = JSON.parse(rawBody);
  if (seen.has(event.id)) return "200 duplicate";
  seen.add(event.id);
  if (event.type !== "SUBSCRIPTION_PAUSED") return "200 ignored";
  return "keep access until the period ends, show resume date " + new Date(event.auto_resume_at_ms).toISOString().slice(0, 10);
}

console.log(handle(rawBody, header));

Run-checked: npm run check:snippets runs this snippet with Node and compares its output with show resume date 2026- (checked 2026-10-03).

How RevenueDot produces it#

diffSubscription returns SUBSCRIPTION_PAUSED when an auto-resume date appears on the stored purchase. For Google Play it is set from the pause schedule notification, while the subscription is still active, and again from the paused state.

Source#

Checked: 2026-10-03

Edit this page on GitHub ↗ View as Markdown Last updated