Discounts & Gift Cards
Coupons (code-gated), automatic offers, and prepaid sats gift cards — how discounts and prepaid credit work on ZapAPI checkouts.
Two merchandising primitives for your project: discounts (coupons + automatic offers) reduce what a payer pays at checkout, and gift cards are prepaid sats codes you sell or give away.
Discounts — one engine, two modes
| Coupon (Stripe-style) | Offer (Razorpay-style) | |
|---|---|---|
| How it applies | Customer enters the code | Automatic in its validity window |
| Best for | Influencer codes, launch promos | Seasonal sales, launch week |
| Dashboard | Project → Coupons & Offers → Coupons | Project → Coupons & Offers → Offers |
Both shapes are percent off (1–99%) or fixed sats off, with an optional validity window and a max redemptions cap. Discounts apply to project invoices and payment-link checkouts — never below 1 sat.
Create one
curl -X POST https://api.zapapi.com/api/v1/discounts \
-H "Authorization: Bearer zap_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"projectId": "uuid-of-project",
"code": "LAUNCH20",
"type": "percent",
"percentOff": 20,
"maxRedemptions": 100
}'An offer is identical with "mode": "offer" — the best valid offer
auto-applies at checkout, no code needed.
Where discounts apply
- Invoices —
POST /v1/invoiceswithcouponCode. The invoice keeps the list price for accounting (metadata.listAmountSats) and charges the discounted amount (metadata.discount). The discount is snapshotted at creation — edits to the coupon never change existing invoices. - Payment links — the hosted checkout auto-applies the best offer and shows a coupon field with a price breakdown (price − discount = you pay).
Redemptions are reserved at invoice creation (compare-and-swap on the cap)
— an abandoned invoice may leave timesRedeemed slightly high, which is the
safe direction for caps.
Custom checkouts
Validate before you show a price:
const result = await zap.discounts.validate({
projectId: 'proj_…',
code: 'LAUNCH20',
amountSats: 21000,
});
// { valid: true, discountSats: 4200, finalSats: 16800 }Gift cards — prepaid sats with real money movement
A gift card is a code (ZAP-GIFT-XXXX-XXXX) holding a sats balance. Unlike
platform-credit systems, money moves for real at every step:
- Sell (
issueType: 'paid') — the buyer pays a purchase invoice into your settlement wallet now. The card activates on settlement. This is the classic gift-card sale: you hold the sats up front. - Give (
issueType: 'free') — you fund the card from your own wallet (comp, promo, goodwill). - Redeem — the recipient enters the code at zapapi.app/gift-card and claims real sats into their own wallet via LNURL-withdraw. Partial claims leave a remaining balance; the balance can't be overdrawn (CAS accounting).
// Issue (sell)
const { card, purchase } = await zap.giftCards.issue({
balanceSats: 21000,
issueType: 'paid',
});
// → show purchase.bolt11 to the buyer · share card.code as the gift
// Issue (give — active immediately)
await zap.giftCards.issue({ balanceSats: 5000, issueType: 'free' });
// Redeem (public — the code is the capability)
const { lnurl, remainingSats } = await zap.giftCards.redeem(card.code, 21000);
// → show lnurl as a QR; the recipient's wallet pulls the satsThe code is the capability
Anyone holding a gift-card code can check and redeem its balance — treat
codes like cash. Expiry is optional; cards can be disabled from the
dashboard or zap.giftCards.disable(id).
API reference
| Endpoint | Notes |
|---|---|
POST/GET/PATCH/DELETE /v1/discounts | Owner CRUD (payment-links:* scopes) |
POST /v1/discounts/validate | Public — compute a coupon for an amount |
POST /v1/discounts/offer | Public — resolve the auto-applied offer |
POST /v1/gift-cards | Issue (paid = purchase invoice, free = active) |
GET /v1/gift-cards, /:id, POST /:id/disable | Owner management |
GET /v1/gift-cards/public/:code | Public card status + redeem LNURL |
POST /v1/gift-cards/public/:code/redeem | Public redemption (rate-limited) |