Documentation Index
Fetch the complete documentation index at: https://docs.dr.green/llms.txt
Use this file to discover all available pages before exploring further.
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.
GET /dapp/carts returns a paginated list of clients, each with their clientCart[] and cartItems[] embedded. The top-level field is clients, not carts. This is a small but real footgun — your UI code expecting data.carts will silently render nothing.
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, fetch 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 is CreateCartItemsDto per the live OpenAPI spec — full schema not formally declared, but the working pattern is:
{
"clientId": "2cbab026-9dc6-46fd-8580-77e3dfd2f086",
"cartItems": [
{ "strainId": "c77dd198-ec9a-4ced-8105-937fde104424", "quantity": 5 },
{ "strainId": "<other-strain-id>", "quantity": 2 }
]
}
| 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.
{
"success": true,
"statusCode": 201,
"message": "Success",
"data": { "id": "<cart-uuid>" }
}
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)
{
"success": true,
"statusCode": 200,
"message": "Success",
"data": {
"clients": [
{
"id": "715b0db6-02e9-46b4-8b1a-eb6cac47f9d1",
"firstName": "Ricardo",
"lastName": "Capone",
"email": "ricardo.capone@protonmail.ch",
"shippings": [ { "countryCode": "ZAF", "currency": null } ],
"clientCart": [
{
"id": "6712f17e-678e-419c-9790-27dff0de7ed2",
"cartItems": [
{
"quantity": 5,
"createdAt": "2026-05-08T11:46:12.738Z",
"strain": { /* embedded strain summary */ }
}
]
}
]
}
],
"pageMetaDto": {
"page": "1", "take": 10, "itemCount": 1,
"pageCount": 1, "hasPreviousPage": false, "hasNextPage": false
}
}
}
🪲 clientCart is an array even though each client has at most one active cart. Take [0] and treat the rest defensively.
🪲 Cart items have no id in the embedded list — only quantity, createdAt, and the embedded strain. 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)
def carts_in_progress(page=1, limit=10):
query = {"page": str(page), "limit": str(limit)}
payload = canonical_payload("GET", query=query)
headers = auth_headers(API_KEY, SECRET_KEY, payload)
url = f"https://api.drgreennft.com/api/v1/dapp/carts?{urlencode(query)}"
r = httpx.get(url, headers=headers)
r.raise_for_status()
clients = r.json()["data"]["clients"]
# Flatten to a "cart progress" list
return [
{"client": c, "cart": (c["clientCart"][0] if c["clientCart"] else None)}
for c in clients
]
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
async function clientCart(clientId: string): Promise<Cart | null> {
const client = await getClient(clientId); // GET /dapp/clients/{clientId}
return client.clientCart[0] ?? null;
}
Replace cart contents (since there’s no “remove item”)
async function setCart(clientId: string, items: { strainId: string; quantity: number }[]) {
const client = await getClient(clientId);
const existing = client.clientCart[0];
if (existing) await deleteCart(existing.id);
await postCart({ clientId, cartItems: items });
}
Cart-to-order
When the customer checks out, you call POST /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.
Caching guidance
Don’t cache cart responses. Carts are mutable and customers expect immediate feedback after add/remove operations. Always read fresh.
See also
- Strains — pricing and strain IDs to populate the cart
- Clients — client detail also includes
clientCart[]
- Orders — converting a cart into an order