Skip to main content

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 just curl + openssl), and an (apiKey, secretKey) pair from a Dr Green NFT holder.

What you’re building

A minimum-viable Dr Green-backed store flow:
  1. ✅ Verify your key pair works against the live API
  2. 🛒 Browse the strain catalogue (filtered to a country)
  3. 👤 Onboard a customer (creates a “client” record + triggers KYC via FirstAML)
  4. ⏳ Wait for KYC + admin approval
  5. 📦 Place an order
  6. 🔔 Detect status changes via polling
Once this works, scale it into a real storefront.

Step 0 — set up the helpers

This walkthrough uses Node.js / TypeScript. Equivalent code in Python, cURL, and PHP is in examples/{python,curl,php}/.
Save your key pair as environment variables. Never commit them to git. Use .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:
Run it:
Expected output:
If you see 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 is strains: [], the holder doesn’t have any products available in that country. Try GBR, USA, ZAF, or DEU. 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 from false/PENDING to true/VERIFIED before the customer can transact:
  1. isKYCVerified — flipped by FirstAML’s callback once verification completes
  2. adminApproval — flipped by Dr Green’s admin team after manual review
Polling pattern (60-second intervals with 10% jitter, terminal-state detection): 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

Once isKYCVerified=true, adminApproval=VERIFIED, and isActive=true, the customer can buy. Note the order needs:
  • clientId — from step 3
  • shippingId — from the client’s shippings[] array (call GET /dapp/clients/{id} to read it)
  • orderLines[]{strainId, quantity} per line; strain must be available in the customer’s country
  • paymentMethodCRYPTO (CoinRemitter), FIAT (Payinn), or PGPAY
step5_place_order.ts:
⚠️ POST /dapp/orders is not idempotent. A retry creates a duplicate. If the call fails, do NOT blindly retry — first GET /dapp/orders and 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 inside orderDetails — unlike most other endpoints which put data at the top of data. 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:
  1. Build a cart first (POST /dapp/carts), let the customer add/remove items, then convert the cart into an order. See reference/carts.md.
  2. Display prices in the customer’s local currency, using localPrice.currency and localPrice.totalAmount from the order detail rather than totalAmount (USD). See orders.md.
  3. Implement webhook-style polling at scale with active-set tracking and jittered intervals across many customers. See 06-webhooks.md § The polling pattern.
  4. Handle errors and retries properly including the no-Idempotency-Key workaround for writes. See 04-errors.md § Retry guidance.

Where to go next


Common stumbles when getting started