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_msholds when the subscription resumes.- The customer still has access until the period ends.
EXPIRATIONwithexpiration_reason: SUBSCRIPTION_PAUSEDfollows.
What your server should do#
- Keep access until
expiration_at_ms. - Show the resume date from
auto_resume_at_ms. - Expect a
RENEWALwhen it resumes. - 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.
Related#
- What does the RevenueDot webhook event EXPIRATION mean?
- What does the RevenueDot webhook event RENEWAL mean?
- What does the RevenueDot webhook event CANCELLATION mean?
- SUBSCRIPTION_PAUSED fields and a recorded example payload
- Webhooks guide: set up, verify and test
Source#
- RevenueDot docs: which webhook events RevenueDot sends
- RevenueCat: webhook event types and fields (the format RevenueDot follows)
- RevenueDot core: events.ts (the event types and the rules that derive them)
- RevenueDot server: services/events.ts (how an event is recorded and delivered)
- RevenueDot server: services/purchases.ts (where store changes become events)
Checked: 2026-10-03