Providers / April 25, 2026

How to receive Polar webhooks reliably (subscriptions, orders, customers)

Polar webhook flow showing subscription and order events delivered to a billing agent

Polar fires webhooks for billing events in your project: subscriptions created, updated, cancelled, orders placed and refunded, customer changes, benefit grants, and more. They're how your application stays in sync with billing without polling Polar's API.

This guide covers what Polar sends, how to verify it (Standard Webhooks-compatible), and what production reliability requires.

What Polar sends

Polar webhooks are JSON POST requests. A subscription.created event:

{
  "type": "subscription.created",
  "data": {
    "id": "sub_01HM3K8N2P9X4ABCD",
    "status": "active",
    "customer_id": "cus_01HM3K8N2P9X4ABCD",
    "product_id": "prod_01HM3K8N2P9X4ABCD",
    "price_id": "price_01HM3K8N2P9X4ABCD",
    "current_period_start": "2026-04-25T14:30:00Z",
    "current_period_end": "2026-05-25T14:30:00Z",
    "cancel_at_period_end": false,
    "created_at": "2026-04-25T14:30:00Z"
  }
}

Headers (Standard Webhooks spec):

webhook-id: msg_2NxV5y...
webhook-timestamp: 1714000000
webhook-signature: v1,abc123...
content-type: application/json

Common Polar event types:

  • subscription.created, subscription.updated, subscription.canceled, subscription.uncanceled
  • subscription.active, subscription.revoked
  • order.created, order.refunded, order.updated
  • customer.created, customer.updated, customer.deleted
  • customer.state_changed (subscription state transitions per customer)
  • benefit.created, benefit_grant.created, benefit_grant.updated, benefit_grant.revoked
  • pledge.created, pledge.updated (for funding model)

The data.id is the Polar resource ID — use the Standard Webhooks webhook-id header for idempotency.

Polar's signature scheme (Standard Webhooks)

Polar follows the Standard Webhooks spec. The signature is HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{raw-body}, base64-encoded, prefixed with v1,:

signature = "v1," + base64(hmac_sha256(secret, webhook_id + "." + webhook_timestamp + "." + raw_body))

Verification:

  1. Read the raw body before any JSON parsing
  2. Reject if now - webhook_timestamp > 300 seconds (replay protection)
  3. Recompute the signature; the header may contain multiple space-separated values during secret rotation — accept any match
  4. Compare constant-time

The standardwebhooks and svix libraries handle this for you in every supported language.

Polar's retry policy

Polar retries failed webhooks (non-2xx, or no response within their delivery infrastructure's timeout) with exponential backoff for several hours. Failed deliveries are visible in the Polar dashboard's webhook log.

This means:

  • Acknowledge fast — return 2xx in under one second
  • Be idempotent on webhook-id — duplicates will arrive during retries
  • Have a recovery story — past Polar's window, manual replay from the dashboard is the only built-in fallback (or backfill from the Polar API)

DIY: minimal Polar webhook handler in Node

import { Webhook } from 'standardwebhooks'

const secret = process.env.POLAR_WEBHOOK_SECRET!

export async function POST(req: Request) {
  const headers = {
    'webhook-id': req.headers.get('webhook-id')!,
    'webhook-timestamp': req.headers.get('webhook-timestamp')!,
    'webhook-signature': req.headers.get('webhook-signature')!,
  }
  const body = await req.text()

  let event
  try {
    event = new Webhook(secret).verify(body, headers)
  } catch {
    return new Response('Bad signature', { status: 400 })
  }

  // Idempotency on webhook-id
  // ...

  queueWork(event)

  return new Response('ok', { status: 200 })
}

Production needs more:

  • A persistence layer for webhook-id idempotency
  • A queue for async work
  • Replay path for events past Polar's retry window
  • Routing — subscription.* to your entitlements service, order.* to fulfillment, customer.* to the CRM
  • Strict ordering for subscription event sequences (created → updated → canceled in order)

Hooksbase: receive Polar webhooks without rebuilding the rest

Polar isn't one of Hooksbase's five pre-verified provider packs (Stripe, GitHub, Clerk, Slack, Resend), and Hooksbase does not forward Polar's original Standard Webhooks headers to your destination. If you need Polar signature verification, verify it in a small pre-ingest forwarder, then post the verified raw body to Hooksbase with the bearer ingest secret.

Setup:

  1. Create a webhook in Hooksbase with your application URL as the destination
  2. Add your verification forwarder URL to your Polar project's webhook settings
  3. In the forwarder, verify the Standard Webhooks signature, then POST the same raw body to the Hooksbase ingest URL with Authorization: Bearer <ingest secret>

You get:

  • Acknowledge in milliseconds to Polar regardless of how slow your downstream is
  • Idempotency for your destination — every dispatch includes a unique webhook-id header you can dedupe on across retries
  • Retries with exponential backoff to your endpoint after Hooksbase accepts the event
  • Strict FIFO ordering if you turn it on (Pro+) — useful for subscription state sequences that must process in order
  • Routing rules by event type — send subscription.created to provisioning, order.refunded to support escalation, customer.created to CRM sync
  • Payload transforms — flatten Polar's structure to the shape your downstream expects
  • Deterministic replay — re-run a failed delivery with the same payload bytes while the payload is retained
  • Delivery history and DLQ

Common Polar webhook use cases for AI agents

  • Provisioning agentsubscription.created events trigger an agent that provisions resources, sends the welcome email, and schedules onboarding tasks
  • Failed payment recovery agentsubscription.updated events with status changes trigger an agent that runs your dunning playbook (notify, retry, downgrade, win-back)
  • Refund triage agentorder.refunded events trigger an agent that classifies the refund reason and surfaces patterns to the team
  • Entitlements sync agentsubscription.active and subscription.revoked events trigger an agent that updates downstream entitlements (feature flags, API quotas, dashboard access)
  • Benefit grant agentbenefit_grant.created events trigger an agent that delivers the promised benefit (Discord role, GitHub team add, license key dispatch)

Where to go next

Start free at app.hooksbase.com.

Related guides