Reliability

Stripe webhooks for access control

Understand inbound Stripe webhooks, Licenzy outbound webhooks, webhook retries, and why access should change only after webhook processing updates entitlements.

Start free in test mode. Go live when you're ready.

Category: Operations5 sections

Inbound Stripe webhooks are the access-control source of truth

This page is about delivery boundaries. Stripe inbound webhooks and Licenzy outbound webhooks solve different problems and should not be treated as the same event stream.

In a Licenzy checkout flow, the browser redirect is not payment confirmation. Your app starts checkout through POST /v1/checkout/session, Stripe later sends billing events to Licenzy, and Licenzy updates entitlement state from webhook processing. That updated state is what your backend should use for access decisions.

In the confirmed integration flow, you do not forward Stripe events manually. Stripe calls Licenzy webhook endpoints directly:

  • /v1/webhooks/stripe/tenant
  • /v1/webhooks/stripe/billing

Licenzy verifies the Stripe signature, enforces idempotency using stored Stripe event tracking, and updates entitlements from those webhook events.

Do not grant access from success_url
A success_url visit is a browser event, not an authoritative payment event. Grant access only after webhook processing updates Licenzy entitlement state, then verify access from your backend.

Retries, idempotency and async delivery

Webhooks are asynchronous and may be retried. Your integration should assume that a checkout success page can load before billing finalization has been processed and that webhook events can be delivered more than once.

  • Stripe webhook endpoints are called by Stripe, not by your application.
  • Webhook processing must be idempotent because retries can happen.
  • Access checks should tolerate a short delay while entitlement state is updated.
  • Post-payment screens should poll or refresh backend access state instead of trusting the redirect.

For the user-facing verification path after payment, see how to check user access after Stripe payment.

Outbound webhooks from Licenzy

Licenzy outbound webhooks are a separate mechanism from Stripe inbound webhooks. They are delivered asynchronously, signed with Licenzy HMAC headers, and should be treated as Licenzy's own event stream rather than raw forwarded Stripe event payloads.

Outbound webhook delivery can be configured and inspected through runtime webhook endpoints. That includes endpoint management, delivery inspection, requeue and resend operations.

  • purchase.finalized
  • entitlement.updated
  • subscription.updated
  • subscription.deleted
  • invoice.payment_succeeded
  • invoice.payment_failed
  • usage.consumed
Integration rule
Do not treat outbound Licenzy webhooks as mirrored Stripe events. Let Stripe talk to Licenzy, then consume Licenzy runtime state and Licenzy outbound events as separate concerns. For the access decision that happens after billing finalization, see how to check user access after Stripe payment.

What not to do

  • Do not grant access when checkout is created.
  • Do not grant access from a success_url page load.
  • Do not have your app call the inbound Stripe webhook endpoints directly.
  • Do not treat outbound Licenzy webhooks as raw forwarded Stripe events.
  • Do not build access from frontend state when entitlement-backed access checks are available.