Discount codes

A discount code is a coupon, voucher, or promo entered at checkout to reduce the order total. Storlaunch's discount-code surface is a thin proxy to Ripllo, the marketing-orchestration product in the Forjio family. Ripllo owns the table, the validation rules, and the usage-tracking; Storlaunch just exposes it under your auth.

All endpoints require an sk_* key.

Endpoints

Method Path Purpose
POST /v1/discount-codes Create a discount code
GET /v1/discount-codes List discount codes
GET /v1/discount-codes/:id Retrieve one discount code
PATCH /v1/discount-codes/:id Update a code
DELETE /v1/discount-codes/:id Archive a code

For the unauthenticated validate endpoint (used by the public storefront to live-check a code in a cart), see POST /v1/storefront/public/validate-discount.

Create

POST /v1/discount-codes

Request body

Field Type Required Description
code string (1–50) yes The code the buyer types (LAUNCH10, SUMMER25). Case-insensitive at validation time; stored as-typed. Must be unique per workspace.
description string (≤500) no Internal label.
type enum yes percent — percent off subtotal. fixed — flat amount off. shipping_percent — percent off shipping. shipping_fixed — flat amount off shipping.
value integer yes For percent types: 1–100 (treated as percent). For fixed types: smallest currency unit.
currency string (3) yes ISO 4217.
scope enum no cart (default, applies to whole cart), products (only to specific products), tags (only to products carrying matching tags).
productIds array of strings conditional Required when scope: products.
tagFilter array of strings conditional Required when scope: tags. Match-any semantics.
minPurchaseAmount integer no Smallest-unit. Below this, the code is rejected with MIN_PURCHASE_NOT_MET.
maxUsesTotal integer no Cap across all buyers. null = unlimited.
maxUsesPerCustomer integer no Cap per buyer.
startsAt / expiresAt ISO 8601 string no Validity window. Outside it, the code is rejected with EXPIRED (or NOT_YET_VALID before startsAt).
active boolean no Defaults true.
public boolean no If true, the code appears in /v1/storefront/public/:merchantSlug/discount-codes so storefronts can list "active promos".

Response201 Created. Returns the discount-code object.

Errors specific to this endpoint

Status error.code When
409 CODE_EXISTS The code already exists in this workspace.
400 VALIDATION_ERROR Required field missing or shape wrong.
curl -X POST https://storlaunch.forjio.com/api/v1/discount-codes \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "code":"LAUNCH10",
    "type":"percent",
    "value":10,
    "currency":"IDR",
    "minPurchaseAmount":100000,
    "maxUsesTotal":500,
    "expiresAt":"2026-06-01T00:00:00Z",
    "public":true
  }'
// Node
const code = await client.discountCodes.create({
  code: 'LAUNCH10',
  type: 'percent',
  value: 10,
  currency: 'IDR',
  maxUsesTotal: 500,
});

List

GET /v1/discount-codes?limit=50&cursor=…&active=true

Cursor-paginated. Filter by active for show-only-current view.

Retrieve / update / archive

Standard. The code field is not patchable — rename means delete and recreate. DELETE is a soft-archive (sets active: false and excludes from /storefront/public/.../discount-codes).

The discount-code object

Field Type Nullable Description
id string no disc_ + ULID.
code string no The code itself.
description string yes Internal label.
type enum no percent / fixed / shipping_percent / shipping_fixed.
value integer no Percent (1–100) or smallest-unit amount.
currency string no ISO 4217.
scope enum no cart / products / tags.
productIds array yes When scope: products.
tagFilter array yes When scope: tags.
minPurchaseAmount integer yes Floor for the cart subtotal.
maxUsesTotal integer yes Global cap.
maxUsesPerCustomer integer yes Per-buyer cap.
usesTotal integer no Counter, incremented at successful checkout completion.
startsAt string (ISO 8601) yes Earliest valid moment.
expiresAt string (ISO 8601) yes Last valid moment.
active boolean no If false, validation always fails.
public boolean no Listed on the public storefront.
createdAt / updatedAt string no Timestamps.

Validation semantics

When a code is applied at checkout, Ripllo applies these checks in order:

  1. active is true.
  2. Now >= startsAt and now < expiresAt.
  3. usesTotal < maxUsesTotal (when set).
  4. Customer's prior uses < maxUsesPerCustomer (when set).
  5. Cart subtotal ≥ minPurchaseAmount.
  6. For scope: products, at least one cart line item is in productIds.
  7. For scope: tags, at least one cart product carries a tag in tagFilter.

Each failure returns a specific reason: INACTIVE, NOT_YET_VALID, EXPIRED, MAX_USES_REACHED, CUSTOMER_LIMIT_REACHED, MIN_PURCHASE_NOT_MET, SCOPE_MISMATCH. The public validate-discount endpoint surfaces these directly.

Events

Storlaunch does not emit dedicated discount-code events. Use the validate endpoint or polling.

If you need usage events ("a code was redeemed"), subscribe on the Ripllo side — Ripllo emits discount_code.applied and discount_code.redeemed. Those are routed to Ripllo-side endpoints, not Storlaunch's.

Next