Skip to main content

Webhooks

The one endpoint the billing gateway calls back, and how it authenticates without a JWT.

POST /webhooks/billing

This is the only unauthenticated write endpoint in the API — because no Auth0 session or brs_ token exists on the caller's side. Authenticity comes from an HMAC signature instead, verified before the payload is parsed (BillingWebhookVerifier from @infinitescape/billing-gateway-client, CONST-STACK-09: no Stripe SDK, no payment code, in this product at all). The handler always answers 200 for anything it settles, including an event it does not act on — so the gateway stops retrying — and every apply is idempotent by event id, so a redelivered webhook is a no-op rather than a double-charge.

Response
{ "ok": true }

Events acted on

The handler keys off the gateway's canonical event-type constants rather than hardcoded strings, so a wire format change on the gateway's side cannot silently drop an event here. These are the ones that change plan entitlement (cached locally in D1, per CONST-STACK-09 — the gateway is the source of truth, this is a read cache):

Canonical event typeEffect
SUBSCRIPTION_ACTIVATEDMoves the organization onto the plan tier the checkout was for.
SUBSCRIPTION_UPDATEDApplies a plan or seat change made from the billing portal.
SUBSCRIPTION_CANCELEDMoves the organization back to the free tier at period end.
SUBSCRIPTION_PAUSEDSuspends entitlements without deleting the subscription.
SUBSCRIPTION_RESUMEDRestores entitlements for a paused subscription.
SUBSCRIPTION_TRIAL_WILL_ENDDrives the PastDue / trial-ending banner in the app.

Any other event type — an invoice or a payment event, for instance — is acknowledged with 200 and otherwise ignored: it carries no entitlement change this product needs to react to.

What it is not

This endpoint never appears in the API reference's authenticated groups because it answers to the billing gateway, not to a user or a CLI. If you are looking for the endpoint the brs CLI or the PR-gate Action call, see API reference or PR gate instead.