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.
Sales
Endpoint reference: ✅ Verified live against production on 10 May 2026.
“Sales” in Dr Green parlance is a lightweight CRM-style pipeline — a way to track customer interest before it becomes an order. Each sale entry has a stage (LEADS / ONGOING / CLOSED), is associated with a client, and optionally references the order it eventually became.
Endpoints
| Method | Path | Auth | Description |
|---|
POST | /dapp/sales | API-key + sig | Create a new sales pipeline entry |
GET | /dapp/sales | API-key + sig | List sales entries, paginated |
PATCH | /dapp/sales | API-key + sig | Update an existing sale (stage transition, etc.) |
GET | /dapp/sales/summary | API-key + sig | Counts by stage |
Stage enum
| Stage | Meaning |
|---|
LEADS | Initial interest captured (e.g. consultation booked, enquiry submitted) |
ONGOING | Active conversation, customer is engaged but hasn’t ordered |
CLOSED | Sale concluded — either won (linked to an order) or lost |
The stage field is set by your store; the platform doesn’t auto-promote between stages. Combine with orderId to distinguish won vs. lost closures (orderId set → won; null → lost).
POST /dapp/sales — create a sale entry
Use this when a customer first expresses interest — e.g. they book a consultation, submit a contact form, or have an offline conversation your team logs in your store CRM.
Request body
Per createSaleDto (full schema not formally declared in the spec; verified pattern):
{
"clientId": "2cbab026-9dc6-46fd-8580-77e3dfd2f086",
"stage": "LEADS",
"description": "Booked initial consultation for chronic pain"
}
| Field | Type | Required | Notes |
|---|
clientId | string (UUID) | ✅ | Must belong to the calling holder |
stage | string | ✅ | One of LEADS, ONGOING, CLOSED |
description | string | No | Free-text notes; visible in the holder’s dashboard |
orderId | string (UUID) | No | Set only when transitioning to CLOSED with a linked order |
Canonical payload (for signing)
JSON.stringify(body) — compact, no whitespace.
Response
201 Created with data.id.
🔒 Full POST response shape not yet captured live (would require a write call against production data — held off pending explicit clearance).
GET /dapp/sales — list sales
Paginated list of all sales entries for the holder.
Query parameters
| Name | Type | Default | Notes |
|---|
page | integer | 1 | |
limit | integer | 10 | |
stage | string | — | Filter by LEADS, ONGOING, or CLOSED |
clientId | string (UUID) | — | Filter to one client |
search | string | — | Free-text on description and client name |
Canonical payload
urlencode(query) if any params, else "{}".
Response shape (verified)
{
"success": true,
"statusCode": 200,
"message": "Success",
"data": {
"sales": [
{
"id": "97f09fc5-17dc-4677-8a82-aa886b5bfd03",
"createdAt": "2025-02-26T05:49:11.465Z",
"updatedAt": "2025-02-26T05:49:11.465Z",
"stage": "LEADS",
"description": null,
"orderId": null,
"client": {
"id": "2cbab026-9dc6-46fd-8580-77e3dfd2f086",
"firstName": "Dheeraj",
"lastName": "Kumar",
"email": "dheeraj.k+11@rejolut.com",
"phoneCountryCode": null,
"phoneCode": "+91",
"contactNumber": "6394234342",
"isActive": true
}
}
],
"pageMetaDto": {
"page": "1", "take": 10, "itemCount": 19,
"pageCount": 2, "hasPreviousPage": false, "hasNextPage": true
}
}
}
🪲 description is null when unset (not empty string). Handle nullable in your UI.
🪲 No filterable created/updated date range at the API level — fetch wider and filter client-side if you need a time window.
Worked example (Node)
async function listSales(stage?: 'LEADS' | 'ONGOING' | 'CLOSED', page = 1, limit = 10) {
const q = { page: String(page), limit: String(limit), ...(stage ? { stage } : {}) };
const payload = canonicalPayload('GET', null, q);
const headers = authHeaders(API_KEY, SECRET_KEY, payload);
const url = new URL('https://api.drgreennft.com/api/v1/dapp/sales');
Object.entries(q).forEach(([k, v]) => url.searchParams.set(k, v));
const res = await fetch(url, { headers });
return (await res.json()).data;
}
PATCH /dapp/sales — update a sale
Move a sale through the pipeline (e.g. LEADS → ONGOING → CLOSED).
Request body
Per updateSaleDto:
{
"id": "97f09fc5-17dc-4677-8a82-aa886b5bfd03",
"stage": "CLOSED",
"orderId": "603b3f1a-4cab-44af-972d-a26a77fbdff9",
"description": "Closed after follow-up call; ordered Femme Fatale 1g"
}
| Field | Type | Required | Notes |
|---|
id | string (UUID) | ✅ | The sale to update |
stage | string | No | New stage |
orderId | string (UUID) | No | Link to the order this sale converted into |
description | string | No | Replaces existing description |
⚠️ Note this is PATCH /dapp/sales (no path param) — the ID goes in the body, not the URL. Unusual REST shape; mind the path.
Canonical payload
JSON.stringify(body) — compact.
Response
200 OK with data.id.
🔒 PATCH response shape not yet captured live.
GET /dapp/sales/summary — counts by stage
Canonical payload
{}
Response shape (verified)
{
"success": true,
"statusCode": 200,
"message": "Success",
"data": {
"summary": {
"ONGOING": 16,
"LEADS": 16,
"CLOSED": 5,
"totalCount": 19
},
"count": 19
}
}
🪲 summary.totalCount and count are duplicates — same value, two fields. Pick one.
🪲 The summary counts don’t sum to totalCount in the verified data (16 + 16 + 5 = 37, but totalCount is 19). The summary numbers appear to be per-stage cumulative count over time rather than current state. Confirm with Dr Green which interpretation is intended; document accordingly. 🔒
Common patterns
Lead → won-order pipeline
// 1. Customer enquires → create LEAD
await createSale({ clientId, stage: 'LEADS', description: enquiry });
// 2. Sales rep follows up → move to ONGOING
await updateSale({ id: saleId, stage: 'ONGOING' });
// 3. Customer orders → close + link
const order = await createOrder({ clientId, ... });
await updateSale({ id: saleId, stage: 'CLOSED', orderId: order.id });
Render a “won vs lost” view
const { sales } = await listSales('CLOSED');
const won = sales.filter(s => s.orderId !== null);
const lost = sales.filter(s => s.orderId === null);
Caching guidance
- Sales list: 30s TTL. Sales rep activity is real-time-ish.
- Summary: 1m TTL.
See also
- Clients — sales reference clients
- Orders — closed sales link to orders via
orderId
- Commissions — commissions accrue from won sales