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.

Strains (Catalogue)

Endpoint reference: ✅ Verified live against production on 10 May 2026. Strains are the products in your store. The Dr Green platform handles inventory and fulfilment; your job is to surface the catalogue, filtered to what’s available in the customer’s country, and link strain selections into orders.

Endpoints

MethodPathAuthDescription
GET/dapp/strainsAPI-key + sigList strains for a country, paginated
GET/dapp/strains/{strainId}API-key + sigGet one strain with full detail
Both routes accept DualAuthGuard — JWT (DAPP UI) or API-key+signature (your store).

GET /dapp/strains

List strains available in the given country. Returns a paginated wrapper.

Query parameters

NameTypeRequiredDescription
countryCodestringYesISO 3166-1 alpha-3 code, uppercase. E.g. GBR, USA, DEU, ZAF. Alpha-2 (GB, US) returns 400.
pageintegerNo (default 1)1-indexed page number
limitintegerNo (default 10)Items per page (server may cap; 50 is safe)
searchstringNoFree-text match on strain name

Canonical payload (for signing)

urlencode(query) — e.g. countryCode=GBR&page=1&limit=10

Response shape (verified)

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": {
    "strains": [ /* array of strain summary objects, see below */ ],
    "pageMetaDto": {
      "page": "1",
      "take": 10,
      "itemCount": 47,
      "pageCount": 5,
      "hasPreviousPage": false,
      "hasNextPage": true
    }
  }
}

Strain summary object

The strains[] items contain the fields needed to render a catalogue card. The full schema isn’t formally declared in the OpenAPI spec; the verified shape from production includes:
FieldTypeNotes
idstring (UUID)Strain identifier — use this everywhere
namestringDisplay name
imageUrlstringS3 path or full URL — see § Image URLs
wholeSalePricenumber (USD)Holder’s wholesale cost
retailPricenumber (USD)Suggested retail to the customer
currencystring"USD" for the canonical price
descriptionstringMarketing copy
thcContent / cbdContentnumberCannabinoid percentages
strainTypestringe.g. INDICA, SATIVA, HYBRID
availableCountriesarrayCountries this strain ships to
⚠️ Field set varies by deployment. Always defensively read fields (strain.imageUrl ?? null) — fields can be added without notice.

Worked examples

cURL:
PAYLOAD='countryCode=GBR&page=1&limit=10'
SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -sign ~/.drgreen/secret.pem | base64 -w0)
curl "https://api.drgreennft.com/api/v1/dapp/strains?$PAYLOAD" \
  -H "x-auth-apikey: $DRGREEN_API_KEY_B64" \
  -H "x-auth-signature: $SIG"
Node.js / TypeScript:
import { canonicalPayload, authHeaders } from './sign';
async function listStrains(countryCode: string, page = 1, limit = 10) {
  const query = { countryCode, page: String(page), limit: String(limit) };
  const payload = canonicalPayload('GET', null, query);
  const headers = authHeaders(process.env.DRGREEN_API_KEY!, process.env.DRGREEN_SECRET_KEY!, payload);
  const url = new URL('https://api.drgreennft.com/api/v1/dapp/strains');
  Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
  const res = await fetch(url, { headers });
  const json = await res.json();
  if (!res.ok) throw new Error(`${json.statusCode}: ${json.message}`);
  return json.data; // { strains: [...], pageMetaDto: {...} }
}
Python:
import os, httpx
from urllib.parse import urlencode
from sign import canonical_payload, auth_headers

def list_strains(country_code: str, page: int = 1, limit: int = 10):
    query = {"countryCode": country_code, "page": str(page), "limit": str(limit)}
    payload = canonical_payload("GET", query=query)
    headers = auth_headers(os.environ["DRGREEN_API_KEY"], os.environ["DRGREEN_SECRET_KEY"], payload)
    url = f"https://api.drgreennft.com/api/v1/dapp/strains?{urlencode(query)}"
    r = httpx.get(url, headers=headers, timeout=15.0)
    r.raise_for_status()
    return r.json()["data"]
PHP:
function list_strains(string $countryCode, int $page = 1, int $limit = 10): array {
    $query = ['countryCode' => $countryCode, 'page' => (string)$page, 'limit' => (string)$limit];
    $payload = dr_green_canonical('GET', null, $query);
    $headers = dr_green_headers($_ENV['DRGREEN_API_KEY'], $_ENV['DRGREEN_SECRET_KEY'], $payload);
    $url = 'https://api.drgreennft.com/api/v1/dapp/strains?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
    $ch = curl_init($url);
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers]);
    $res = curl_exec($ch);
    if ($res === false) throw new RuntimeException('curl: ' . curl_error($ch));
    $body = json_decode($res, true);
    if (($body['statusCode'] ?? 500) !== 200) throw new RuntimeException($body['message'] ?? 'unknown');
    return $body['data'];
}

Errors specific to this endpoint

StatusCauseFix
400 "countryCode must not be empty"Missing countryCode query paramAlways send it
400 "countryCode must be a valid ISO 3166-1 alpha-3"You sent "GB" instead of "GBR"Use alpha-3 codes
200 with strains: []No strains available in that countryShow an empty-state UI; not an error

GET /dapp/strains/{strainId}

Get one strain with full detail. Use this when rendering a strain detail page or refreshing data on an item already in the cart.

Path parameters

NameTypeDescription
strainIdstring (UUID)The id from the list endpoint

Canonical payload (for signing)

{} — no query params, the empty-params fallback. Not "". See 02-authentication.md § Canonical payload.

Response shape

{
  "success": true,
  "statusCode": 200,
  "message": "Success",
  "data": {
    "id": "c77dd198-ec9a-4ced-8105-937fde104424",
    "name": "Femme Fatale",
    "imageUrl": "33eac80b-58c4-46d3-a82b-b70c875d333f-cakes-and-cream.png",
    "wholeSalePrice": 99,
    "retailPrice": 165,
    "currency": "USD",
    "description": "...",
    "thcContent": 22.5,
    "cbdContent": 0.3,
    "strainType": "HYBRID",
    "availableCountries": [ /* alpha-3 codes */ ],
    "createdAt": "2025-02-26T05:49:11.465Z",
    "updatedAt": "2025-05-13T13:05:08.284Z"
  }
}

Worked example (cURL)

STRAIN_ID="c77dd198-ec9a-4ced-8105-937fde104424"
SIG=$(printf '%s' '{}' | openssl dgst -sha256 -sign ~/.drgreen/secret.pem | base64 -w0)
curl "https://api.drgreennft.com/api/v1/dapp/strains/$STRAIN_ID" \
  -H "x-auth-apikey: $DRGREEN_API_KEY_B64" \
  -H "x-auth-signature: $SIG"

Errors

StatusCauseFix
404 "Strain not found"Strain was soft-deleted by Dr Green adminRefresh your local cache, drop the stale ID
401 "User is not authorized"Signed "" instead of "{}"Use the canonical payload "{}"

Image URLs

Strain imageUrl values come in two forms:
  1. A bare filename / S3 key — e.g. 33eac80b-58c4-46d3-a82b-b70c875d333f-cakes-and-cream.png
  2. A full URL — e.g. https://cdn.drgreennft.com/strains/...
Defensively prefix bare keys with the Dr Green CDN base when rendering:
const imageSrc = strain.imageUrl?.startsWith('http')
  ? strain.imageUrl
  : `https://cdn.drgreennft.com/strains/${strain.imageUrl}`;
🔒 The CDN base URL for bare-key images is not formally documented and may differ between staging and production. Confirm with Dr Green; I’ve inferred this pattern from production but haven’t verified the exact base.

Caching guidance

The catalogue changes infrequently — new strains land, occasionally one is removed. Cache aggressively:
  • List response: TTL 5 minutes per (countryCode, page, limit, search) cache key.
  • Detail response: TTL 10 minutes per strainId.
  • 404 on detail: drop the strain from your local index immediately and refresh the list.

See also

  • Carts — adding strains to a customer’s cart
  • Orders — placing orders, including the strain price snapshot at order time
  • Revenue — sales aggregated by strain