Skip to main content
Waffo has a first-party discount engine. You define coupons (what the discount is) and issue promotion codes (the strings customers type, or that you distribute), then read redemptions and discount spend back out of the Portal. It all lives under the Marketing menu: Coupons, Promotion Codes, Discount Analytics and Coupon Settings. The feature is permission-gated — if the Marketing menu is visible in your Portal, it is available on your account.

How do I define the discount itself?

A currency left unconfigured on an AMOUNT_OFF coupon is not eligible — an order in that currency is refused rather than silently charged full price. Configure every currency you sell in. Duration counts billing cycles, so it only bites on subscriptions; a one-time payment consumes the discount once either way. ONCE — a single payment only, and a second attempt on the same discount is refused. REPEATING — N cycles, where you also set Duration in Cycles. FOREVER — every cycle.

What else do I fill in when creating a coupon?

Discount type, discount value and duration are immutable after creation — the page warns you before you submit, and afterwards only Name and Metadata are editable. Deactivate stops further redemptions and is reversible; Archive is irreversible and also deactivates the coupon’s promotion codes. Neither touches discounts already applied. The list shows a lifecycle tag rather than a raw status: archived wins, then expired (past redeemBy), then the coupon’s own state.

How do I issue promotion codes?

Batch creation is a four-step wizard — pick the coupon, set prefix and quantity, set restrictions, then a preview returning sample codes and an estimated generation time before you commit. Generated codes are permanent, so plan the prefix and quantity first. The batch runs asynchronously: Batch Tasks lists Task ID, coupon, status (PENDING → PROCESSING → SUCCESS / FAILED), success count and creation time, and links through to the codes a task produced. A code-level expiry cannot extend the coupon — the coupon’s own redeemBy is still checked separately, so the earlier of the two ends redemption.

Why was a code refused at checkout?

Eligibility is decided before the amount is calculated, roughly in this order:
  • Coupons are disabled on your account, or no coupon configuration exists yet.
  • The coupon or the code is inactive, expired, or has exhausted its redemption stock; or the code does not belong to the coupon passed in the same request.
  • The order amount is below the code’s Minimum Amount for that currency, or AMOUNT_OFF has no amount configured for the order currency.
  • A per-customer limit, a daily limit or First Time Only applies, but the request carried no customer identifier — the discount is refused rather than applied unrestricted.
  • First Time Only and the customer already has a completed redemption — scoped to your account as a whole, not to this one coupon.
  • A ONCE coupon was already used, or a REPEATING coupon has used up its cycles.

Where do I see who redeemed what?

The coupon detail page has Overview, Promotion Codes and Redemption History tabs. Each redemption row carries Record ID, promotion code, customer, source type and source ID, Original / Discount / Final amount in the order currency, status and creation time. Filter by customer, source ID, status and date range, then export the filtered view. Status runs CREATED (reserved, not yet final) → COMPLETED (confirmed — the only state analytics counts), or VOIDED (the payment never completed, reservation released) / REVERSED (rolled back after a refund).

How is Discount Analytics calculated?

Three caveats decide whether the numbers match your own. It counts completed redemptions only, so CREATED, VOIDED and REVERSED are excluded. Amounts are summed across currencies with no conversion, which is why the cards and the performance table show bare numbers without a currency symbol — use the redemption export for per-currency totals. And Redemption Rate = completed redemptions ÷ the summed Max Redemptions of the coupons in scope, so coupons with no cap contribute 0 to the denominator. Default range is the last 30 days. The trend chart buckets by each redemption’s confirmation time in your Portal time zone, and the Daily / Weekly / Monthly toggle re-buckets the same data. Coupon Performance splits the period per coupon: Total Codes, Redemptions, Total Discount, Redemption Rate, Average Order Value.

What do the account-level coupon settings do?

What happens to a discount on refund, and during a subscription trial?

Under the Full Reversal strategy, a full refund reverses the redemption — the record becomes REVERSED and the slot returns to both the coupon and the code. A partial refund does not reverse it: the record stays COMPLETED and the slot stays consumed. A payment that never completed releases the reservation as VOIDED instead. Reversal returns only what was actually consumed, so a cycle that never received a real discount cannot push a counter negative. A zero-amount trial period occupies that period’s redemption slot on the coupon and code, but does not burn the discount’s own lifecycle count. A ONCE coupon on a subscription with a free trial therefore still has its single real discount available for the first paid cycle. Outside subscriptions, a zero-amount order with no discount actually applied consumes nothing.

How do I apply a discount through the API?

Order create carries a promotionInfo object with two mutually exclusive fields, each up to 64 characters. promotionInfo.promotionCode is used by you or by the Waffo-hosted checkout page — the checkout discount input accepts a promotion code only, so a customer can never type a coupon ID. promotionInfo.couponId is yours alone, at order create, to apply a specific coupon with no code involved. Pass one or the other, never both; if a code and a coupon ID both arrive, the code must belong to that coupon. Field-level rules for order create and the checkout page’s own behaviour are covered in the order-create field guide and the checkout page docs.

What can I export from the Marketing pages?

Everything the Marketing pages manage is a Portal feature — create, edit, deactivate and archive coupons; create single codes and code batches; browse redemptions and analytics — and there is no API-key-signed integration endpoint for any of it. The only API touchpoint is promotionInfo on order create, above. Four lists have an Export button: Coupons, Promotion Codes, Redemption History and Discount Analytics. Each export runs as a task and hands you a CSV named <type>-<task-id>.csv — prefix coupon, promotion-code, promotion-record or promotion-analytics. An export honours the filters on screen and covers up to 10,000 rows. Timestamps in lists and exports render in your Portal time zone; the Portal sends its offset with the request.
Numbers not adding up? Contact support with the coupon ID, the promotion code, or the redemption Record ID.