coupon.updated
A coupon's terms changed, with a field-by-field list of what moved.
When this fires
A creator changed a coupon's terms — its code, name, discount, caps, minimum, or window. Fires once per save, from the dashboard or the REST API.
It does not fire for an on/off flip. Switching a coupon on or off emits
coupon.activated or coupon.deactivated instead, because that
is what an automation actually wants to react to and burying it in a generic update would force
every consumer to diff active themselves.
Editing terms is dashboard and API only
The Telegram bot can create, inspect, switch off and delete a coupon, but not edit its terms — a multi-field edit is a form, not a chat. So this event originates from the dashboard or the API.
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.updated",
"created_at": "2026-11-24T16:41:11Z",
"api_version": "2026-05-01",
"project_id": "prj_01HX...",
"data": {
"coupon": {
"id": "cpn_01HX...",
"code": "BLACKFRIDAY",
"name": "Black Friday 2026",
"discount_type": "percentage",
"discount_value": "30.0000",
"currency_id": null,
"duration": "once",
"max_redemptions": 1000,
"max_redemptions_per_user": 1,
"minimum_amount": null,
"starts_at": "2026-11-27T00:00:00+00:00",
"expires_at": "2026-12-01T00:00:00+00:00",
"active": true
},
"changes": {
"discount_value": { "from": "25.0000", "to": "30.0000" },
"max_redemptions": { "from": 500, "to": 1000 }
}
}
}Field reference
| Field | Type | Notes |
|---|---|---|
data.coupon | object | The new state, in the same shape every coupon.* lifecycle event carries. See coupon.created for each key. |
data.changes | object | One entry per field that actually moved, keyed by field name, each { "from": …, "to": … }. |
changes only contains keys whose value differs. A save that changed nothing sends
"changes": {} — the event still fires, because the creator did press save.
Caveats
changescompares the snapshot, not the database columns. It covers exactly the keys indata.coupon, so a change to the coupon's plan restriction does not appear — that lives in a separate relation the snapshot deliberately omits. Re-readGET /v1/projects/{project}/coupons/{coupon}if you track which plans a code covers.fromandtokeep their original types. A decimal field gives you decimal strings, an integer field integers, a nullable fieldnull. Don't assume both sides are strings.- Changing the code does not migrate past redemptions. Redemptions already recorded stay attached to the coupon by id, so a renamed code keeps its history — but any subscriber who wrote the old string down now has a dead code.
- A change is not retroactive. Subscribers who already redeemed keep the terms they paid under; a reduced discount only affects checkouts from here on.
Related events
coupon.created— the same snapshot shape, on authoring.coupon.activated/coupon.deactivated— the on/off flip this event deliberately excludes.- Coupon events overview — back to family overview.
How is this guide?