Merch ships with thirty-two designs, and if none of them is what you want, the front end is replaceable. The shop that ships with it is just one consumer of this HTTP API and holds no privileged access — so anything it does, your own front end can do, in React, Vue, Svelte, Flutter or a native app, while the invoices, the FIFO stock and the books stay exactly as they are.
Forty-three endpoints, on your own domain. Your store serves its own OpenAPI document at /api/openapi.json and a browsable page at /api/docs. This page is the same surface, so you can read it before installing anything.
Everything on this page, already built. No framework, no build step, no dependencies — separate .html, .css and .js files, so a team that knows HTML, CSS and JavaScript can read all of it and change any of it. Point one line at your shop and it runs.
Exercised against a real store: an order was placed from the browser and the total shown matched the amount charged, to the paisa.
git clone https://github.com/Abhi-hyperscripts/merch-reference-storefront cd merch-reference-storefront # edit ONE line in config.js: # export const API_BASE = 'https://shop.mybrand.com'; python3 -m http.server 5610
It is a folder of static files, so it deploys to GitHub Pages, Netlify, Vercel or your own nginx with nothing to build. And it does not have to live on the same domain as your shop.
MIT licensed — fork it, strip it, rewrite it. The README calls out the handful of things that are genuinely easy to get wrong, including the two checkout paths below and a shipping flag whose name means the opposite of what you would assume. Read the code on GitHub →
https://shop.example.com/api/…. There is no Ragenaizer-hosted API to sign up for and no extra host or port; the store you installed is the API.
Authorization: Bearer <token>. A shopper token comes from the shopper sign-in endpoint; the browsing endpoints need no token at all. It is a stateless JWT, so there is no session affinity and no cookie handling — a mobile app authenticates exactly as a browser does.
Authorization header and not a cookie, nothing depends on the two sharing a site.
{"error": "…"}, with a message written to be shown to a shopper as-is. 400 is a rejected request, 401 a missing or expired token, 404 an unknown id, 429 rate limiting, and 503 the store being temporarily unreachable — that last one adds "storeUnavailable": true so you can tell it apart from an ordinary refusal and retry rather than blame the shopper.
429 in the usual error shape. The catalogue, collections and blog are not limited, so a browse-heavy front end is not penalised.
Both take the same request body, so nothing about their shapes tells you which to call. Choosing wrong is the one mistake here that costs money, so it is worth thirty seconds: an order is created by exactly one call, and which call it is depends on whether you are collecting payment up front.
POST /api/checkout with the cart, the customer, and any coupon or gift card.Do not touch the payment endpoints on this path.
POST /api/payment/create-order with that same body. Nothing is ordered yet; you get back what the payment widget needs.keyId, amount and razorpayOrderId it returned. Note the amount is in paise, not rupees.POST /api/payment/verify with them. This is the call that creates the order, and it answers with the same shape /api/checkout would have.Never call /api/checkout as well. It would place a second, unpaid order for the same basket.
Two things that will save you writing code you do not need. Verifying twice is safe — a repeated verify returns the order already created, never a second one — so a retry after a dropped connection needs no guard of your own. And if the shopper pays and closes the tab before your verify fires, the store still receives the gateway’s own notification and creates the order anyway, so that basket is not lost.
Grouped the way a front end consumes them. Open any one for a runnable curl, what it does, its parameters, and the JSON going in and out. The shapes are illustrative — field names and types are real, the values are placeholders.
/api/catalogThe product list, returned as a bare array — there is no envelope and no pagination, so one call gives you the whole visible catalogue and any paging is yours to do client-side. Optional query: search, category, minPrice, maxPrice, inStock, locale, and sort, which accepts exactly price_asc, price_desc or name — any other value is ignored rather than rejected, so a typo silently returns catalogue order. Products that share a variant group fold to one card here; use the variants endpoint to expand it. Prices and stock are live from the merchant's books, and hidden products are already excluded.
curl https://your-shop.example.com/api/catalog
search query stringcategory query stringminPrice query numbermaxPrice query numberinStock query booleansort query stringlocale query string[
{
"id": "string",
"name": "string",
"description": "string",
"imageUrls": [
"string"
],
"category": "string",
"unit": "string",
"price": 0.0,
"mrp": 0.0,
"taxNote": "string",
"availability": "string",
"qtyHint": 0,
"featured": false,
"variantGroupId": "string",
"variantCount": 0,
"isDigital": false
}
]
/api/catalog/{id}One product in full, including its description, images and live price and availability.
curl https://your-shop.example.com/api/catalog/ITM-001
id path string requiredlocale query string{
"id": "string",
"name": "string",
"description": "string",
"imageUrls": [
"string"
],
"category": "string",
"unit": "string",
"price": 0.0,
"mrp": 0.0,
"taxNote": "string",
"availability": "string",
"qtyHint": 0,
"featured": false,
"variantGroupId": "string",
"variantCount": 0,
"isDigital": false
}
/api/categoriesThe category facets present in the catalogue, for building navigation or a filter rail.
curl https://your-shop.example.com/api/categories
[ "string" ]
/api/collectionsMerchandising collections — manual picks and rule-based ones — as configured by the merchant.
curl https://your-shop.example.com/api/collections
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"handle": "string",
"title": "string",
"description": "string",
"imageUrl": "string"
}
]
/api/collections/{handle}One collection and the products currently in it, resolved at request time so a smart collection stays current.
curl https://your-shop.example.com/api/collections/coffee-essentials
handle path string requiredlocale query string{
"collection": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"handle": "string",
"title": "string",
"description": "string",
"imageUrl": "string"
},
"products": [
{
"id": "string",
"name": "string",
"description": "string",
"imageUrls": [
"string"
],
"category": "string",
"unit": "string",
"price": 0.0,
"mrp": 0.0,
"taxNote": "string",
"availability": "string",
"qtyHint": 0,
"featured": false,
"variantGroupId": "string",
"variantCount": 0,
"isDigital": false
}
]
}
/api/coupon/validatePreview a coupon code against the current basket without applying it. The subtotal is computed server-side from live prices, so a client cannot claim a total.
curl -X POST https://your-shop.example.com/api/coupon/validate \
-H "Content-Type: application/json" \
-d '{"code":"SAVE10","lines":[{"itemId":"ITM-001","qty":2}]}'
{
"code": "SAVE10",
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
]
}
{
"valid": false,
"reason": "string",
"code": "string",
"discountAmount": 0.0,
"newSubtotal": 0.0
}
/api/currenciesThe display currencies the merchant has enabled, with their rates, for a currency switcher.
curl https://your-shop.example.com/api/currencies
{
"base": {
"code": "string",
"symbol": "string"
},
"currencies": [
{
"code": "string",
"symbol": "string",
"rate": 0.0
}
]
}
/api/discounts/autoPreview the best automatic (no-code) discount for the current basket, so the saving can be shown before checkout.
curl -X POST https://your-shop.example.com/api/discounts/auto \
-H "Content-Type: application/json" \
-d '{"lines":[{"itemId":"ITM-001","qty":2}]}'
{
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
]
}
{
"applies": false,
"title": "string",
"discountAmount": 0.0,
"newSubtotal": 0.0,
"freeShipping": false
}
/api/discounts/bxgyPreview any buy-X-get-Y saving that applies to the current basket.
curl -X POST https://your-shop.example.com/api/discounts/bxgy \
-H "Content-Type: application/json" \
-d '{"lines":[{"itemId":"ITM-001","qty":2}]}'
{
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
]
}
{
"applies": false,
"title": "string",
"discountAmount": 0.0
}
/api/eventsReport a small batch of funnel events (viewed, added to cart, and so on) for the merchant's analytics. Unknown event types are ignored rather than rejected.
curl -X POST https://your-shop.example.com/api/events \
-H "Content-Type: application/json" \
-d '{"sessionId":"string","events":[{"type":"view","itemId":"ITM-001"}]}'
{
"sessionId": "string",
"events": [
{
"type": "view",
"itemId": "ITM-001"
}
]
}
{
"ok": false,
"recorded": 0
}
/api/giftcard/checkCheck a gift card's balance before offering it at checkout. This does not redeem it.
curl -X POST https://your-shop.example.com/api/giftcard/check \
-H "Content-Type: application/json" \
-d '{"code":"GIFT500"}'
{
"code": "GIFT500"
}
{
"valid": false,
"reason": "string",
"code": "string",
"balance": 0.0
}
/api/media/ph/{seed}.svgA deterministic placeholder image, generated locally. Useful while building against a demo store with no product photography.
curl https://your-shop.example.com/api/media/ph/ITM-001-a.svg
seed path string requiredAnswers with an image (image/svg+xml), not JSON.
/api/products/{id}/variantsThe sibling options of a product (size, colour and so on) for a variant selector on the product page.
curl https://your-shop.example.com/api/products/ITM-001/variants
id path string requiredlocale query string{
"groupId": "string",
"title": "string",
"options": [
{
"id": "string",
"name": "string",
"label": "string",
"price": 0.0,
"availability": "string",
"current": false
}
]
}
/api/products/{itemId}/reviewsPublished reviews for a product, with the rating summary used for stars on a listing.
curl https://your-shop.example.com/api/products/ITM-001/reviews
itemId path string required{
"summary": {
"average": 0.0,
"count": 0
},
"reviews": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"author": "string",
"rating": 0,
"title": "string",
"body": "string",
"verified": false,
"createdAt": "2026-08-09T00:00:00Z"
}
]
}
/api/products/{itemId}/reviewstokenWrite or update the signed-in shopper's review. Requires a shopper token. Whether the review counts as verified is decided server-side from that shopper's order history — a client cannot claim it.
curl -X POST https://your-shop.example.com/api/products/ITM-001/reviews \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"rating":5,"title":"string","body":"string"}'
Requires Authorization: Bearer <shopper token>.
itemId path string required{
"rating": 5,
"title": "string",
"body": "string"
}
{
"ok": false,
"verified": false
}
/api/shipping/quoteThe delivery cost for a destination pincode and basket. This is the FINAL figure checkout will charge, resolved the same way the order is priced, so the number shown is the number billed. READ options, NOT available. Despite the name, available reports only whether a LIVE COURIER RATE was found — a store on flat-rate shipping, or one that has not connected a courier account, answers available: false on every quote while shipping perfectly happily. Wiring that flag to “we cannot deliver to your pincode” tells most shoppers of most stores that the shop does not serve them. options is always populated and is the real answer; shipping is the cheapest option’s amount.
curl -X POST https://your-shop.example.com/api/shipping/quote \
-H "Content-Type: application/json" \
-d '{"pincode":"110001","lines":[{"itemId":"ITM-001","qty":2}],"paymentMethod":"cod"}'
{
"pincode": "110001",
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
],
"paymentMethod": "cod"
}
{
"available": false,
"shipping": 0.0,
"courier": "string",
"etaDays": 0,
"options": [
{
"id": "string",
"name": "string",
"amount": 0.0,
"etaMin": 0,
"etaMax": 0,
"live": false
}
]
}
/api/themeThe shop's branding and layout — colours, fonts, logo, banners, legal pages and the chosen design preset. Fetch this first; it is what makes the storefront look like this particular business.
curl https://your-shop.example.com/api/theme
{
"brandName": "string",
"tagline": "string",
"logoUrl": "string",
"faviconUrl": "string",
"layout": "string",
"colors": {
"primary": "string",
"primaryHover": "string",
"accent": "string",
"bg": "string",
"surface": "string",
"text": "string",
"muted": "string",
"border": "string"
},
"radius": "string",
"fontHeading": "string",
"fontBody": "string",
"customCss": "string",
"hero": {
"headline": "string",
"subtext": "string",
"ctaText": "string",
"imageUrl": "string"
},
"banners": [
{
"imageUrl": "string",
"headline": "string",
"subtext": "string",
"link": "string"
}
],
"featuredItemIds": [
"string"
],
"hiddenItemIds": [
"string"
],
"navLinks": [
{
"label": "string",
"href": "string"
}
],
"currency": "string"
}
/api/cart/savePersist the in-progress basket against an email address. This is what makes abandoned-cart recovery possible; guests are included deliberately.
curl -X POST https://your-shop.example.com/api/cart/save \
-H "Content-Type: application/json" \
-d '{"email":"shopper@example.com","lines":[{"itemId":"ITM-001","name":"string","qty":2,"price":0.0}]}'
{
"email": "shopper@example.com",
"lines": [
{
"itemId": "ITM-001",
"name": "string",
"qty": 2,
"price": 0.0
}
]
}
{
"ok": false
}
/api/checkoutPlace a Cash-on-Delivery order. Works for a guest with inline customer details or for a signed-in shopper. Online payment uses the payment endpoints instead, because there the order must not exist until the money is verified.
curl -X POST https://your-shop.example.com/api/checkout \
-H "Content-Type: application/json" \
-d '{"lines":[{"itemId":"ITM-001","qty":2}],"customer":{"name":"string","email":"shopper@example.com","phone":"9999999999","address":"string","city":"New Delhi","state":"DL","pincode":"110001"},"paymentMethod":"cod","idempotencyKey":"string","couponCode":"SAVE10","giftCardCode":"GIFT500","shippingRateId":"string"}'
{
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
],
"customer": {
"name": "string",
"email": "shopper@example.com",
"phone": "9999999999",
"address": "string",
"city": "New Delhi",
"state": "DL",
"pincode": "110001"
},
"paymentMethod": "cod",
"idempotencyKey": "string",
"couponCode": "SAVE10",
"giftCardCode": "GIFT500",
"shippingRateId": "string"
}
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"invoiceId": "string",
"orderRef": "string",
"status": "string",
"total": 0.0,
"invoicePdfUrl": "string",
"giftCardApplied": 0.0,
"amountDue": 0.0
}
/api/orders/lookupGuest order lookup by email plus an order reference. Both must match, and it is rate limited, so it cannot be used to enumerate other people's orders.
curl -X POST https://your-shop.example.com/api/orders/lookup \
-H "Content-Type: application/json" \
-d '{"email":"shopper@example.com","reference":"SO-1A2B3C4D"}'
{
"email": "shopper@example.com",
"reference": "SO-1A2B3C4D"
}
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"invoiceId": "string",
"orderRef": "string",
"status": "string",
"total": 0.0,
"invoicePdfUrl": "string",
"createdAt": "2026-08-09T00:00:00Z",
"awb": "string",
"trackingUrl": "string",
"fulfillmentStatus": "string",
"carrier": "string",
"trackingNumber": "string",
"fulfilledAt": "2026-08-09T00:00:00Z",
"cancelledAt": "2026-08-09T00:00:00Z"
}
/api/orders/{id}An order's current status and timeline, for the confirmation page.
curl https://your-shop.example.com/api/orders/ITM-001
id path string required{
"order": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"invoiceId": "string",
"orderRef": "string",
"status": "string",
"total": 0.0,
"invoicePdfUrl": "string",
"createdAt": "2026-08-09T00:00:00Z",
"awb": "string",
"trackingUrl": "string",
"fulfillmentStatus": "string",
"carrier": "string",
"trackingNumber": "string",
"fulfilledAt": "2026-08-09T00:00:00Z",
"cancelledAt": "2026-08-09T00:00:00Z"
},
"events": [
{
"type": "string",
"at": "2026-08-09T00:00:00Z"
}
],
"access": [
{
"itemId": "string",
"name": "string",
"url": "string"
}
]
}
/api/auth/configWhich sign-in methods this shop offers, so the front end knows whether to render a Google button. The client id it returns is public by design.
curl https://your-shop.example.com/api/auth/config
{
"google": {
"enabled": false,
"clientId": "string"
}
}
/api/shopper/addressestokenThe shopper's saved delivery addresses. Requires a shopper token.
curl https://your-shop.example.com/api/shopper/addresses \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"phone": "string",
"line": "string",
"city": "string",
"state": "string",
"pincode": "string",
"isDefault": false
}
]
/api/shopper/addressestokenSave a new delivery address for the signed-in shopper.
curl -X POST https://your-shop.example.com/api/shopper/addresses \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"string","phone":"9999999999","line":"12 Nehru Place","city":"New Delhi","state":"DL","pincode":"110001","makeDefault":false}'
Requires Authorization: Bearer <shopper token>.
{
"name": "string",
"phone": "9999999999",
"line": "12 Nehru Place",
"city": "New Delhi",
"state": "DL",
"pincode": "110001",
"makeDefault": false
}
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"phone": "string",
"line": "string",
"city": "string",
"state": "string",
"pincode": "string",
"isDefault": false
}
/api/shopper/addresses/{id}tokenRemove one of the shopper's saved addresses.
curl -X DELETE https://your-shop.example.com/api/shopper/addresses/ITM-001 \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
id path string required{
"ok": false
}
/api/shopper/addresses/{id}tokenUpdate one of the shopper's saved addresses.
curl -X PUT https://your-shop.example.com/api/shopper/addresses/ITM-001 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"string","phone":"9999999999","line":"12 Nehru Place","city":"New Delhi","state":"DL","pincode":"110001","makeDefault":false}'
Requires Authorization: Bearer <shopper token>.
id path string required{
"name": "string",
"phone": "9999999999",
"line": "12 Nehru Place",
"city": "New Delhi",
"state": "DL",
"pincode": "110001",
"makeDefault": false
}
{
"ok": false
}
/api/shopper/addresses/{id}/defaulttokenMark a saved address as the shopper's default for checkout.
curl -X POST https://your-shop.example.com/api/shopper/addresses/ITM-001/default \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
id path string required{
"ok": false
}
/api/shopper/googletokenSign in with a Google ID token. The token is verified server-side and the shopper is found or created from the verified email. Returns a shopper JWT.
curl -X POST https://your-shop.example.com/api/shopper/google \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"credential":"string"}'
Requires Authorization: Bearer <shopper token>.
{
"credential": "string"
}
{
"token": "string",
"shopper": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "string",
"name": "string",
"phone": "string"
}
}
/api/shopper/metokenThe signed-in shopper's profile. Requires a shopper token.
curl https://your-shop.example.com/api/shopper/me \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "string",
"name": "string",
"phone": "string",
"linked": false
}
/api/shopper/orderstokenThe signed-in shopper's order history. Requires a shopper token.
curl https://your-shop.example.com/api/shopper/orders \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"invoiceId": "string",
"orderRef": "string",
"status": "string",
"total": 0.0,
"invoicePdfUrl": "string",
"createdAt": "2026-08-09T00:00:00Z",
"awb": "string",
"trackingUrl": "string",
"fulfillmentStatus": "string",
"carrier": "string",
"trackingNumber": "string",
"fulfilledAt": "2026-08-09T00:00:00Z",
"cancelledAt": "2026-08-09T00:00:00Z"
}
]
/api/shopper/wishlisttokenThe signed-in shopper's wishlist. Requires a shopper token.
curl https://your-shop.example.com/api/shopper/wishlist \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
[ "string" ]
/api/shopper/wishlisttokenAdd a product to the shopper's wishlist.
curl -X POST https://your-shop.example.com/api/shopper/wishlist \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"itemId":"ITM-001"}'
Requires Authorization: Bearer <shopper token>.
{
"itemId": "ITM-001"
}
{
"ok": false
}
/api/shopper/wishlist/{itemId}tokenRemove a product from the shopper's wishlist.
curl -X DELETE https://your-shop.example.com/api/shopper/wishlist/ITM-001 \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
itemId path string required{
"ok": false
}
/api/payment/configWhether online payment is switched on, and the public key needed to open the payment widget. Never returns the secret.
curl https://your-shop.example.com/api/payment/config
{
"enabled": false,
"keyId": "string",
"currency": "string",
"symbol": "string",
"name": "string"
}
/api/payment/create-orderPrice the basket server-side and open a payment against it. The amount is computed from live prices, coupons and gift cards — the client never states what it owes.
curl -X POST https://your-shop.example.com/api/payment/create-order \
-H "Content-Type: application/json" \
-d '{"lines":[{"itemId":"ITM-001","qty":2}],"customer":{"name":"string","email":"shopper@example.com","phone":"9999999999","address":"string","city":"New Delhi","state":"DL","pincode":"110001"},"paymentMethod":"cod","idempotencyKey":"string","couponCode":"SAVE10","giftCardCode":"GIFT500","shippingRateId":"string"}'
{
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
],
"customer": {
"name": "string",
"email": "shopper@example.com",
"phone": "9999999999",
"address": "string",
"city": "New Delhi",
"state": "DL",
"pincode": "110001"
},
"paymentMethod": "cod",
"idempotencyKey": "string",
"couponCode": "SAVE10",
"giftCardCode": "GIFT500",
"shippingRateId": "string"
}
{
"razorpayOrderId": "string",
"amount": 0,
"currency": "string",
"keyId": "string",
"name": "string",
"prefill": {
"name": "string",
"email": "string",
"contact": "string"
}
}
/api/payment/verifyVerify the signed payment result and create the order. Idempotent per payment, so a retried or duplicated callback cannot produce two orders.
curl -X POST https://your-shop.example.com/api/payment/verify \
-H "Content-Type: application/json" \
-d '{"razorpayOrderId":"string","razorpayPaymentId":"string","razorpaySignature":"string"}'
{
"razorpayOrderId": "string",
"razorpayPaymentId": "string",
"razorpaySignature": "string"
}
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"invoiceId": "string",
"orderRef": "string",
"status": "string",
"total": 0.0,
"invoicePdfUrl": "string",
"giftCardApplied": 0.0,
"amountDue": 0.0
}
/api/returnstokenThe signed-in shopper's own return requests and their status.
curl https://your-shop.example.com/api/returns \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"orderRef": "string",
"invoiceId": "string",
"reason": "string",
"status": "string",
"creditNoteNumber": "string",
"refundAmount": 0.0,
"adminNote": "string",
"orderTotal": 0.0,
"createdAt": "2026-08-09T00:00:00Z",
"resolvedAt": "2026-08-09T00:00:00Z"
}
]
/api/returnstokenRequest a return against one of the shopper's own orders. The merchant's books issue a credit note when it is accepted.
curl -X POST https://your-shop.example.com/api/returns \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"orderId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","reason":"string"}'
Requires Authorization: Bearer <shopper token>.
{
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"orderRef": "string",
"invoiceId": "string",
"reason": "string",
"status": "string",
"creditNoteNumber": "string",
"refundAmount": 0.0,
"adminNote": "string",
"orderTotal": 0.0,
"createdAt": "2026-08-09T00:00:00Z",
"resolvedAt": "2026-08-09T00:00:00Z"
}
/api/subscriptionstokenThe signed-in shopper's recurring orders.
curl https://your-shop.example.com/api/subscriptions \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"frequency": "string",
"status": "string",
"nextRun": "2026-08-09T00:00:00Z",
"createdAt": "2026-08-09T00:00:00Z"
}
]
/api/subscriptionstokenStart a recurring order for the signed-in shopper.
curl -X POST https://your-shop.example.com/api/subscriptions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"lines":[{"itemId":"ITM-001","qty":2}],"customer":{"name":"string","email":"shopper@example.com","phone":"9999999999","address":"string","city":"New Delhi","state":"DL","pincode":"110001"},"frequency":"string","title":"string"}'
Requires Authorization: Bearer <shopper token>.
{
"lines": [
{
"itemId": "ITM-001",
"qty": 2
}
],
"customer": {
"name": "string",
"email": "shopper@example.com",
"phone": "9999999999",
"address": "string",
"city": "New Delhi",
"state": "DL",
"pincode": "110001"
},
"frequency": "string",
"title": "string"
}
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"frequency": "string",
"status": "string",
"nextRun": "2026-08-09T00:00:00Z",
"createdAt": "2026-08-09T00:00:00Z",
"access": [
{
"itemId": "string",
"name": "string",
"url": "string"
}
],
"accessPendingPayment": false
}
/api/subscriptions/{id}/{op}tokenChange a subscription's state. The operation is one of pause, resume or cancel.
curl -X POST https://your-shop.example.com/api/subscriptions/ITM-001/pause \ -H "Authorization: Bearer $TOKEN"
Requires Authorization: Bearer <shopper token>.
id path string requiredop path string required{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "string",
"frequency": "string",
"status": "string",
"nextRun": "2026-08-09T00:00:00Z",
"createdAt": "2026-08-09T00:00:00Z"
}
/api/blogPublished posts, newest first, without bodies — for an index page.
curl https://your-shop.example.com/api/blog
[
{
"slug": "string",
"title": "string",
"excerpt": "string",
"coverUrl": "string",
"createdAt": "2026-08-09T00:00:00Z"
}
]
/api/blog/{slug}One published post in full, by slug.
curl https://your-shop.example.com/api/blog/coffee-essentials
slug path string required{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"slug": "string",
"title": "string",
"excerpt": "string",
"coverUrl": "string",
"body": "string",
"published": false,
"createdAt": "2026-08-09T00:00:00Z",
"updatedAt": "2026-08-09T00:00:00Z"
}
Generated from a live store’s OpenAPI document. If your store is a newer build, trust /api/openapi.json on it over this page.
| The OpenAPI document | |
| Spec | https://your-shop.example.com/api/openapi.json |
| Browsable | https://your-shop.example.com/api/docs |
| What to do with it | |
| Explore | Import the spec into Postman, Insomnia or Bruno |
| Generate a client | Any OpenAPI generator — TypeScript, Dart, Swift, Kotlin, Python |
| Try it before installing | Leave the API key blank on first run and the store boots a demo with sample products, so the API answers with real data immediately |
The store is free and self-hosted. It still needs a Ragenaizer tenant to post into, because the invoices, the stock and the returns belong to the Accounts module — that is the trade: you build the front end, the business OS keeps the books.
Thirty-two designs if you want one, and a documented API if you do not.
This does not make Merch open source — the application ships as prebuilt container images and its source is not published. What is yours is the shopfront: the API is a contract you can build anything against, without inheriting the part that is genuinely hard to get right, which is GST-correct invoicing and stock that agrees with the books.