Carts
Endpoint reference: ✅ Verified live against production on 10 May 2026.
Carts are per-client. Each client has at most one active cart. Adding items to a cart doesn’t reserve inventory — items are only committed when an order is placed via POST /dapp/orders.
Conceptual surprise: the API exposes “clients-with-carts”, not “carts directly”
⚠️ Read this before building cart UI.If your store has a “carts in progress” view (for the holder, showing all customers who’ve started a cart), this endpoint feeds it. If you’re rendering a single customer’s cart, fetchGET /dapp/cartsreturns a paginated list of clients, each with theirclientCart[]andcartItems[]embedded. The top-level field isclients, notcarts. This is a small but real footgun — your UI code expectingdata.cartswill silently render nothing.
GET /dapp/clients/{clientId} instead — clientCart[] is included in the detail response.
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /dapp/carts | API-key + sig | Add items to a client’s cart (creates the cart if none exists) |
GET | /dapp/carts | API-key + sig | List clients with carts in progress |
DELETE | /dapp/carts/{cartId} | API-key + sig | Delete a cart |
POST /dapp/carts — add items
Adds one or more strain items to a client’s cart. Creates the cart on first call.
Request body
The shape isCreateCartItemsDto per the live OpenAPI spec — full schema not formally declared, but the working pattern is:
| Field | Type | Required | Notes |
|---|---|---|---|
clientId | string (UUID) | ✅ | Must belong to the calling holder |
cartItems | array | ✅ | At least one item |
cartItems[].strainId | string (UUID) | ✅ | From Strains |
cartItems[].quantity | integer | ✅ | Positive integer (units of grams) |
Canonical payload (for signing)
JSON.stringify(body) — compact, no whitespace. Send the same exact string as the request body.
Response
201 Created with the cart’s id.
Idempotency
POST /dapp/carts is not idempotent server-side. Calling it twice with the same payload will add the items twice. If the customer hits the “Add to cart” button rapidly, debounce client-side. There’s no Idempotency-Key header support yet (see 04-errors.md § Idempotency).
Errors
| Status | Cause | Fix |
|---|---|---|
400 ["clientId must be a UUID", "cartItems should not be empty"] | Validation failure | Validate input client-side first |
403 “Client does not belong to this holder” | clientId is for another holder’s customer | Re-fetch the holder’s client list and pick from there |
404 “Strain not found” | strainId was soft-deleted between catalogue fetch and add | Refresh the catalogue, drop stale items |
GET /dapp/carts — clients with carts
Returns a paginated list of clients who have an active cart, with the cart and items embedded.
Query parameters
| Name | Type | Default |
|---|---|---|
page | integer | 1 |
limit | integer | 10 |
search | string | — |
Canonical payload
urlencode(query) if any present, else "{}".
Response shape (verified)
🪲clientCartis an array even though each client has at most one active cart. Take[0]and treat the rest defensively.
🪲 Cart items have noidin the embedded list — onlyquantity,createdAt, and the embeddedstrain. To remove a single item, you delete the whole cart and recreate. There’s no “remove item from cart” endpoint in the current API. 🔒
Worked example (Python)
DELETE /dapp/carts/{cartId} — delete a cart
Removes a cart and all its items. Used when the customer abandons or chooses to start over.
Path parameters
| Name | Type | Description |
|---|---|---|
cartId | string (UUID) | The id from clientCart[0].id |
Canonical payload
{}
Response
200 OK with data: null or a confirmation id.
Errors
| Status | Cause | Fix |
|---|---|---|
404 “Cart not found” | Already deleted, or wrong holder | Treat as success on the client side; idempotent in practice |
401 “User is not authorized” | Signed "" instead of "{}" | Use the canonical payload "{}" |
Common patterns
Resolve a single client’s cart
Replace cart contents (since there’s no “remove item”)
Cart-to-order
When the customer checks out, you callPOST /dapp/orders with the cart’s contents. The order endpoint takes the same shape as the cart contents, with the addition of shipping and payment details. The cart itself isn’t automatically cleared on order — clean it up with DELETE if you want a fresh slate.