Skip to content

Purchase flow ​

Product detail → cart → checkout shipping, ending at "Go to payment". One endpoint carries most of this flow: syncBag is the cart, the pricing engine and the discount applier at once, and the customer order endpoint reads the bag back off the server rather than from the request.

End-to-end ​

1. Product Display Page ​

Path: Shop listing page → tap shop card → Product Display Page → add to cart / increase qty / decrease qty / delete item from cart → Go to cart button

StageActionAPI callAuth
1Page loadsGET /api/v1/mobile/getproduct/:slug — optional hubId, roomId, productIdBrand
1Reviews on the pageGET /api/v1/mobile/review/product/:productId or POST /api/v1/mobile/review/product-slug if only the slug is in handBrand
1"You may also like" railPOST /api/v1/mobile/related-catalog — guest mount /mobile/open/related-catalogCustomer or Brand
1Save for laterPOST /api/v1/user/mobile/wishlist/create — productIdCustomer
1a–1dAdd / increase / decrease / deletePUT /api/v1/mobile/syncBag — guest mount /mobile/open/syncBagCustomer or Brand

2. Cart ​

Path: Cart → cart items listing → add to cart / increase qty / decrease qty / delete item → Checkout button

StageActionAPI callAuth
2aEvery quantity change, and the totals after itPUT /api/v1/mobile/syncBagCustomer or Brand
2aWhich discounts could applyPOST /api/v1/mobile/discount/applicable — guest mount /mobile/open/discount/applicable; hubId is required unless the token carries itCustomer or Brand
2aEnter a codePUT /api/v1/mobile/explicitDiscount — discounts is a string array, uppercased. Codes can also ride along on syncBagCustomer
2aThe user's own claimed rewardsGET /api/v1/mobile/discount/reward — includeExpired defaults to falseCustomer
2aEmpty the cartDELETE /api/v1/mobile/emptyCart — customer only, no guest mountCustomer

syncBag is the whole cart ​

It is the answer to several of the open questions below, so it is worth stating plainly. You send the entire bag — not a delta — and get back "totals, taxes and applied discounts":

  • bag[] — hubProductId and quantity per line, plus packageId, points_source, isPromotional
  • modification[] — the same lines flagged created / updated / deleted, for when the server needs to know what the user just did
  • syncBag: true — persist the cart server-side. The customer order endpoint depends on this (see stage 3)
  • addressId — needed before delivery fees and tax can be calculated
  • points — points to burn against the order
  • discounts[] — uppercased codes

So "increase qty" is not a quantity endpoint; it is a re-price of the whole bag. And there is no separate "cart totals" call to add later — the totals the shipping and payment screens need are already in this response.

3. Checkout — Shipping Page ​

Path: Checkout → Shipping Page → ship-to details (first name, last name, email, phone number) + shipping address (select existing or add new: flat name, street name, city, zipcode, country/state) → Go to Payment button

StageActionAPI callAuth
3aPrefill ship-to detailsGET /api/v1/mobile/user-detailCustomer
3bList saved addressesGET /api/v1/user/mobile/addressCustomer
3bAdd a new addressPOST /api/v1/user/mobile/address/addCustomer
3bEdit / remove onePUT /api/v1/user/mobile/address/update/:addressId · DELETE /api/v1/user/mobile/address/delete/:addressIdCustomer
3bRe-price with delivery fees and taxPUT /api/v1/mobile/syncBag with the chosen addressIdCustomer or Brand
3cPlace the order — signed inPOST /api/v1/mobile/createOrder — every field optional, because the bag comes from the synced server-side cartCustomer
3cPlace the order — guestPOST /api/v1/mobile/create-order — the bag and a customer object (with address / billingAddress inline) go in the requestBrand

Guest checkout is supported, and it is a different endpoint ​

The guest order endpoint takes the whole basket and the buyer in one call: the customer object needs one of email / phoneNumber / userId and exactly one of userId / name, and its address object must contain either an addressId or an address1. So a "Skip for now" shopper can complete a purchase without an account — but through create-order, not createOrder, and with no saved-address list to pick from.

Address field mapping ​

The form's five fields do not line up one-to-one with the address payload, which requires more than the form collects:

Form fieldSends asNote
Flat nameaddress1 (or address2)Unverified split. The API has address1 (required), address2, and a separate name label
Street nameaddress1 / address2Same question — the two form fields have to be composed into these
CitycityRequired
ZipcodezipRequired
Country / Statecountry + statecountry must be an ISO code from country_iso_codes; short_state is separate and optional
—addressStringRequired, and not on the form. The full formatted address; the client has to compose it
—latLngOptional { lat, lng } — needed if the map/locator side of the app wants to place the address

isPrimary defaults to true

Adding a second address silently demotes the first unless you pass isPrimary: false. On a checkout screen that adds an address mid-purchase, that is a real behaviour change to the user's account.

Open questions ​

Cart mechanics

  • "Add to cart" appears on the cart items listing as well as the product page. On the cart screen, is that a separate action, or is it the same control as increase qty? At the API level there is no difference — both are a syncBag with a new quantity.
  • Decrease qty at 1 — does it go to 0 and remove the item, or stop at 1 and require the delete button? syncBag accepts either: omit the line, or flag it deleted in modification.
  • Delete item: confirmation step, or immediate with undo? Undo is cheap here — the client still holds the previous bag and can re-send it.
  • Empty cart state and the path out of it aren't defined. Note emptyCart has no guest mount, so a guest's "clear cart" is a syncBag with an empty bag.
  • Is the cart badge/count shown on the listing page and product page? There is no "get cart" endpoint in the mobile set — the count comes from the last syncBag response the client received.

Product page

  • Delete-from-cart on the product display page implies the page knows the item is already in the cart — so the button set changes based on cart state. Worth spelling out which controls show when. Same constraint as the badge: cart state is whatever the last syncBag returned.
  • No variant selection (size, colour, etc.) in the flow — is that out of scope or just not captured? The bag line takes a packageId alongside hubProductId, which is the closest thing to a variant in the payload.
  • Out-of-stock path. The search side has in_stock and with_inventory; what getproduct reports for a sold-out item is not documented.

Checkout

  • A user who came in via "Skip for now" has no account. Guest checkout is possible — see above — so the open question is a product one: offer it, or push them into login / signup?
  • Ship-to details duplicate profile data — prefill from the account? user-detail has all four fields.
  • Address management: can a saved address be edited or deleted from here, and is a newly added address saved to the account for reuse? Yes to both — the update and delete endpoints exist, and address/add saves to the account, not to the order.
  • No order summary / totals / shipping cost / tax in the flow yet. The data exists: it is the syncBag response, once an addressId has been sent.
  • No shipping method choice (standard vs express). The order payload has deliverySchedule, deliveryInstruction, transporterId and method (enum keys curbside, doorstep, pos, instore, shipment) — a method picker would map onto method.
  • Promo / discount code — in or out? The endpoints are there (explicitDiscount, discount/applicable); the screen for them is not.
  • Billing address, and whether it can differ from shipping. The payloads keep them separate (billingAddressId on the customer order, billingAddress on the guest one), so the API assumes they can differ.
  • Validation failures on the shipping form aren't defined. Joi is strict — an unexpected key is rejected outright, so the client must not echo a GET response straight back into these calls.
  • Order before payment. createOrder runs at stage 3c, and the payment step takes an orderId — so the order exists before any money moves. The spec should say what state that order is in, and what happens to it if the user abandons payment.

Batch platform, Shopify app and API documentation