Public storefront

The public storefront API is the unauthenticated read path that hosted Storlaunch storefronts use. Anything a buyer can browse without logging in — the merchant's catalogue, individual product pages, the cart-checkout flow, the order tracker — is served from /v1/storefront/public/*.

This is the only section of the API that does not require an API key. Routes are scoped by the merchant's slug (the path segment, e.g. acme), not by your bearer token.

You'll integrate with these endpoints when:

  • Building a fully custom (headless) storefront against Storlaunch.
  • Embedding a product picker in your own site.
  • Implementing a tracking widget against the buyer-facing order URL.

For the auth-required catalogue write API, see Products.

Endpoints

Reads

Method Path Purpose
GET /v1/storefront/public/resolve/:slug Resolve a slug to its workspace ID + branding
GET /v1/storefront/public/:merchantSlug Storefront landing payload (products, branding, hero)
GET /v1/storefront/public/:merchantSlug/:productSlug Product detail page
GET /v1/storefront/public/:merchantSlug/blog Blog index
GET /v1/storefront/public/:merchantSlug/blog/:slug Blog post
GET /v1/storefront/public/:merchantSlug/blog/rss.xml Blog RSS feed
GET /v1/storefront/public/:merchantSlug/sitemap XML sitemap
GET /v1/storefront/public/:merchantSlug/pixels Pixels config (Meta/TikTok/GA) for client init
GET /v1/storefront/public/:merchantSlug/feeds/google.xml Google Merchant feed
GET /v1/storefront/public/:merchantSlug/feeds/meta.xml Meta catalog feed
GET /v1/storefront/public/:merchantSlug/feeds/tiktok.xml TikTok catalog feed
GET /v1/storefront/public/:merchantSlug/discount-codes Public-listed discount codes
GET /v1/storefront/public/:merchantSlug/r/:code Resolve a referral link
GET /v1/storefront/public/order/:deliveryId Buyer-facing order page (download URL, tracking)
GET /v1/storefront/public/:merchantSlug/order/:number Order tracker by order number

Writes (unauthenticated, but rate-limited and CAPTCHA-gated)

Method Path Purpose
POST /v1/storefront/public/track Submit a pixel/analytics event
POST /v1/storefront/public/:merchantSlug/:productSlug/checkout Single-product buy-now checkout
POST /v1/storefront/public/:merchantSlug/cart-checkout Multi-product cart checkout
POST /v1/storefront/public/:merchantSlug/manual-checkout Manual-payment (bank transfer / WhatsApp) checkout
POST /v1/storefront/public/validate-discount Quote a discount code against a cart
POST /v1/storefront/public/:merchantSlug/referral/capture Capture a referral attribution cookie
GET/POST /v1/storefront/public/abandoned-cart/unsubscribe Unsubscribe link target

Storefront landing

GET /v1/storefront/public/:merchantSlug

Returns the everything-the-homepage-needs payload: merchant branding, the published product list, the featured/hero block, and the active discount-codes banner (public-listed only).

Path parameters

Param Type Description
merchantSlug string The merchant's account.slug (or a known custom-domain alias).

Response200 OK

{
  "data": {
    "merchant": {
      "id": "acc_01HX...",
      "name": "Acme",
      "slug": "acme",
      "brandColor": "#0a84ff",
      "brandLogo": "https://cdn.../logo.png",
      "brandBanner": "https://cdn.../banner.jpg",
      "description": "Independent stationery, made in Bandung."
    },
    "products": [ /* product objects, published only, archived excluded */ ],
    "discountCodes": [ /* publicly-listed codes only */ ],
    "pixels": { "metaPixelId": "...", "tiktokPixelId": "...", "gaMeasurementId": "..." }
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

Errors

Status error.code When
404 RESOURCE_NOT_FOUND No merchant has this slug (and no custom-domain alias matches).

Product detail

GET /v1/storefront/public/:merchantSlug/:productSlug

Returns one product plus its variants, attached files (digital only), inventory state (when fulfillment module on), and the resolved cross-sell list. Used to render the product detail page.

curl https://storlaunch.forjio.com/api/v1/storefront/public/acme/field-notes-notebook

Buy-now checkout

POST /v1/storefront/public/:merchantSlug/:productSlug/checkout

Creates a checkout session for a single product purchase. Returns a hosted URL the buyer is redirected to. The upstream payment is handled by Plugipay; Storlaunch only owns the session orchestration.

Request body

Field Type Required Description
email string yes Buyer email — required for digital delivery and receipts.
name string no Buyer's display name.
phone string no Phone, for couriers that need it.
variantId string no The chosen variant (if the product has them).
quantity integer no Default 1.
shippingAddress object conditional Required for physical products. See address shape.
couponCode string no Apply a discount code at the create stage.
referralCode string no Referral attribution.
successUrl / cancelUrl string no Override the post-pay redirects.
metadata object no Free-form passthrough into the checkout session and the downstream product.purchased event.

Response201 Created

{
  "data": {
    "checkoutSessionId": "cs_01HX...",
    "hostedUrl": "https://pay.plugipay.com/c/cs_01HX..."
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

Redirect the buyer to hostedUrl immediately. After payment, the post-pay flow runs and checkout.completed + product.purchased events fire.

Cart checkout

POST /v1/storefront/public/:merchantSlug/cart-checkout

Same as buy-now but accepts an array of { productId, variantId?, quantity } items. Returns one combined hostedUrl; on payment, one delivery row is created per line item.

Manual checkout

POST /v1/storefront/public/:merchantSlug/manual-checkout

For workspaces on the free tier (no Plugipay module). Records a "manual order" with status awaiting_payment and emails the buyer the merchant's bank account details / WhatsApp number. The merchant later confirms payment in the dashboard, which flips the order to payment_confirmed and starts fulfilment.

This endpoint does not create a cs_… checkout session — manual orders live in a separate ManualOrder table; see Manual orders.

Validate a discount

POST /v1/storefront/public/validate-discount

Quote a discount code against a cart without creating a checkout. Returns the discount value and any error reason (CODE_NOT_FOUND, MIN_PURCHASE_NOT_MET, MAX_USES_REACHED, EXPIRED). Used by storefronts to give live feedback in the cart UI.

// Request
{
  "code": "LAUNCH10",
  "merchantSlug": "acme",
  "items": [
    { "productId": "prod_01HX...", "variantId": "var_01HX...", "quantity": 2 }
  ],
  "subtotalAmount": 150000
}
// Response (success)
{
  "data": {
    "valid": true,
    "code": "LAUNCH10",
    "type": "percent",
    "value": 10,
    "discountAmount": 15000,
    "appliesToShipping": false
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

Order tracking

GET /v1/storefront/public/order/:deliveryId

The buyer's post-purchase URL. Returns the delivery object without auth — the del_ ID itself is the credential (random 26-char ULID, infeasible to guess). Renders the download button, the licence key, or the courier tracking widget.

The merchant-scoped sibling, /:merchantSlug/order/:number, takes a human-readable order number from the post-pay receipt email instead of the raw ID.

Custom domains

Custom merchant domains (e.g. shop.acme.com) resolve to the same endpoints via the /resolve/:slug lookup. The host header is matched against a Domain row; if it matches, the request is rewritten to the corresponding merchant slug before the handler runs. See Domains for set-up.

Rate limits

Public endpoints share a per-IP rate budget separate from the authenticated tier:

Endpoint class Limit
Read (catalogue, blog, feeds) 200 req/min per IP
Checkout creates 10 req/min per IP per merchant
Validate discount 30 req/min per IP per merchant

Hitting the cap returns 429 Too Many Requests. The hosted storefront and SDKs already respect these — you're unlikely to hit them outside scripted abuse.

Events

The public endpoints don't emit events directly. Downstream events are emitted when state transitions happen:

  • A successful checkout-create kicks off a Plugipay checkout flow that, on payment, emits checkout.completed and product.purchased.
  • A manual checkout writes a row, no event yet — the event chain starts when the merchant confirms in the dashboard.

Next