The medgrid command line puts your catalog on MedGrid without integration code: import products, prices and stock straight from CSV exports, track approvals, and receive signed order notifications. One pip install, five commands.
# install once pip install medgrid-cli # connect with the credentials MedGrid issued you medgrid init connected ✓ vendor=your-store scope='sync' # load your whole catalog from a spreadsheet export medgrid import products.csv imported: 412 succeeded, 0 failed medgrid status --file skus.txt # track approval
Requires Python 3.9+ and nothing else — the tool has zero dependencies.
Standard install with a standard upgrade path.
pip install medgrid-cli # or: pipx install medgrid-cli medgrid --version # update later pip install -U medgrid-cli
The tool is a single self-contained file — download and run it with any Python 3.9+.
curl -O https://medgrid.com/assets/medgrid/cli/medgrid python3 medgrid --help
MedGrid issues you three values per environment: a public client_id (mgk_…), a client_secret for API authentication, and a webhook_secret that signs the notifications we send you. They are shown exactly once at generation — store them securely.
Stores credentials in a locked-down local file (./.medgrid/config.json, mode 600), verifies them with a live connection test, and reports who you authenticate as. Access tokens are then obtained, cached and refreshed automatically — you never handle them. Failures come back in plain language: wrong environment, bad secret, profile not yet approved, locked out.
$ medgrid init Environment [uat/prod] (uat): client_id: mgk_1a2b3c… client_secret: (hidden) webhook_secret (optional): (hidden) verifying against https://uat.medgrid.com ... connected ✓ vendor=your-store scope='sync'
MedGrid runs two identical copies of the platform. UAT (uat.medgrid.com) is the test environment — build and verify there first; orders never result in real shipments or charges. Production (medgrid.com) is the live marketplace. You receive UAT credentials to integrate against, and a separate production set at go-live.
--env uat or --env prod; UAT is the default, and production always announces itself with a visible banner before anything runs. Not sure which environment issued the credentials you're holding? medgrid check <client_id> answers without needing the secret.Exit codes are honest — 0 only when everything succeeded — so every command is safe to use in your own scripts and schedulers. warehouses, status and orders accept --json for machine-readable output.
| Command | What it does |
|---|---|
medgrid init | Store credentials for an environment and verify them live. Scriptable via --client-id / --client-secret / --webhook-secret; prompts otherwise. |
medgrid whoami / auth | Show the authenticated vendor, account class, permitted scope, and session expiry. auth forces a fresh session. |
medgrid check <client_id> | No secret needed: asks each environment whether it issued this client_id. Each probe counts one failed attempt toward that ID's lockout. |
| Command | What it does |
|---|---|
medgrid warehouses [--create NAME] | List your registered warehouses, or register one. Required before stock uploads. |
medgrid import FILE | Push a CSV of products, prices or stock — see CSV import. --dry-run validates without sending; --type overrides the filename guess. |
medgrid status SKU… [--file skus.txt] | Approval and publish state for up to 500 SKUs: Pending / Approved, live or not. |
| Command | What it does |
|---|---|
medgrid orders | Recent orders containing your items, newest first (default: last 7 days; filter with --since, --status). Your safety net if your endpoint missed a notification. |
medgrid webhook set-url URL | Register where MedGrid should deliver event notifications. |
medgrid webhook test | Fire a real, signed test event at that URL right now and report how your endpoint answered. |
medgrid webhook verify | Offline check that a payload you received was genuinely signed by MedGrid. |
medgrid listen [--port 9876] | Local test receiver: prints each incoming event with its signature verdict while you build your real endpoint. Pair with a tunnel to receive UAT deliveries on your machine. |
Headers are case-insensitive and common synonyms are understood (title → name, category → group, item_code → sku). Prices accept $ signs and thousands commas. Rows that fail validation are skipped with a line-numbered reason and are never partially sent.
| Type | Columns | Required per row |
|---|---|---|
products | sku vendor_product_id name description group price cost image | sku or vendor_product_id |
prices | sku price price_list | all three — price_list is the list name MedGrid assigns you |
inventory | sku warehouse qty snapshot_at | sku, warehouse, qty |
Every batch carries an idempotency key derived from the file's content. Re-running the same file — after a network failure, a crash, even two runs at once — replays the original result instead of applying it twice. Never creates duplicates. Imports upsert: an existing SKU is updated, and if a SKU appears twice in one file, the later row wins.
Everything you push enters MedGrid's review queue and appears on the marketplace only when approved — track it with medgrid status. Large files are batched automatically (500 rows per request); one bad row fails that row only. Inventory snapshots are timestamped, so a stale file can never overwrite newer stock.
MedGrid POSTs JSON events to your registered URL: order.new, order.cancelled, product.approved, product.rejected, webhook.test. Respond with any 2xx within 10 seconds; failed deliveries retry three times (after 1, 5 and 15 minutes). If your endpoint was down longer, reconcile with medgrid orders — its payload mirrors the webhook body.
The X-MedGrid-Signature header is HMAC-SHA256 of the exact request body, keyed with your webhook_secret. Always verify before trusting a payload:
import hashlib, hmac expected = "sha256=" + hmac.new( webhook_secret.encode(), raw_body, hashlib.sha256 ).hexdigest() assert hmac.compare_digest( expected, headers["X-MedGrid-Signature"])
Prove the wiring before any real order depends on it.
# 1. watch real signed events locally medgrid listen # 2. point MedGrid at your endpoint medgrid webhook set-url https://you.example/events # 3. fire a signed test at it medgrid webhook test delivered ✓ your endpoint answered HTTP 200
| Limit | Detail |
|---|---|
| 120 API requests / minute / vendor | Exceeding it returns HTTP 429 with a Retry-After header. A full catalog import is only a handful of requests — you're unlikely to hit this outside tight polling loops. |
| 500 records per batch | The CLI batches large files automatically. Also 500 SKUs per status call. |
| 10 failed logins / 15 minutes | Locks the client_id temporarily. Fix the secret before retrying. |
| Symptom | Meaning / fix |
|---|---|
this environment has never issued this client_id | Your credentials belong to the other environment. medgrid check <client_id> tells you which one issued them; then pass --env accordingly. |
the client_id is known here, but the client_secret is wrong | Re-copy the secret (watch for whitespace), or ask MedGrid to rotate credentials. |
the vendor profile is not Approved yet | Your MedGrid vendor profile hasn't finished onboarding — contact your MedGrid representative. |
temporarily locked out | Too many failed authentications. Wait ~15 minutes; fix the secret before retrying. |
Unknown price list on a prices import | Use the price list name MedGrid assigned to your account. |
webhook test reports your endpoint's error status | Your receiver is reachable but rejecting the request — check body parsing and that you respond 2xx. |
An identical request is already in progress | The same file is being imported concurrently; the CLI waits and retries automatically. |
Everything the tool does rides on the Vendor API — the same REST surface is yours for deeper integrations: direct sync from your systems, custom fulfillment flows, pharmacy endpoints.
Read the API reference