Skip to main content

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

MethodPathAuthDescription
POST/dapp/salesAPI-key + sigCreate a new sales pipeline entry
GET/dapp/salesAPI-key + sigList sales entries, paginated
PATCH/dapp/salesAPI-key + sigUpdate an existing sale (stage transition, etc.)
GET/dapp/sales/summaryAPI-key + sigCounts by stage

Stage enum

StageMeaning
LEADSInitial interest captured (e.g. consultation booked, enquiry submitted)
ONGOINGActive conversation, customer is engaged but hasn’t ordered
CLOSEDSale 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"
}
FieldTypeRequiredNotes
clientIdstring (UUID)Must belong to the calling holder
stagestringOne of LEADS, ONGOING, CLOSED
descriptionstringNoFree-text notes; visible in the holder’s dashboard
orderIdstring (UUID)NoSet 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

NameTypeDefaultNotes
pageinteger1
limitinteger10
stagestringFilter by LEADS, ONGOING, or CLOSED
clientIdstring (UUID)Filter to one client
searchstringFree-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. LEADSONGOINGCLOSED).

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"
}
FieldTypeRequiredNotes
idstring (UUID)The sale to update
stagestringNoNew stage
orderIdstring (UUID)NoLink to the order this sale converted into
descriptionstringNoReplaces 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