Back to Documentation

Webhooks

Receive real-time notifications when purchases, cancellations, and payment events occur

When to Use Webhooks

For most web app integrations, the Auth SDK is recommended — it handles authentication and access control without webhooks. Use webhooks when you need to:

  • Sync purchase data to your own database
  • Trigger emails or notifications on purchase events
  • Integrate with third-party services (CRMs, analytics)
  • Run custom business logic on subscription changes

Webhook Events

purchase.completed

Sent when a customer completes a purchase (one-time or subscription).

{
  "event": "purchase.completed",
  "data": {
    "purchaseId": "purch_abc123",
    "buyerEmail": "customer@example.com",
    "buyerName": "John Doe",
    "productId": "prod_xyz789",
    "productName": "Pro Plan",
    "amountCents": 2999,
    "currency": "usd",
    "isSubscription": true,
    "status": "active",
    "currentPeriodEnd": "2024-02-15T00:00:00Z"
  },
  "timestamp": "2024-01-15T12:00:00Z"
}
purchase.cancelled

Sent when a subscription is cancelled.

{
  "event": "purchase.cancelled",
  "data": {
    "purchaseId": "purch_abc123",
    "buyerEmail": "customer@example.com",
    "productId": "prod_xyz789",
    "productName": "Pro Plan",
    "status": "cancelled"
  },
  "timestamp": "2024-01-20T12:00:00Z"
}
payment.failed

Sent when a recurring payment fails.

{
  "event": "payment.failed",
  "data": {
    "purchaseId": "purch_abc123",
    "buyerEmail": "customer@example.com",
    "productId": "prod_xyz789",
    "productName": "Pro Plan",
    "status": "past_due"
  },
  "timestamp": "2024-01-15T12:00:00Z"
}
plan.switched

Sent when a customer upgrades or downgrades their subscription.

{
  "event": "plan.switched",
  "data": {
    "purchaseId": "purch_abc123",
    "buyerEmail": "customer@example.com",
    "previousProductId": "prod_basic",
    "previousProductName": "Basic Plan",
    "newProductId": "prod_pro",
    "newProductName": "Pro Plan",
    "amountCents": 4999,
    "currency": "usd"
  },
  "timestamp": "2024-01-15T12:00:00Z"
}

Security

All webhooks are signed with HMAC-SHA256. Verify using the x-rev-signature header and your webhook secret.

Signature Verification

import crypto from "crypto";

function verifySignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  const sigBuffer = Buffer.from(signature);
  const expectedBuffer = Buffer.from(expected);
  if (sigBuffer.length !== expectedBuffer.length) return false;
  return crypto.timingSafeEqual(sigBuffer, expectedBuffer);
}

Handler Example

// app/api/webhooks/revnu/route.ts
import crypto from "crypto";
import { NextResponse } from "next/server";

function verifySignature(payload: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

export async function POST(request: Request) {
  const payload = await request.text();
  const signature = request.headers.get("x-rev-signature") ?? "";

  if (!verifySignature(payload, signature, process.env.REV_WEBHOOK_SECRET!)) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  const event = JSON.parse(payload);

  switch (event.event) {
    case "purchase.completed":
      // Sync to your database, send welcome email, etc.
      break;
    case "purchase.cancelled":
      // Update your records, revoke access
      break;
    case "payment.failed":
      // Notify the user, pause access
      break;
    case "plan.switched":
      // Update user's plan in your system
      break;
  }

  return NextResponse.json({ received: true });
}

Retry Policy

  • Webhooks timeout after 10 seconds
  • Failed deliveries retry up to 4 times with exponential backoff
  • Retry schedule: 1 minute, 5 minutes, 30 minutes, 2 hours
  • Each webhook includes a unique event ID for idempotency

Checkout Redirect URLs

Configure where customers go after checkout:

  • Success URL — redirect after successful payment
  • Cancel URL — redirect if they cancel checkout

Use {CHECKOUT_SESSION_ID} as a placeholder to receive the Stripe session ID.

Dashboard Configuration

  1. 1. Go to Dashboard → Webhooks
  2. 2. Enter your webhook endpoint URL (must be HTTPS)
  3. 3. Click Test to verify your endpoint
  4. 4. Select which events to receive

Environment Variables

# Add to your .env.local
REV_WEBHOOK_SECRET=whsec_your_webhook_secret

Find your webhook secret in Dashboard → Webhooks after configuring your endpoint.