Appearance
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
| Stage | Action | API call | Auth |
|---|---|---|---|
| 1 | Page loads | GET /api/v1/mobile/getproduct/:slug — optional hubId, roomId, productId | Brand |
| 1 | Reviews on the page | GET /api/v1/mobile/review/product/:productId or POST /api/v1/mobile/review/product-slug if only the slug is in hand | Brand |
| 1 | "You may also like" rail | POST /api/v1/mobile/related-catalog — guest mount /mobile/open/related-catalog | Customer or Brand |
| 1 | Save for later | POST /api/v1/user/mobile/wishlist/create — productId | Customer |
| 1a–1d | Add / increase / decrease / delete | PUT /api/v1/mobile/syncBag — guest mount /mobile/open/syncBag | Customer or Brand |
2. Cart
Path: Cart → cart items listing → add to cart / increase qty / decrease qty / delete item → Checkout button
| Stage | Action | API call | Auth |
|---|---|---|---|
| 2a | Every quantity change, and the totals after it | PUT /api/v1/mobile/syncBag | Customer or Brand |
| 2a | Which discounts could apply | POST /api/v1/mobile/discount/applicable — guest mount /mobile/open/discount/applicable; hubId is required unless the token carries it | Customer or Brand |
| 2a | Enter a code | PUT /api/v1/mobile/explicitDiscount — discounts is a string array, uppercased. Codes can also ride along on syncBag | Customer |
| 2a | The user's own claimed rewards | GET /api/v1/mobile/discount/reward — includeExpired defaults to false | Customer |
| 2a | Empty the cart | DELETE /api/v1/mobile/emptyCart — customer only, no guest mount | Customer |
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[]—hubProductIdandquantityper line, pluspackageId,points_source,isPromotionalmodification[]— the same lines flaggedcreated/updated/deleted, for when the server needs to know what the user just didsyncBag: 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 calculatedpoints— points to burn against the orderdiscounts[]— 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
| Stage | Action | API call | Auth |
|---|---|---|---|
| 3a | Prefill ship-to details | GET /api/v1/mobile/user-detail | Customer |
| 3b | List saved addresses | GET /api/v1/user/mobile/address | Customer |
| 3b | Add a new address | POST /api/v1/user/mobile/address/add | Customer |
| 3b | Edit / remove one | PUT /api/v1/user/mobile/address/update/:addressId · DELETE /api/v1/user/mobile/address/delete/:addressId | Customer |
| 3b | Re-price with delivery fees and tax | PUT /api/v1/mobile/syncBag with the chosen addressId | Customer or Brand |
| 3c | Place the order — signed in | POST /api/v1/mobile/createOrder — every field optional, because the bag comes from the synced server-side cart | Customer |
| 3c | Place the order — guest | POST /api/v1/mobile/create-order — the bag and a customer object (with address / billingAddress inline) go in the request | Brand |
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 field | Sends as | Note |
|---|---|---|
| Flat name | address1 (or address2) | Unverified split. The API has address1 (required), address2, and a separate name label |
| Street name | address1 / address2 | Same question — the two form fields have to be composed into these |
| City | city | Required |
| Zipcode | zip | Required |
| Country / State | country + state | country must be an ISO code from country_iso_codes; short_state is separate and optional |
| — | addressString | Required, and not on the form. The full formatted address; the client has to compose it |
| — | latLng | Optional { 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
syncBagwith a newquantity. - Decrease qty at 1 — does it go to 0 and remove the item, or stop at 1 and require the delete button?
syncBagaccepts either: omit the line, or flag itdeletedinmodification. - 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
emptyCarthas no guest mount, so a guest's "clear cart" is asyncBagwith 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
syncBagresponse 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
syncBagreturned. - No variant selection (size, colour, etc.) in the flow — is that out of scope or just not captured? The bag line takes a
packageIdalongsidehubProductId, which is the closest thing to a variant in the payload. - Out-of-stock path. The search side has
in_stockandwith_inventory; whatgetproductreports 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-detailhas 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/addsaves to the account, not to the order. - No order summary / totals / shipping cost / tax in the flow yet. The data exists: it is the
syncBagresponse, once anaddressIdhas been sent. - No shipping method choice (standard vs express). The order payload has
deliverySchedule,deliveryInstruction,transporterIdandmethod(enum keyscurbside,doorstep,pos,instore,shipment) — a method picker would map ontomethod. - 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 (
billingAddressIdon the customer order,billingAddresson 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.
createOrderruns at stage 3c, and the payment step takes anorderId— 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.