coupon.redeemed
A subscriber's discounted payment settled and the redemption was recorded.
When this fires
A payment that carried a coupon settled. The reservation taken at checkout has been converted into a recorded redemption, and the money has actually moved.
This is the money event in the family. It is the only coupon.* event that carries amounts, and
the only one that tells you the code was really used rather than merely offered.
It fires on settlement, not on apply
A subscriber typing a code into checkout does not emit anything. If they
abandon the payment, the hold expires and no event ever fires — which is
exactly why a coupon.redeemed can be trusted as revenue and a "code applied"
signal could not.
Required ability
project-coupon:view — token must carry this at mint time to subscribe an endpoint to this event.
Payload
{
"id": "evt_01HX...",
"type": "coupon.redeemed",
"created_at": "2026-11-28T14:07:52Z",
"api_version": "2026-05-01",
"project_id": "prj_01HX...",
"data": {
"coupon_id": "cpn_01HX...",
"code": "BLACKFRIDAY",
"subscription_id": "sub_01HX...",
"subscriber_id": "usr_01HX...",
"plan_id": "pln_01HX...",
"original_amount": "40.0000",
"discount_amount": "10.0000",
"final_amount": "30.0000",
"currency_id": "cur_01HX...",
"redemptions_count": 137,
"max_redemptions": 500
}
}Field reference
| Field | Type | Notes |
|---|---|---|
id | string ULID | Unique event id, prefixed evt_. Use for idempotent processing. |
type | string | Always coupon.redeemed for this event. |
created_at | ISO 8601 timestamp | Server-side emission time. |
api_version | string | Webhook API contract version. |
project_id | string ULID | Project the coupon belongs to. |
data.coupon_id | string ULID | The coupon that was redeemed. |
data.code | string | The code as stored — uppercase. |
data.subscription_id | string ULID | The subscription the discounted payment bought. |
data.subscriber_id | string ULID | The subscriber. Resolve name and contact from the API — no payload carries them. |
data.plan_id | string ULID | The plan bought. |
data.original_amount | decimal string | List price before the discount, in currency_id. |
data.discount_amount | decimal string | What came off. |
data.final_amount | decimal string | What was actually charged. original_amount − discount_amount. |
data.currency_id | string ULID | Currency all three amounts are in. |
data.redemptions_count | integer | Total settled redemptions of this coupon, read fresh at emission. |
data.max_redemptions | integer | null | The total cap. null means unlimited. |
This is the amount your reporting should use
final_amount is what the payment provider charged and what Subscriby's own transaction fee is
calculated from. If you mirror revenue into a spreadsheet or a warehouse, use final_amount — not
the plan's list price, and not original_amount.
Fires alongside payment.succeeded and
subscription.created for the same purchase. All three
describe one checkout from three angles; only this one knows a discount was involved.
Caveats
- Only the first payment is discounted. Coupons are first-payment-only today, so a renewal on
the same subscription charges list price and emits no coupon event. Do not carry
discount_amountforward into MRR. redemptions_countis read fresh, so it can jump. Under concurrent checkouts two events may report counts that are not consecutive. Treat it as a gauge, not a sequence number, and never derive ordering from it.- Amounts are decimal strings in the coupon redemption's currency, which is the plan's currency — not necessarily your dashboard's display currency. Convert before summing across plans.
- A redemption can arrive after the code was switched off, because a checkout already in flight is allowed to complete. This is expected, not a leak.
- Percentage and fixed discounts are both rounded to the currency's precision before charging,
so
discount_amountis the rounded figure that actually moved, not a raw percentage oforiginal_amount.
Related events
coupon.exhausted— emitted immediately after this one when the redemption takes the last use.payment.succeeded— the payment itself.subscription.created— the subscription it bought.- Coupon events overview — back to family overview.
How is this guide?