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": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"coupon_id": "4b9d3e08-a1f6-4275-9c83-7e01d6a2f594",
"code": "BLACKFRIDAY",
"subscription_id": "5b7e2d40-1a86-4c39-97f2-e83d0b16c5a4",
"subscriber_id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80",
"plan_id": "c4e82f16-93a7-4d5b-b81c-6e0f27a94d3b",
"original_amount": "40.0000",
"discount_amount": "10.0000",
"final_amount": "30.0000",
"currency_id": "8f27a0d4-63be-4915-8c07-1a5d9e34b628",
"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 UUID | Project the coupon belongs to. |
data.coupon_id | string UUID | The coupon that was redeemed. |
data.code | string | The code as stored — uppercase. |
data.subscription_id | string UUID | The subscription the discounted payment bought. |
data.subscriber_id | string UUID | The subscriber. Resolve name and contact from the API — no payload carries them. |
data.plan_id | string UUID | 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 UUID | Currency all three amounts are in. |
data.redemptions_count | integer | null | Settled redemptions plus reservations not yet released — a denormalised counter bumped when a checkout takes a hold, not when it settles. Read fresh at emission; null only if the coupon row disappeared between settlement and 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.activated 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.activated— the subscription it bought.- Coupon events overview — back to family overview.
How is this guide?