Quickstart
Goal: From “I have a holder’s API key pair” to “I’ve placed an order in the customer’s name” in under 30 minutes. Prerequisites: Node.js 20+ (or Python 3.10+, or PHP 7.4+, or justcurl+openssl), and an(apiKey, secretKey)pair from a Dr Green NFT holder.
What you’re building
A minimum-viable Dr Green-backed store flow:- ✅ Verify your key pair works against the live API
- 🛒 Browse the strain catalogue (filtered to a country)
- 👤 Onboard a customer (creates a “client” record + triggers KYC via FirstAML)
- ⏳ Wait for KYC + admin approval
- 📦 Place an order
- 🔔 Detect status changes via polling
Step 0 — set up the helpers
This walkthrough uses Node.js / TypeScript. Equivalent code in Python, cURL, and PHP is inexamples/{python,curl,php}/.
.env + .gitignore, or your secrets manager:
Step 1 — verify the keys (sanity check)
Before you write any real code, prove your key pair signs correctly. The cheapest authenticated read is the dashboard summary.step1_verify.ts:
401 "User is not authorized": your signature is being rejected. Check 02-authentication.md § Common 401 causes. The most common cause is signing "" instead of "{}" for empty-query GETs.
Step 2 — browse the strain catalogue
Strains are country-filtered. The customer’s country determines what they can buy. Use ISO 3166-1 alpha-3 codes (GBR, USA, DEU — not GB, US, DE).
step2_strains.ts:
If the response isstrains: [], the holder doesn’t have any products available in that country. TryGBR,USA,ZAF, orDEU. If still empty, ask the holder to confirm their product allocation in the DAPP UI.
Step 3 — onboard a customer (this triggers KYC)
POST /dapp/clients creates a customer record AND kicks off the KYC flow. Dr Green’s backend will email the customer with verification instructions and fire a webhook to FirstAML to open a case. Your store has nothing more to do for KYC — just create the client and wait.
step3_create_client.ts:
⚠️ Don’t run this with real PII unless you’re ready — it creates a real client record on production and sends a real email. If you’re just exploring, use a throwaway email address you control.
Step 4 — poll for KYC + admin approval
Two things need to flip fromfalse/PENDING to true/VERIFIED before the customer can transact:
isKYCVerified— flipped by FirstAML’s callback once verification completesadminApproval— flipped by Dr Green’s admin team after manual review
step4_wait_for_verification.ts:
Tuning the interval. 5 minutes is the recommended polling cadence. Faster won’t make FirstAML or Dr Green’s admin review go quicker, and adds load to the API. Slower may delay your customer experience. See guides/kyc-flow.md for the full state machine.
Step 5 — place an order
OnceisKYCVerified=true, adminApproval=VERIFIED, and isActive=true, the customer can buy. Note the order needs:
clientId— from step 3shippingId— from the client’sshippings[]array (callGET /dapp/clients/{id}to read it)orderLines[]—{strainId, quantity}per line; strain must be available in the customer’s countrypaymentMethod—CRYPTO(CoinRemitter),FIAT(Payinn), orPGPAY
step5_place_order.ts:
⚠️POST /dapp/ordersis not idempotent. A retry creates a duplicate. If the call fails, do NOT blindly retry — firstGET /dapp/ordersand look for a recent matching order before attempting again. See 04-errors.md § Idempotency.
Step 6 — track the order
Same polling pattern as KYC. Watch for status transitions until terminal state:Notice the response wraps the data insideorderDetails— unlike most other endpoints which put data at the top ofdata. This is a known inconsistency. See orders.md § GET /dapp/orders/.
What you skipped
This walkthrough creates an order from a single strain directly. In a real store, you’d typically:- Build a cart first (
POST /dapp/carts), let the customer add/remove items, then convert the cart into an order. See reference/carts.md. - Display prices in the customer’s local currency, using
localPrice.currencyandlocalPrice.totalAmountfrom the order detail rather thantotalAmount(USD). See orders.md. - Implement webhook-style polling at scale with active-set tracking and jittered intervals across many customers. See 06-webhooks.md § The polling pattern.
- Handle errors and retries properly including the no-Idempotency-Key workaround for writes. See 04-errors.md § Retry guidance.
Where to go next
| If you want to… | Read… |
|---|---|
| Understand how everything fits together | guides/store-architecture.md |
| Master the KYC flow (FirstAML integration) | guides/kyc-flow.md |
| Track orders properly through their full lifecycle | guides/order-lifecycle.md |
| Reference any specific endpoint | reference/index.md |
| Debug a 401 | 02-authentication.md § Common 401 causes |
| Deploy with confidence | 03-environment.md |
Common stumbles when getting started
| Stumble | Symptom | Fix |
|---|---|---|
Signing "" for an empty-query GET | 401 "User is not authorized" | Sign "{}" instead. Use the helpers in examples/, don’t roll your own |
Sending Content-Type: application/json on a GET | 400 "is not valid JSON" | Only set Content-Type when you have a body |
| Using alpha-2 country codes | 400 "must be a valid ISO 3166-1 alpha-3" | GBR, not GB |
Trying to call /user/me or /dapp/users/nfts | 401 "Unauthorized" (43 bytes) | These are JWT-only; the holder must use the DAPP UI for them |
| Polling KYC status faster than 5 min | More 200s, no faster results | Stick to 5-minute polling — FirstAML’s pace is what it is |
Retrying POST /dapp/orders blindly | Duplicate orders | Always GET /dapp/orders first to check if the original landed |