- When it fires — in plain language, with the underlying transition.
- What it means — what just happened to your business.
- What you can do with it — concrete ideas creators use today.
- Full payload — a real example, ready to paste into your handler.
user_promotion.* events share the same nested objects (user, promotion). Only the top-level type, the status, and the timing fields change between transitions. The shared envelope is documented in Payload structure.
user_promotion.access_granted
A buyer just gained access to one of your promotions.
When it fires
- A buyer completed a one-off paid checkout.
- A buyer started a subscription (Stripe
customer.subscription.created). - A buyer claimed a free promotion.
- You granted access manually from your dashboard.
What it means
Someone is now an active member of this promotion. They can consume the content, attend the calls, or whatever the promotion unlocks. This is the moment your business gained a new customer for this product.What you can do with it
- Send a ”🎉 New sale” notification to your team in Discord or Slack with the buyer’s name, email, and the promotion title.
- Add the buyer to a Mailchimp / Brevo / ConvertKit list, tagged with the promotion they just bought, to enroll them into your onboarding sequence.
- Grant them a role in your Discord server, an access in a Notion workspace, or any other resource gated behind the promotion.
- Append a new row to a Google Sheet for your sales log.
Payload
Fields worth highlighting
statusisactive.start_dateis the exact moment access began.end_dateisnullfor open-ended subscriptions and ongoing access.promotion.typetells you whether this was afreeclaim, aone_timepurchase, or asubscriptionstart.promotion_payment_idisnullfor free promotions and manual grants.
user_promotion.canceled
A buyer’s access has been canceled.
When it fires
- The buyer canceled their subscription from their account.
- You canceled their access manually from your dashboard.
- Stripe reported the subscription as deleted (
customer.subscription.deleted).
What it means
This buyer no longer has access to the promotion. For a subscription, this is the equivalent of “they unsubscribed”. For a one-off, this is a manual revoke on your side.What you can do with it
- Drop a quiet note in your team’s Discord/Slack so you know someone churned and can reach out if you want.
- Remove the buyer’s tag in your email tool, or move them to a “former customer” segment for a win-back sequence.
- Revoke the role you granted them on your Discord server, or remove their Notion access.
- Update your churn dashboard.
Payload
Fields worth highlighting
statusiscanceled.end_dateis set to the exact moment access ended.- Same
idas the originalaccess_granted— this is the sameuser_promotion, just transitioned.
user_promotion.expired
A subscription term ended and was not renewed.
When it fires
- A subscription that had been stuck in
past_duewas finally given up on (Stripe stopped trying to charge it). - A fixed-term access reached its end date.
What it means
The buyer lost access because their subscription effectively ran out. This is different fromcanceled: the buyer didn’t choose to cancel, the access simply timed out (most often after several failed payment attempts).
What you can do with it
- Trigger a different email sequence than for
canceled— typically a “your subscription expired, here is the link to resubscribe” reminder rather than a win-back. - Remove their access in external tools, exactly like for
canceled. - Log the expiration in your dashboard so you can monitor your dunning success rate.
Payload
Fields worth highlighting
statusisexpired.end_dateis the moment access actually ended (typically after the dunning window closed).
user_promotion.suspended
A recurring payment failed and the buyer’s access is paused.
When it fires
- A subscription invoice could not be charged. Stripe reports the subscription as
past_dueand Boble suspends access.
What it means
The buyer still exists as a customer, but their access is temporarily blocked until the payment succeeds. This is your dunning window — most of the time, payments recover on the second or third attempt and you’ll receive a freshaccess_granted once that happens. If they never recover, you’ll eventually receive an expired.
This event is fired only on the transition into
past_due. A successful recovery does not fire suspended again — it fires access_granted.What you can do with it
- Email the buyer with a link to update their payment method. This is one of the highest-ROI things you can wire on a webhook.
- Pause their access in external tools, but don’t revoke it permanently — they will most likely come back.
- Add them to a “dunning” segment in your CRM for follow-up.
Payload
Fields worth highlighting
statusispast_due(notsuspended—suspendedis the event name, the underlying status ispast_due).end_datestaysnullbecause the access is paused, not ended.
webhook.test
A test event you triggered manually from your dashboard.
When it fires
You clicked the “Send test event” button (or hit the test API). The test event is dispatched even if your endpoint is disabled, so you can verify connectivity before flipping the switch on.What it means
Nothing happened on a real buyer. This is a smoke test to confirm that:- Your endpoint is reachable.
- Your handler accepts a Boble webhook delivery.
- Your signature verification logic works.
What you can do with it
- Use it after deploying changes to your handler, to confirm everything still works without waiting for a real sale.
- Use it once at integration time to validate your verification code.
Payload
Fields worth highlighting
data.objectis intentionally minimal — no buyer, no promotion, just a marker that this is a test.livemodeisfalseoutside production.- A test delivery still appears in your event log so you can inspect what your endpoint replied.