Coupons
Coupons are discount codes that end users can apply during Stripe checkout. The admin Coupons view is the CRUD surface: create new codes, see redemption counts, and deactivate codes that should no longer be usable.
Where to find it
Admin sidebar → Coupons (/coupons).
Quick start
- Open Admin → Coupons.
- Click Create Coupon.
- Fill in a code (e.g.
LAUNCH20), optional description, discount type (Percentage or Fixed Amount), value, optional max redemptions (leave blank for unlimited), and optional valid-until date. - Click Create. The row appears in the list with an Active badge.
- To retire a code, click Deactivate on its row — existing subscriptions that used it keep their discount; no new redemptions are possible.
Detailed walkthrough
Coupons table
Columns:
- Code — the code string users type at checkout (case sensitive).
- Discount — formatted as
20%or$5.00depending on discount type. - Redemptions — count of successful uses.
- Valid Until — expiry timestamp or
—if unlimited. - Status — Active or Inactive badge.
- Actions — Deactivate button (hidden for already-inactive coupons).
Create Coupon dialog (create-coupon-dialog.tsx)
Fields:
- Code — e.g.
LAUNCH20. - Description — optional free text.
- Discount Type —
PercentageorFixed Amountradio. - Discount Value — integer. For percentage it's 1–100; for fixed amount it's cents or minor units for the configured Stripe currency.
- Max Redemptions — integer; leave blank for Unlimited.
- Valid Until — optional date.
Create button triggers POST /api/coupons; while submitting the button shows "Creating…" and is disabled.
Deactivating
Deactivate sets the coupon's active flag to false without deleting the row. Existing redemptions remain valid via Stripe; new redemptions are rejected. To hard-delete, use the DELETE endpoint directly (no button in the UI).
Common scenarios
- Launch promotion — create
LAUNCH20at 20% for the first 100 redemptions, expiry in 7 days. - Influencer code — percentage coupon with no expiry, unlimited redemptions.
- Retire a leaked code — click Deactivate; existing paying customers keep their discount.
- Stripe-level refund — not done here. Refunds are applied in the Stripe Dashboard against the invoice, not against the coupon.
Permissions
| Action | Permission |
|---|---|
| View coupon list / detail | coupons:read |
| Create a coupon | coupons:create |
| Edit / deactivate a coupon | coupons:edit |
| Delete a coupon (hard delete) | coupons:delete |
Dashboard entry requires admin:access. System admins implicitly hold every coupons permission.
API
| UI action | GraphQL | REST |
|---|---|---|
| List coupons | adminCoupons | GET /v1/admin/coupons |
| Get coupon | adminCoupon | GET /v1/admin/coupons/\{id\} |
| Create coupon | adminCreateCoupon | POST /v1/admin/coupons |
| Update coupon | adminUpdateCoupon | PUT /v1/admin/coupons/\{id\} |
| Deactivate coupon | adminDeactivateCoupon | PUT /v1/admin/coupons/\{id\} with { active: false } |
| Delete coupon (no UI) | — | DELETE /v1/admin/coupons/\{id\} |
| End-user validate coupon | — | GET /v1/billing/validate-coupon?code=X |
Tips & gotchas
- Codes are case sensitive.
LAUNCH20andlaunch20are different coupons — the UI does not normalise. - Deactivation is soft. It keeps redemption history intact. Delete only if the row is unused and you want it gone forever.
- The discount value field is raw — percentage
20means 20%, fixed amount500means 500 minor units (e.g. 5.00 USD). Document the convention in the description field for future admins. - Max redemptions is enforced at redemption time. Concurrent redemptions at the limit are a race — Stripe wins.
Related
- Billing — account-level subscription overview
- Subscriptions — per-account subscription listing
- Plans — plans the coupon can be applied against