Skip to content
Linra Omni Developer Docs

Discount codes

Define your own promo codes, check one against a cart, and apply it at checkout.

Discount codes are yours — you define them for your own end customers. They never change what you pay Linra: your commission, VAT, and settlement are computed exactly as if the order had no discount at all. A code is purely a display/reporting overlay on top of the price Linra computes.

Terminal window
POST /api/v1/scent/discount-codes
{
"code": "SAVE10",
"discountType": "Percent",
"value": 10
}
{
"state": "CREATED",
"payload": {
"id": "5b2e...",
"code": "SAVE10",
"discountType": "Percent",
"value": 10,
"isActive": true,
"scopedVariantIds": [],
"usageCount": 0
}
}

Omitting scopedVariantIds (or sending an empty list) applies the code to your whole catalog. To restrict it to specific products instead, pass the variant IDs explicitly:

Terminal window
POST /api/v1/scent/discount-codes
{
"code": "SAUVAGE10",
"discountType": "Percent",
"value": 10,
"scopedVariantIds": ["a1b2c3d4-...", "e5f6a7b8-..."]
}

The code string is immutable once created, and so is everything else about it — there is no update endpoint at all. To retire a typo’d or campaign-ended code, deactivate it and create a new one:

Terminal window
PATCH /api/v1/scent/discount-codes/{id}/active
{ "isActive": false }

This activate/deactivate toggle is the only mutation a discount code supports after creation — it’s also how you retire a code that has already been redeemed at least once, since a redeemed code can never be deleted (see “Managing usage” below).

Field Meaning
validFrom / validTo Optional validity window. Both null = open-ended.
totalUsageCap Optional cap on total redemptions across all customers.
perCustomerCap null (no limit) or exactly 1 (at most one redemption per customerRef, ever). No other value is accepted in v1.
scopedVariantIds Present and non-empty ⇒ the code applies only to those variants. Omitted/empty ⇒ applies to your whole catalog.
minOrderValue Optional minimum cart total (VAT-inclusive) for the code to apply.

Every one of these is fixed for the life of the code — there is no way to edit a term after create; deactivate and recreate instead. A code is scoped to you — two partners may both register SAVE10 independently.

Before checkout, check whether a code currently applies — this never consumes a use. customerRef is a required path segment, identifying which of your carts to check against — pass the same value you used to build that cart:

Terminal window
POST /api/v1/scent/cart/{customerRef}/discount-codes/validate
{ "code": "SAVE10" }
{
"state": "SUCCESS",
"payload": {
"valid": true,
"reason": null,
"totals": {
"originalTotal": 200.00,
"discountAmount": 20.00,
"discountedTotal": 180.00
}
}
}

(Worked from a cart holding 2 units at 100 SAR each — a 10% code computes a 20 SAR discount, Math.Round-ed to 4 decimal places and never exceeding the cart total.)

If you’ve requested a supported non-SAR display currency via X-Partner-Currency, payload.totals additionally carries a nested display object — { originalTotal, discountAmount, discountedTotal } — the same additive display-currency pattern used everywhere else in the API, alongside the canonical SAR figures above (never replacing them). The code’s own configured value (e.g. a fixed-amount discount) is always SAR-denominated regardless of your display currency — only the computed result shown back here converts for display.

If the code doesn’t apply, valid is false and reason is one of:

Reason Meaning
EMPTY_CART Your cart has no lines.
NOT_FOUND No such code for your account.
INACTIVE The code exists but is deactivated.
NOT_YET_VALID / EXPIRED Outside the code’s validity window.
BELOW_MIN_ORDER_VALUE Cart total is below the code’s minOrderValue.
PRODUCT_SCOPE_MISMATCH The code has a non-empty scopedVariantIds, and none of your cart lines are in it.
CAP_REACHED The total-usage cap has been reached (advisory here — see below).
ALREADY_USED_BY_CUSTOMER This customerRef has already redeemed a per-customer-capped code.

totals.originalTotal/totals.discountAmount/totals.discountedTotal are display figures only — they never change what you are actually charged. discountAmount/discountedTotal (SAR and display alike) are null whenever valid is false.

This validate call is the only place a discount is ever shown as its own line. Checkout preview and the price lock it produces stay discount-free by design — its totals are exactly what Linra charges/nets, which a discount code never changes (see “How charging works” below). To show the end customer their saving before they confirm, call validate and render its totals yourself; don’t expect a discount line to appear anywhere in the cart/preview response.

Pass the SAME code on the order-create call. partnerId is never a request field — it comes from your bearer token — and lockId is required: every order is placed against a frozen price lock (the id from a prior POST /api/v1/scent/cart/{customerRef}/price-lock call), never a live-priced create — see the Cart preview & price locks guide:

Terminal window
POST /api/v1/scent/orders
{
"externalReference": "order-42",
"lockId": "9f2b3c1a-6e7d-4a12-8b3f-1c2d3e4f5a6b",
"discountCode": "SAVE10",
"shippingAddress": { "...": "..." }
}
{
"state": "CREATED",
"payload": {
"id": "...",
"pricing": {
"discount": { "code": "SAVE10", "amount": 20.00 },
"...": "..."
}
}
}

The code is re-validated at this point (state may have changed since you last validated it — the usage cap in particular is only advisory at validate time and authoritative here). If it no longer applies, order creation is rejected with VALIDATION_DISCOUNT_CODE_INVALID, VALIDATION_DISCOUNT_CODE_EXPIRED, VALIDATION_DISCOUNT_CODE_CAP_REACHED, or BUSINESS_DISCOUNT_CODE_ALREADY_USED_BY_CUSTOMER — never a silent downgrade to “no discount applied.” A use is only ever counted when the order itself is actually created: a failed or cancelled checkout never burns a redemption.

How charging works (the part that never changes)

Section titled “How charging works (the part that never changes)”

However a code is defined, it has zero effect on:

  • what you pay Linra (wholesale amount, or your commission in commission mode),
  • VAT computation and settlement,
  • whether a price is available to check out.

Those are all computed on the undiscounted price, exactly as if no code were applied. If you want to fund a promotion, it comes out of your own share of the sale — Linra has no stake in it either way.

GET /api/v1/scent/discount-codes and GET /api/v1/scent/discount-codes/{id} include a running usageCount so you can track redemption progress without a separate report. A code that has never been redeemed (usageCount == 0) can be deleted outright (DELETE /api/v1/scent/discount-codes/{id}); once it has at least one redemption, delete is rejected with BUSINESS_DISCOUNT_CODE_IN_USE — deactivate it instead, so the historical order data referencing it stays meaningful.