diff --git a/apps/docs/mintlify/documentation/webhooks.mdx b/apps/docs/mintlify/documentation/webhooks.mdx index 076a5373b..d4d56e580 100644 --- a/apps/docs/mintlify/documentation/webhooks.mdx +++ b/apps/docs/mintlify/documentation/webhooks.mdx @@ -7,10 +7,6 @@ With Autumn, you don't need webhooks for managing billing — subscription state However, webhooks can still be useful for specific use cases where you want to trigger actions in your own systems. - -Webhooks are currently in beta. Please reach out to us on [Discord](https://discord.gg/STqxY92zuS) or email us at support@useautumn.com to enable webhooks for your account. - - ## Use Cases While Autumn handles billing complexity for you, webhooks are helpful for: @@ -22,44 +18,117 @@ While Autumn handles billing complexity for you, webhooks are helpful for: ## Available Events -### customer.products.updated +### billing.updated -Fired when a customer's product or plan changes. The `scenario` field indicates what type of change occurred. +Fired whenever a customer's plans change — new subscriptions, upgrades, downgrades, etc. Each event carries a `plan_changes` array describing exactly what happened to each affected plan. -| Scenario | Description | -|----------|-------------| -| `new` | Customer subscribed to a new product | -| `upgrade` | Customer upgraded to a higher-tier plan | -| `downgrade` | Customer downgraded to a lower-tier plan | -| `renew` | Subscription renewed for another billing period | -| `cancel` | Subscription was canceled | -| `expired` | Subscription has expired | -| `past_due` | Payment is past due | -| `scheduled` | A plan change has been scheduled | +| Action | Description | +|--------|-------------| +| `activated` | A plan is now active on the customer (newly attached, or a previously scheduled plan reached its start date) | +| `scheduled` | A plan has been queued to start at a future date | +| `updated` | A plan's state changed in place (cancellation set or cleared, `past_due` flipped, items added or removed) | +| `expired` | A plan ended and is no longer in effect | -**Example payload:** +Each entry also includes the `subscription` (or `purchase` for one-off products) after the change, and `previous_attributes` holding the prior values of any fields that were updated. For instance, if a plan was canceled at period end: ```json expandable { - "type": "customer.products.updated", + "action": "updated", + "subscription": { + "plan_id": "pro", + "status": "active", + "past_due": false, + "started_at": 1759248000000, + "canceled_at": 1761840000000, + "expires_at": 1764432000000, + "trial_ends_at": null, + "current_period_start": 1761840000000, + "current_period_end": 1764432000000 + }, + "previous_attributes": { + "canceled_at": null, + "expires_at": null + }, + "item_changes": [] +} +``` + +The top-level `tags` array surfaces optional reason tags describing why the event fired: + +| Tag | When | +|-----|------| +| `trial_ended` | A trial just ended (Stripe subscription transition or the trial-expiry cron) | +| `phase_changed` | A Stripe subscription schedule phase advanced | + +**Example payload (upgrade from `free` to `pro`):** + +```json expandable +{ + "type": "billing.updated", "data": { - "scenario": "new", - "customer": { - "id": "user_123", - "name": "John Doe", - "email": "john@example.com", - "balances": { ... }, - "subscriptions": [ ... ] - }, - "updated_product": { - "id": "pro_plan", - "name": "Pro Plan", - "features": [ ... ] - } + "object": "billing.updated", + "customer_id": "user_123", + "plan_changes": [ + { + "action": "activated", + "subscription": { + "plan_id": "pro", + "status": "active", + "past_due": false, + "started_at": 1761840000000, + "canceled_at": null, + "expires_at": null, + "trial_ends_at": null, + "current_period_start": 1761840000000, + "current_period_end": 1764432000000 + }, + "previous_attributes": null, + "item_changes": [] + }, + { + "action": "expired", + "subscription": { + "plan_id": "free", + "status": "expired", + "past_due": false, + "started_at": 1759248000000, + "canceled_at": 1761840000000, + "expires_at": 1761840000000, + "trial_ends_at": null, + "current_period_start": null, + "current_period_end": null + }, + "previous_attributes": { "status": "active" }, + "item_changes": [] + } + ], + "tags": [] } } ``` +For entity-scoped events, the payload will also include an `entity_id`: + +```json expandable +{ + "type": "billing.updated", + "data": { + "object": "billing.updated", + "customer_id": "user_123", + "entity_id": "team_456", + "plan_changes": [ ... ], + "tags": [] + } +} +``` + +**Common patterns:** + +- **Sync Autumn state back to your DB** — listen for every `billing.updated` and persist each `plan_changes` entry's `subscription` snapshot keyed by `customer_id` (+ `entity_id` if set). +- **Notify on upgrades** — filter for entries with `action: "activated"`. For "upgrade from previous plan" specifically, pair it with an `action: "expired"` entry on the same event. +- **Detect cancellations** — filter for entries where `previous_attributes.canceled_at === null` (a cancellation was just set) or `previous_attributes.canceled_at` is a number (an uncancel). +- **Trial-end emails** — filter for `tags.includes("trial_ended")`. + ### balances.limit_reached Fired when a customer hits a usage limit for a feature. A limit can be the included allowance, a max purchase cap, or a spend limit. @@ -183,7 +252,7 @@ Fired when a customer crosses a configured usage alert threshold. Usage alerts l ## Setup -Once webhooks are enabled for your account, you can configure your webhook endpoints in the Autumn dashboard: +Configure your webhook endpoints in the Autumn dashboard: