coupon.created
A new coupon code was authored in a project.
When this fires
A creator authored a new coupon code over the REST API (POST /v1/projects/{project}/coupons) or
via the create_coupon MCP tool. Coupons authored in the dashboard or through the Telegram bot do
not currently emit this event — those surfaces write through the service layer directly.
It fires on authoring, not on availability. A coupon created with a future starts_at fires
this immediately and only becomes redeemable later, so do not read this as "a discount is now
live" — check starts_at and active.
There is no separate event for a coupon becoming redeemable when its
starts_at passes. Schedule against starts_at from this payload if you need
that moment.
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.created",
"created_at": "2026-11-20T09:14:02Z",
"api_version": "2026-05-01",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"coupon": {
"id": "4b9d3e08-a1f6-4275-9c83-7e01d6a2f594",
"code": "BLACKFRIDAY",
"name": "Black Friday 2026",
"discount_type": "percentage",
"discount_value": "25",
"currency_id": null,
"duration": "once",
"max_redemptions": 500,
"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
}
}
}Field reference
| Field | Type | Notes |
|---|---|---|
id | string ULID | Unique event id, prefixed evt_. Use for idempotent processing. |
type | string | Always coupon.created 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's UUID. No prefix — only the envelope id is prefixed. |
data.coupon.code | string | The redeemable string. Always uppercase — stored and compared that way. |
data.coupon.name | string | Creator-facing label. Never shown to subscribers. |
data.coupon.discount_type | percentage | fixed | Whether discount_value is a percent or an amount. |
data.coupon.discount_value | decimal string | The value exactly as submitted — this event is emitted before the row is read back, so it is "25", not "25.0000". The other lifecycle events re-read the row and carry four decimal places. Parse as a decimal, never a float. |
data.coupon.currency_id | string UUID | null | null on a percentage coupon; set on a fixed-amount one. |
data.coupon.duration | once | Which charges the discount applies to. Only once — the first payment — is authorable. |
data.coupon.max_redemptions | integer | null | Total cap across everyone. null means unlimited. |
data.coupon.max_redemptions_per_user | integer | Per-subscriber cap. Always at least 1. |
data.coupon.minimum_amount | decimal string | null | Order floor in the plan's own currency. null means no floor. As with discount_value, it is the value exactly as submitted here — "10", not "10.0000". |
data.coupon.starts_at | ISO 8601 | null | When it becomes redeemable. null means immediately. |
data.coupon.expires_at | ISO 8601 | null | When it stops. null means it runs until switched off. |
data.coupon.active | boolean | Whether the creator has it switched on. |
Caveats
- The snapshot carries no redemption counts and no plan restriction. Counts move
independently of the coupon row, and the plan list is a separate relation. Read both from
GET /v1/projects/{project}/coupons/{coupon}if you need them. - A coupon can be created already switched off.
active: falsehere is legitimate, not a race — a creator can author a code ahead of a campaign and switch it on later, which emitscoupon.activated. codeis uppercase regardless of what was typed. A creator enteringblackfridaygetsBLACKFRIDAY. Compare case-sensitively against the uppercase form.- Timestamps inside
data.couponare ISO 8601 with offset (+00:00), unlike the envelope'screated_at, which usesZ. Both are UTC.
Related events
coupon.updated— the same snapshot after a change, plus a list of what moved.coupon.activated/coupon.deactivated— availability flips.coupon.redeemed— a subscriber actually used it.- Coupon events overview — back to family overview.
How is this guide?