NewNo plan feeNo plan fee. Just 2.9% + $0.30 per card sale, and signing up is free.Try now →
All posts
api for couponsNovember 5, 2026·19 min read

API for Coupons: Endpoints, Rules and Code Examples

By PlatformDTC Team


Your flash sale starts in an hour, the code works in staging, and then checkout accepts it twice on the same order after a timeout. This is when teams realize a coupon isn't just a string in a marketing form. It's money moving under concurrency, retries, refunds, and channel sync. An API for Coupons has to make one authoritative decision, record it once, and ensure that record survives every retry path your storefront, POS, and agents can create.

I've written this for engineers shipping that decision. Paths and field names below are illustrative resource names, so confirm exact names in your developer documentation before you code against them.

Table of Contents

Coupon API Endpoints at a Glance

A coupon API splits into four jobs, offer management, eligibility preview, redemption and reversal, and reporting. Send your API key in the Authorization header on every call, use the sandbox base URL for all testing, and confirm the auth scheme in the PlatformDTC developer documentation.

Preview never consumes a use. Redeem and void change usage counters, so they must always carry an Idempotency-Key header.

TaskMethod and pathPurposeIdempotency key
Create offerPOST /couponsDefine code, discount type and rulesRequired
Read offerGET /coupons/{id}Fetch rules, status and usage countersNot needed
Update offerPATCH /coupons/{id}Change limits, expiry or statusRequired
Preview eligibilityPOST /coupons/validateCheck a cart without consuming a useNot needed
RedeemPOST /coupons/{id}/redemptionsConsume one use against an orderRequired
Reverse redemptionPOST /redemptions/{id}/voidRelease a use after cancellation or refundRequired
List redemptionsGET /coupons/{id}/redemptionsAudit usage by order and customerNot needed

Creating offers, redemption rules, and error handling each get detailed treatment below, because that's where integrations usually break.

Coupon Object Model and Identifiers

Don't model a coupon as an opaque discount string. Split it into two resources. A coupon_offer holds the issuer, offer terms, eligible product identifiers, redemption channels, validity interval with its time zone, usage limits, and a versioned eligibility ruleset. A coupon_instance is one serialized or single-use code under that offer, with its own state.

That split is what lets one promotion serve many shoppers without confusing the rules with one person's use of them.

A diagram illustrating the Coupon Object Model, detailing coupon_offer and individual coupon data components.

What Each Resource Owns

  • coupon_offer: Issuer, terms, eligible GTINs or internal product IDs, channels, validity interval, global and per-customer limits, ruleset version.
  • coupon_instance: The individual code, optional serial value, assigned customer if any, and redemption state, available, reserved, redeemed, or voided.

The GS1 General Specifications require digital coupons to use a Global Coupon Number (GCN) assigned by the issuer, made from a GS1 Company Prefix and coupon reference, with an optional serial component for an individual instance. GS1 also recommends Global Location Numbers for participating parties and Global Trade Item Numbers for eligible products, and requires coupon identification in a form that enables automatic data capture at transaction time.

Stable identifiers and an explicit version matter for audit. An order should record the offer version it was priced against. If marketing later raises a limit or extends expiry, that edit creates a new version and doesn't rewrite what an earlier customer received.

Record the version on the order. A later edit to an offer should never change the history of a redemption.

Say a brand runs 15 percent off two SKUs for 30 days, one use per customer. The offer holds the percentage, the two product IDs, the window, and the limit. Each emailed code is an instance that moves from available to reserved at checkout, then redeemed or back to available if payment fails.

With one normalized definition and one redemption ledger, web checkout, subscriptions, POS, B2B orders, and agent workflows all validate against the same rules instead of reimplementing them per channel.

Discount Types and Stacking Rules

Discount typeRequest parameterWorked example on 80 dollarsReduces
Percentage off orderpercent_off15 percent removes 12 dollars, total 68 dollarsOrder subtotal
Fixed amount off orderamount_off10 dollars off leaves 70 dollarsOrder subtotal
Free shippingfree_shippingShipping fee of 6 dollars becomes 0Shipping total
Percentage on eligible lines onlypercent_off with product filter15 percent on a 50 dollar eligible line removes 7.50 dollarsEligible line items

Send percent_off as a whole percentage and amount_off in minor units, so 10 dollars is 1000, not 10.00 floating through your pricing code. Free shipping needs a shipping rule that says which methods it covers, otherwise a shopper can pick the fastest option and erase a fee you didn't intend to waive.

Discount type is only the first layer. Minimum spend, product and customer eligibility, and currency rounding per currency decide whether the number in the table is the number on the order. Decide rounding at line or order level and write it down, because half-cent differences turn into reconciliation tickets.

Why Stacking Order Changes the Total

An exclusive policy allows one coupon per order, which is simpler to explain and to refund. A combinable policy applies discounts by explicit stacking priority with a cap on total discount, which is flexible but needs that cap to protect margin.

Priority isn't cosmetic. On an 80 dollar cart with 10 percent off and 10 dollars off, percentage first gives 80 x 0.90 = 72, then 62 dollars. Fixed amount first gives 70, then 63 dollars. That one dollar difference is why the server, not the storefront, must own the order of operations. Recurring plans add another wrinkle to these rules.

How to Create a Coupon Through the API

  1. Choose the code strategy. Use one shared code for a campaign, or unique single-use codes for affiliates and win-backs where sharing would destroy attribution.
  2. Set the discount type and parameter, for example percent_off with a value of 15.
  3. Define eligibility. Set channels, product filters by GTIN or internal product ID, minimum spend, and customer segment.
  4. Set usage limits as integers, such as a global cap of 500 uses and a per-customer limit of 1.
  5. Set the validity window with an explicit time zone, using ISO 8601 timestamps such as 2025-06-30T23:59:59-05:00. Omitting the zone shifts expiry by hours for customers in other regions.

A two-step infographic explaining how to create coupons via API by choosing a strategy and setting discount rules.

  1. Send POST /coupons with an Idempotency-Key and fields for code SPRING15, discount_type percent_off, percent_off 15, currency USD, max_redemptions 500, per_customer_limit 1, starts_at 2025-06-01T00:00:00-05:00, and ends_at 2025-06-30T23:59:59-05:00. Expect 201 Created with an id and version 1.
  2. Store the offer id and version in your own system so updates and orders can reference them.
  3. Preview against a test cart. Run POST /coupons/validate on an 80.00 USD cart and confirm a discount of 1200 minor units and a total of 6800 minor units, matching your expected amount to the cent before anyone sees the code.

Atomic Redemption and Idempotent Retries

Redemption is one server-side decision, not a client-side check followed by an apply. If those are two calls, the cart, limits, or expiry can change between them, and your storefront ends up honoring a result the server never committed. The GS1 Digital Coupon Management specification frames this as a secure and reliable management process with automatic capture of coupon identifiers at transaction time and exchange of management data among participating parties.

A single redeem call validates merchant, channel, customer or account eligibility, cart contents, currency, start and expiry timestamps, minimum spend, combinability, per-customer and global limits, and stacking priority. It then atomically creates a redemption record tied to the order or checkout attempt, or rejects the request with a reason. Nothing in between is a valid state.

Practical rule: If a retry could create a second discount, the endpoint isn't finished, however fast it responds.

How the Idempotency Key Behaves

  • Same key, same body: Return the original result with a replay flag set. Don't create a second redemption.
  • Same key, different body: Return a conflict. The caller changed the cart and needs a new key.
  • New key: Treat it as a new attempt and validate from scratch.

This protects you during network failures and agent retries, when the caller doesn't know whether the first request landed. Without it, a timeout at the wrong moment either double-discounts the order or tells a customer their code is used when no order exists.

The Redemption Lifecycle

Preview first if you want to show savings in the cart, but treat that number as an estimate. Reserve the use at checkout so a flash sale doesn't oversell the final slots while payment is pending. Redeem when payment is confirmed, because an authorized order is the point where money and the discount both become real. Release the reservation on timeout or cancellation so abandoned checkouts don't hold uses forever.

An example response should carry the redemption id, status, discount amount in minor units, offer version applied, and replay flag. That payload gives support, finance, and analytics the same facts without anyone reconstructing pricing from logs.

Scoped credentials for coupon mutation, approval gates for unusually large discounts, immutable audit events, and replay-safe webhooks belong around this endpoint, especially when automated agents can create or redeem promotions. Rate-limited or constant-time code verification and generic invalid-code errors reduce enumeration risk, and storing only a salted hash of high-entropy bearer codes limits damage if a database leaks.

This is also where a broader e-commerce API integration pattern earns its keep, because checkout, orders, and payments must agree on which attempt owns the redemption.

I would enforce one rule in code review. A retry reuses the key, a new cart gets a new key, and no client ever marks a coupon used on its own.

Coupon Errors and Last-Use Race Conditions

Two shoppers reach the final available use at the same moment. One redeem call wins, the other gets a conflict, and neither gets a silent double discount. A technical shopping cart and orders case study identifies atomic claiming, unique redemption constraints, and idempotent charging as necessary patterns for this kind of workflow.

A preview can be stale by the time payment confirms, so the redeem response is the only result that counts. Typical statuses are 409 for conflicts and 422 for rule failures, confirm them in the developer documentation.

ErrorCauseFix
Usage limit reached, 409A concurrent redeem took the final useShow the offer is no longer available and suggest another offer
Idempotency key reused with a different body, 409The same key was sent with a changed cartGenerate a new key for the new cart
Minimum spend not met, 422Cart subtotal is below the thresholdShow how much more is needed to reach the threshold
Customer limit reached, 422This customer already used the per-customer limitShow that the code was already used on this account
Expired offer, 422The current time is past ends_at in the offer time zoneShow the generic invalid-code message and log the reason
Invalid or unknown code, 422Code is missing, mistyped, expired or ineligibleShow one generic message for all these cases

The invalid-code response is intentionally generic, so log the actual reason internally for support. During flash sales, release unpaid reservations within a fixed window you set, such as 15 minutes, so stock and uses aren't held indefinitely.

Webhooks for Coupon Lifecycle Events

Polling a coupon endpoint for redemption state turns a money-changing event into a stale read. A redemption changes order totals, inventory, and attribution, so the event that records it belongs inside your transaction boundary. Subscribe to lifecycle events and let the provider push state you do not own. The diagram maps the event types your handler needs to recognize.

A hand-drawn diagram illustrating various coupon API event types like coupon created, updated, and offer expired.

Event names vary by provider. Check the developer documentation for events such as coupon created, coupon updated, redemption created, redemption voided, offer exhausted, and offer expired. Treat that list as a checklist, not a contract, because not every API emits all six. Each payload should carry an event id, event type, occurred-at timestamp, offer id, offer version, and redemption id where relevant. Without the offer version, a consumer cannot tell a late duplicate from a real change.

Three Checks Every Handler Needs

  • Verify the signature against the raw body, not a reserialized object, and reject timestamps outside your tolerance window to block replays.
  • Deduplicate on event id. The same event may arrive more than once, so store processed ids for at least the replay tolerance window.
  • Compare offer versions with your stored version and ignore older updates. Delivery can be out of order.

Acknowledge quickly and process asynchronously. A slow handler that times out invites redelivery, which is safe only when processing is idempotent. I would also route redemption events through the same idempotency key as the original redeem call, so a webhook replay and an API retry cannot both release the same use.

Say a redemption is voided after a refund. The webhook arrives and your inventory and analytics systems release the use and reverse the attributed discount. They do not rewrite the order record, because the order still happened and finance still needs its original pricing. If that event is redelivered, the stored event id stops the same use being released twice.

Subscriptions, POS and B2B Redemption Rules

One coupon can behave differently by order type, and that behavior must live on the offer. If the storefront guesses, each channel guesses differently and your usage counts stop agreeing.

Order typeHow the discount appliesWhen the use is countedRule to set
One-time web checkoutApplied once to the order before paymentWhen payment is confirmedReserve at checkout, redeem on confirmation
SubscriptionFirst renewal only, or each renewal for a set number of cyclesEach renewal, if the offer says soChoose first-cycle or repeating scope
POS counter saleApplied at the register and synced to the same orderAt the moment of saleSame limits as online channels
B2B draft orderApplied to the draft and re-checked on conversionWhen the invoice is issuedRe-validate eligibility at conversion

Subscription Renewals Cause the Most Reporting Errors

A first-renewal-only offer discounts one cycle and then stops, which suits an acquisition incentive. A repeating offer discounts a set number of cycles, which suits retention but costs more and consumes more of your limits.

Say a plan costs 50 dollars monthly with 20 percent off. First-renewal scope saves the customer 10 dollars once. A repeating offer for three cycles saves 30 dollars, and if each renewal counts, it uses three slots of the global limit. Decide that counting rule on the offer, not in a report query months later.

POS counter sales must enforce the same global and per-customer limits as online channels, or an in-store transaction can spend uses your campaign budget didn't plan for. A unified POS integration API helps here only if the register writes to the same redemption ledger rather than syncing a local counter afterward.

B2B drafts are different again because they sit open while prices, quantities, and eligibility change. Apply the discount to the draft for quoting, then re-validate at conversion to an invoice and count the use at issue. A code that expired while the draft waited for approval shouldn't discount the final invoice.

Configure channel scope before launch. Changing it after redemptions start creates reporting gaps nobody can reconcile cleanly.

Measuring Coupon Attribution and Incrementality

An affiliate code, a paid-media click, an email link, an automatic promotion, and a loyalty benefit can all touch one order. The redemption record tells you a code was used, it doesn't tell you which touch earned credit or whether the order needed a discount at all. Affiliate guidance on tracking conversions with unique coupon codes without cookies shows codes can link a conversion to a partner without browser tracking, but that linkage isn't the whole measurement story.

Build the Event Schema First

Record events for exposure, eligibility, application, redemption, cancellation, refund, and expiration. On each event store the promotion id, campaign id, customer consent status, order version, and attribution window in an immutable audit trail. When a refund reverses a redemption, you append a reversal event rather than deleting the original, so finance and affiliate reporting can both be corrected.

That first-party record is also your privacy-safe base. As cookies and device signals get less dependable, deterministic redemption data stays usable, but don't claim a coupon code solves attribution by itself. Pair these records with broader reporting, such as an ecommerce analytics dashboard, so discount cost sits next to revenue rather than in a separate spreadsheet.

Unique codes bias results in predictable ways. Codes get shared publicly, existing customers use codes meant for new buyers, and shoppers remember a code after the campaign ends. Each case inflates the apparent performance of the channel that issued the code, and can trigger affiliate commissions on sales that weren't incremental.

Use a Holdout to Answer the Real Question

Withhold the offer from a random group and compare revenue per eligible shopper, including discount cost. Say 10,000 eligible shoppers split 90 percent exposed and 10 percent holdout. Count the coupon as incremental only when the exposed group's uplift exceeds the discount cost per redemption.

That same discipline applies when you're tracking paid social profitability for ecommerce, because a code redeemed after an ad click still doesn't prove the ad caused the purchase.

Security Controls for Coupon Codes

  1. Scope credentials per integration or agent. Give each key only the coupon actions it needs and make keys revocable one at a time, so a compromised key can't mint offers across your whole catalog.
  2. Add approval gates. Require a person to approve offers above a set discount value or total liability cap, which stops a runaway automated agent from publishing a discount you can't afford.
  3. Generate high-entropy codes for public use and store only a salted hash where operations allow it. Short, guessable codes invite brute-force attempts against validate.
  4. Rate-limit the validate endpoint per client and per IP. Enumeration depends on cheap, unlimited guesses, so make guesses expensive.
  5. Return one generic invalid-code response. Attackers shouldn't be able to tell a missing code from an expired or ineligible one, because that distinction tells them when they've found a real code.
  6. Encrypt sensitive metadata, such as customer identifiers attached to unique codes. A leaked code list shouldn't become a leaked customer list.
  7. Write immutable audit events for every create, update, redeem, and void call. When a discount looks wrong, you need to know which key did it and when.
  8. Enforce uniqueness at the database level so two codes can't collide and two redemptions can't share one instance. Application-level checks alone lose races under load.

Short shared codes like SPRING15 are fine for public campaigns because they're meant to be known, and limits carry the risk. Single-use codes carrying real value need the entropy, hashing, and rate limits above, because secrecy is their only protection.

Confirm Your Coupon Integration Before Launch

Run this checklist in a sandbox before any live code ships.

  1. Replay test: Send the same redeem request three times with one idempotency key and confirm one redemption exists.
  2. Race test: Send two concurrent redeem calls for the last use and confirm one succeeds while the other returns a 409 conflict.
  3. Void test: Confirm a void releases the use and the redemption voided webhook fires once.
  4. Reconciliation test: Match discount totals in order records against redemption amounts for a sample of orders.
  5. Subscription test: Verify the renewal scope you chose across two renewals.

Teams that document these flows well usually treat the integration as part of automating workflows through connected software, so checkout, refunds, and reporting update together instead of drifting.

Then build your first live offer on our discounts engine. Our discount codes support percentage off the order, fixed amount off the order, and free shipping, and agents work through scoped, revocable per-agent keys with spending actions waiting for a person.


Build your coupon integration on a platform where discounts, checkout, subscriptions, POS, and analytics share one order record and one redemption ledger, so the rules you tested in sandbox hold in production. PlatformDTC gives your team governed APIs, scoped agent keys, and spend approvals out of the box. Visit the site to start with your first offer.