Developer API Reference
Complete technical reference for all public-facing MedGrid APIs. This guide covers authentication, endpoint specifications, request/response schemas, error handling, and integration details for Shopify and ShipStation.
Base URLs
https://app.medgrid.com
https://sandbox.medgrid.com
Endpoint URL Pattern
All API endpoints follow the Frappe RPC pattern:
https://app.medgrid.com/api/method/{python.module.path.function_name}
RESTful resource endpoints follow:
https://app.medgrid.com/api/resource/{DocType}/{name}
API Groups
Browse warehouses, list available inventory, search products. Used by customers and sales reps to access the MedGrid product catalog.
Vendor self-service hub. Connect, test, sync, and disconnect ShipStation and Shopify integrations from /portal/integrations.
Admin-level APIs for the MedGrid–Shopify store connection. OAuth installation, catalog sync, inventory sync, order creation, and inbound webhooks.
Vendor-facing APIs that push MedGrid Sales Orders into a vendor's ShipStation queue and pull tracking numbers back automatically.
Authentication
MedGrid supports two authentication methods. Token-based authentication is required for all server-to-server integrations.
Token Authentication (Recommended)
Every authenticated request must include the following HTTP header:
Authorization: token {api_key}:{api_secret}
Content-Type: application/json
Accept: application/json
Generate API Credentials
- Log in to the MedGrid Portal.
- Navigate to User Settings → API Access.
- Click Generate Keys.
- Copy the
API KeyandAPI Secret— the secret is shown only once.
Session Token (Browser)
Browser-based portal users authenticate via session cookie obtained at login:
/api/method/login{ "usr": "user@example.com", "pwd": "password" }
On success, a sid session cookie is set automatically for all subsequent browser requests.
Public Endpoints (No Auth)
The following endpoints accept unauthenticated requests. Each is designed to receive inbound data from external systems:
| Endpoint | Purpose |
|---|---|
medgrid.exoceuticals.webhook | Inbound Shopify webhook — HMAC-verified order updates. |
medgrid.exoceuticals.oauth_callback | Shopify OAuth redirect target after merchant authorises the app. |
medgrid.integrations.shopify_callback | Shopify OAuth redirect for vendor self-serve Shopify connections. |
medgrid.contact_api.submit_contact_form | Website contact form submission — no auth required for lead capture. |
API Conventions
Consistent patterns used across all MedGrid APIs.
HTTP Methods
| Method | Usage |
|---|---|
| GET | Read-only data retrieval. Parameters passed as query string. |
| POST | Create, trigger, or mutate data. Body must be JSON. |
| PUT | Update an existing resource entirely. |
| DELETE | Remove a resource. |
Response Envelope
All RPC method responses wrap the return value in a message key:
{ "message": { ...your data... } }
REST resource responses wrap data in a data key:
{ "data": { ...document fields... } }
Dates & Times
- Dates:
YYYY-MM-DD(e.g.2026-06-18) - Datetimes:
YYYY-MM-DD HH:MM:SS(e.g.2026-06-18 14:30:00) - All times are in UTC.
Pagination
List endpoints accept limit_start (offset) and limit_page_length (page size, default 20, max 500) parameters for pagination.
# Get items 21–40
GET /api/resource/Sales Order?limit_start=20&limit_page_length=20
list_warehouses
Auth RequiredReturns all active, non-group warehouses. Use to populate a warehouse picker.
/api/method/medgrid.catalog.list_warehouses
Parameters
None.
Response
{
"message": [
{
"name": "ShipStation Warehouse - SM",
"warehouse_name": "ShipStation Warehouse",
"company": "Skydell Medical"
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
name | string | Unique warehouse identifier. Pass this as the warehouse param in other catalog calls. |
warehouse_name | string | Human-readable display name. |
company | string | Company the warehouse belongs to. |
list_warehouses_ordered
Auth RequiredReturns warehouses ordered by the priority list configured in Medgrid Settings, then alphabetically. Preferred warehouses appear first.
/api/method/medgrid.catalog.list_warehouses_ordered
Parameters
None.
Response
{
"message": [
{ "name": "ShipStation Warehouse - SM", "label": "ShipStation Warehouse - SM" },
{ "name": "Exoceuticals Warehouse - SM", "label": "Exoceuticals Warehouse - SM" }
]
}
items_in_warehouse
Auth RequiredReturns all items with actual_qty > 0 in the specified warehouse, joined with Item Price for the given price list.
/api/method/medgrid.catalog.items_in_warehouse
Query Parameters
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
warehouse | string | Required | — | Warehouse name from list_warehouses. |
price_list | string | Optional | Selling Settings default | Price List to apply for the rate column. |
Response
{
"message": [
{
"item_code": "VD3-5000-120",
"item_name": "Vitamin D3 5000 IU (120 Softgels)",
"description": "High-potency Vitamin D3 supplement.",
"stock_uom": "Box",
"rate": 28.50,
"available_qty": 150
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
item_code | string | Product SKU — primary key used in order line items. |
item_name | string | Display name. |
description | string | Product description (may contain HTML). |
stock_uom | string | Unit of measure. |
rate | number | null | Price from the specified price list. null if no price record exists. |
available_qty | number | Current stock available for ordering. |
search_items
Auth RequiredFull-text search across item_name and description. Returns only items with positive stock. When warehouse is supplied, results are strictly filtered to that warehouse only.
/api/method/medgrid.catalog.search_items
Query Parameters
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Required | — | Search string — matched as LIKE %query% against item_name and description. |
warehouse | string | Optional | All warehouses | Strictly filters to this warehouse when provided. |
price_list | string | Optional | Selling Settings default | Price List for the rate column. |
Response
{
"message": [
{
"item_code": "VD3-5000-120",
"item_name": "Vitamin D3 5000 IU (120 Softgels)",
"description": "High-potency Vitamin D3 supplement.",
"stock_uom": "Box",
"rate": 28.50,
"available_qty": 150,
"warehouse": "ShipStation Warehouse - SM"
}
]
}
warehouse is omitted, the response includes a warehouse column per row indicating where the stock is located.
list_connections
Auth RequiredReturns the integration registry plus the calling user's current connection status for each integration. Credentials are never returned — only metadata and status flags.
/api/method/medgrid.integrations.list_connections
Parameters
None.
Response
{
"message": {
"integrations": [
{
"id": "shipstation",
"label": "ShipStation",
"tagline": "Push your MedGrid orders into ShipStation, pull tracking back.",
"auth_kind": "api_key_secret",
"status": "connected",
"last_sync_at": "2026-06-18 10:30:00",
"last_error": null,
"stats": { "orders_pushed": 12, "tracking_pulled": 8 },
"has_credentials": true,
"credential_keys": ["api_key", "api_secret"]
},
{
"id": "shopify",
"label": "Shopify",
"status": "not_connected",
"last_sync_at": null,
"last_error": null,
"stats": {},
"has_credentials": false
},
{
"id": "sendblue",
"label": "SMS / iMessage support",
"status": "connected",
"last_sync_at": "2026-05-10 08:00:00",
"stats": { "verified_phone": "+15551234567" }
}
],
"user": "vendor@example.com"
}
}
Status Values
| Value | Meaning |
|---|---|
connected | Credentials saved and verified. |
not_connected | No credentials stored yet. |
error | Credentials exist but last test failed. |
syncing | A sync operation is currently running. |
connect_shipstation
Auth RequiredValidates and saves ShipStation API credentials in one step. Credentials are tested against ShipStation before being persisted — if they are invalid, nothing is saved.
/api/method/medgrid.integrations.connect_shipstation
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
api_key | string | Required | ShipStation API Key from Account Settings → API Settings. |
api_secret | string | Required | ShipStation API Secret. |
Request
{
"api_key": "ss-api-key-here",
"api_secret": "ss-api-secret-here"
}
Response — Success
{
"message": {
"ok": true,
"integration": "shipstation",
"status": "connected"
}
}
Response — Failure
{
"message": {
"ok": false,
"error": "Invalid API Key or Secret — ShipStation rejected the credentials.",
"status": "error"
}
}
test_connection
Auth RequiredRe-tests an existing connection's stored credentials against the external service. Updates the integration status to connected or error.
/api/method/medgrid.integrations.test_connection
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
integration_type | string | Required | One of: shipstation, shopify. |
Response
{ "message": { "ok": true, "error": null } }
sync_now
Auth RequiredTriggers a manual full sync for the specified integration. For ShipStation: pushes up to 25 unfulfilled orders and pulls tracking for previously pushed orders. For Shopify: pulls the product catalog.
/api/method/medgrid.integrations.sync_now
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
integration_type | string | Required | shipstation or shopify. |
Response — ShipStation
{
"message": {
"ok": true,
"stats": {
"orders_pushed": 3,
"tracking_pulled": 5,
"round_trip_ok": true,
"push_errors": [],
"pull_errors": []
}
}
}
Response — Shopify
{
"message": {
"ok": true,
"stats": {
"products_pulled": 47,
"round_trip_ok": true,
"sample_titles": ["Product A", "Product B"]
}
}
}
disconnect
Auth RequiredRemoves stored credentials for an integration. The integration status returns to not_connected.
/api/method/medgrid.integrations.disconnect
Request Body
{ "integration_type": "shipstation" }
Response
{ "message": { "ok": true, "integration": "shipstation" } }
shopify_start_install
Auth RequiredGenerates the Shopify OAuth authorisation URL for the vendor's store. The caller opens this URL in a browser popup to begin the OAuth flow.
/api/method/medgrid.integrations.shopify_start_install
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
shop_domain | string | Required | Vendor's Shopify store domain. Must end in .myshopify.com. |
Request
{ "shop_domain": "your-store.myshopify.com" }
Response
{
"message": {
"ok": true,
"authorize_url": "https://your-store.myshopify.com/admin/oauth/authorize?client_id=...&scope=...&state=abc123",
"state": "abc123nonce"
}
}
Open authorize_url in a browser. After the merchant approves, Shopify calls shopify_callback automatically.
shopify_callback
Public (OAuth Redirect)Shopify OAuth redirect target. Called automatically by Shopify after the merchant approves the connection. Exchanges the authorization code for an access token and persists the integration.
/api/method/medgrid.integrations.shopify_callback
This endpoint is called by Shopify — you do not call it directly. Shopify appends these query parameters automatically:
| Param | Provided By | Description |
|---|---|---|
code | Shopify | Temporary authorization code to exchange for an access token. |
hmac | Shopify | HMAC-SHA256 signature of the callback URL params. |
shop | Shopify | The .myshopify.com shop domain. |
state | Shopify (echoed) | The state nonce generated by shopify_start_install. Verified for CSRF protection. |
Success Response
{
"message": {
"ok": true,
"shop": "your-store.myshopify.com",
"redirect": "/portal/integrations?shopify=connected"
}
}
Shopify (Exoceuticals) Integration
MedGrid connects to the Exoceuticals Shopify store via the Shopify Admin GraphQL API (version 2026-04). This integration handles catalog sync, inventory sync, order creation, and inbound fulfillment webhooks.
Configuration (System Settings)
The following fields must be configured in System Settings → API Integration → Exoceuticals before using these APIs:
| Setting Field | Description | Example |
|---|---|---|
exoceuticals_shop_domain | The Shopify store domain. | 2899e4.myshopify.com |
exoceuticals_api_token | Admin API access token (obtained via OAuth callback). Stored encrypted. | shpat_xxxx |
exoceuticals_api_version | Shopify API version to use. | 2026-04 |
exoceuticals_webhook_secret | HMAC secret for verifying inbound webhooks. Set in Shopify Admin → Notifications → Webhooks. | Random string |
exoceuticals_order_tag | Tag applied to every order created in Shopify. Identifies MedGrid orders. | Skydell |
exoceuticals_location_id | Default Shopify fulfillment location GID. | gid://shopify/Location/12345 |
Item-Level Mapping Fields
These custom fields are added to the Item DocType to link MedGrid items with Shopify variants:
| Item Field | Description |
|---|---|
exoceuticals_product_id | Shopify Product GID (populated by sync_catalog). |
exoceuticals_variant_id | Shopify ProductVariant GID. Required to place orders via sync_to_exoceuticals. |
exoceuticals_inventory_item_id | Shopify InventoryItem GID. Required for sync_inventory. |
exoceuticals_qty | Last-known inventory quantity mirrored from Shopify. |
exoceuticals_last_synced | Timestamp of last successful sync for this item. |
oauth_install_url
Auth RequiredBuilds the Shopify install URL for the Exoceuticals Custom App OAuth flow. Admin opens this URL in a browser to authorise the connection.
/api/method/medgrid.exoceuticals.oauth_install_url
Parameters
| Param | Type | Required | Description |
|---|---|---|---|
shop | string | Optional | Override shop domain. Defaults to exoceuticals_shop_domain in System Settings. |
Response
{
"message": {
"install_url": "https://2899e4.myshopify.com/admin/oauth/authorize?client_id=...&scope=...&state=...",
"shop": "2899e4.myshopify.com",
"redirect_uri": "https://app.medgrid.com/api/method/medgrid.exoceuticals.oauth_callback"
}
}
oauth_callback
Public (OAuth Redirect)Shopify redirects here after the merchant authorises the app. Verifies the HMAC, exchanges the code for an Admin API access token, and persists it in System Settings.
/api/method/medgrid.exoceuticals.oauth_callback
Called automatically by Shopify. Verifies: (1) .myshopify.com domain, (2) state nonce, (3) HMAC-SHA256 signature across all query params.
Success Response
{
"status": "success",
"shop": "2899e4.myshopify.com",
"scope": "read_products,write_products,read_orders,write_orders,read_inventory,write_inventory",
"message": "Exoceuticals connected. You can close this tab."
}
Error Response
{
"status": "error",
"message": "HMAC verification failed."
}
sync_catalog
Auth RequiredPulls all products and variants from Shopify and writes their GIDs onto matching MedGrid Items (matched by SKU). Does NOT auto-create Items — catalog membership is curated, not auto-imported.
/api/method/medgrid.exoceuticals.sync_catalog
Parameters
None.
Response
{
"message": {
"status": "ok",
"matched": 142,
"unmatched_count": 3
}
}
Response Fields
| Field | Description |
|---|---|
matched | Number of Shopify variants successfully linked to MedGrid Items by SKU. |
unmatched_count | Number of Shopify variants whose SKU did not match any MedGrid Item. These are logged for review. |
sync_inventory
Auth RequiredRefreshes inventory quantities from Shopify for all Items that have an exoceuticals_inventory_item_id set. Also mirrors quantities into the Exoceuticals warehouse Bin records.
/api/method/medgrid.exoceuticals.sync_inventory
Response
{
"message": {
"status": "ok",
"updated": 138,
"warehouse": "Exoceuticals Warehouse - SM"
}
}
sync_to_exoceuticals
Auth RequiredCreates a Shopify order from a submitted MedGrid Sales Order. Tags the order "Skydell" per Exoceuticals' requirements. Can only be called once per Sales Order (idempotent guard).
/api/method/medgrid.exoceuticals.sync_to_exoceuticals
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
sales_order | string | Required | MedGrid Sales Order name (e.g. SAL-2026-00123). Must be submitted (docstatus=1). |
line_items | array | Optional | List of SO item row names to include. If omitted, all eligible lines are included. |
Request
{ "sales_order": "SAL-2026-00123" }
Response
{
"message": {
"status": "success",
"exoceuticals_order_id": "gid://shopify/Order/4567890123",
"exoceuticals_order_name": "#1042"
}
}
Prerequisites
- Sales Order must be submitted (
docstatus = 1). - Order must not already have an
exoceuticals_order_id. - At least one line item must have
exoceuticals_variant_idset on the Item — runsync_catalogfirst. - Sales Order must have a valid shipping address.
poll_exoceuticals_status
Auth RequiredQueries Shopify for the current fulfillment status of a previously synced order and updates the Sales Order fields accordingly. Used as belt-and-suspenders alongside webhooks.
/api/method/medgrid.exoceuticals.poll_exoceuticals_status
Request
{ "sales_order": "SAL-2026-00123" }
Response
{ "message": "Fulfilled" }
Returns the new exoceuticals_status string. Also updates exoceuticals_tracking_number, exoceuticals_carrier, and exoceuticals_tracking_url on the Sales Order if shipping data is available.
exoceuticals webhook
Public (HMAC-Verified)Inbound webhook endpoint for Shopify order updates. Register this URL in Shopify Admin → Settings → Notifications → Webhooks for the topics listed below.
/api/method/medgrid.exoceuticals.webhook
Required Headers (sent by Shopify)
| Header | Value | Purpose |
|---|---|---|
X-Shopify-Hmac-Sha256 | Base64-encoded HMAC-SHA256 | Signature verification. Computed over the raw request body using the webhook secret. |
X-Shopify-Topic | e.g. orders/fulfilled | The event topic that triggered this webhook. |
Content-Type | application/json | Shopify always sends JSON. |
Supported Topics
| Topic | MedGrid Action |
|---|---|
orders/updated | Re-evaluates order status; updates exoceuticals_status field on the Sales Order. |
orders/fulfilled | Sets status to Fulfilled; writes tracking number, carrier, and tracking URL from fulfillment data. |
orders/cancelled | Sets status to Cancelled on the matched Sales Order. |
Example Payload (orders/fulfilled)
{
"id": 4567890123,
"fulfillment_status": "fulfilled",
"cancelled_at": null,
"fulfillments": [
{
"tracking_number": "1Z999AA10123456784",
"tracking_company": "UPS",
"tracking_urls": ["https://ups.com/track?number=1Z999AA10123456784"]
}
]
}
Response
{
"message": {
"status": "ok",
"topic": "orders/fulfilled",
"sales_order": "SAL-2026-00123"
}
}
ShipStation Integration
The ShipStation integration enables vendors to receive MedGrid Sales Orders in their ShipStation account and automatically synchronise tracking numbers back to MedGrid.
Authentication
ShipStation uses HTTP Basic Auth. MedGrid constructs the header internally from the vendor's stored credentials:
Authorization: Basic BASE64(api_key:api_secret)
Integration Flow
| Step | Direction | API Call | Description |
|---|---|---|---|
| 1 | MedGrid → ShipStation | GET /accounts/listtags | Auth probe — confirms credentials are valid before any data is sent. |
| 2 | MedGrid → ShipStation | POST /orders/createorder | Pushes each eligible Sales Order as a new ShipStation order. |
| 3 | ShipStation → MedGrid | GET /shipments?orderId={id} | MedGrid queries ShipStation for tracking info on previously pushed orders. |
| 4 | Internal | MedGrid DB write | Tracking number and carrier are written to Delivery Note.lr_no and transporter. |
Order Push to ShipStation
When sync_now is called for ShipStation, MedGrid queries for submitted Sales Orders whose items' default_supplier matches the calling vendor and that have not yet been pushed (shipstation_order_id is empty).
Order Payload Structure
MedGrid sends this payload to POST https://ssapi.shipstation.com/orders/createorder:
{
"orderNumber": "SAL-2026-00123",
"orderKey": "SAL-2026-00123",
"orderDate": "2026-06-18",
"orderStatus": "awaiting_shipment",
"customerEmail": "customer@example.com",
"amountPaid": 91.00,
"items": [
{
"lineItemKey": "row-id-1",
"sku": "VD3-5000-120",
"name": "Vitamin D3 5000 IU (120 Softgels)",
"quantity": 2,
"unitPrice": 28.50
}
]
}
On success, ShipStation returns an orderId which MedGrid stores in Sales Order.shipstation_order_id to track the push.
Multi-Vendor Order Splitting
When a Sales Order contains items from multiple vendors, only the calling vendor's line items are included in the ShipStation payload. Items belonging to other vendors are pushed separately by those vendors on their own sync.
Tracking Number Pull
During each sync, MedGrid queries ShipStation for shipments on previously pushed orders and writes tracking info to the corresponding Delivery Note.
How Tracking Pull Works
- MedGrid queries all Sales Orders with a non-empty
shipstation_order_id. - For each, it calls
GET https://ssapi.shipstation.com/shipments?orderId={shipstation_order_id}. - For any shipment with a
trackingNumber, MedGrid finds the linked Delivery Note and sets:Delivery Note.lr_no= tracking numberDelivery Note.transporter= carrier code (uppercase)
// ShipStation shipment response (excerpt)
{
"shipments": [
{
"trackingNumber": "1Z999AA10123456784",
"carrierCode": "ups",
"shipDate": "2026-06-19"
}
]
}
Data Models
Key data structures returned by MedGrid APIs.
Integration Connection Object
{
"id": "shipstation",
"label": "ShipStation",
"tagline": "Push your MedGrid orders into ShipStation, pull tracking back.",
"auth_kind": "api_key_secret",
"status": "connected",
"last_sync_at": "2026-06-18 10:30:00",
"last_error": null,
"stats": {
"orders_pushed": 12,
"tracking_pulled": 8
},
"has_credentials": true,
"credential_keys": ["api_key", "api_secret"],
"permits": ["orders.write", "tracking.read"],
"what_we_push": ["MedGrid Sales Orders (line items, ship-to address, weight)"],
"what_we_pull": ["Tracking numbers", "Carrier + service", "Ship date"]
}
Catalog Item Object
{
"item_code": "VD3-5000-120",
"item_name": "Vitamin D3 5000 IU (120 Softgels)",
"description": "High-potency Vitamin D3 supplement.",
"stock_uom": "Box",
"rate": 28.50,
"available_qty": 150,
"warehouse": "ShipStation Warehouse - SM"
}
Sales Order Status Fields (Exoceuticals)
{
"exoceuticals_order_id": "gid://shopify/Order/4567890123",
"exoceuticals_order_name": "#1042",
"exoceuticals_status": "Fulfilled",
"exoceuticals_tracking_number": "1Z999AA10123456784",
"exoceuticals_carrier": "UPS",
"exoceuticals_tracking_url": "https://ups.com/track?number=1Z999AA10123456784",
"exoceuticals_sync_date": "2026-06-18 14:00:00"
}
Error Reference
Complete reference of HTTP status codes, error response formats, and integration-specific error codes.
Standard MedGrid Error Response
{
"exc_type": "ValidationError",
"exception": "frappe.exceptions.ValidationError: Both API Key and API Secret are required.",
"_server_messages": "[{\"message\": \"Both API Key and API Secret are required.\"}]"
}
HTTP Status Code Reference
| Code | Meaning | Common Cause | Resolution |
|---|---|---|---|
| 200 | Success | — | Request completed. Check message.ok for integration-level success flags. |
| 400 | Bad Request | Missing/invalid field. | Read _server_messages for the specific validation error. |
| 401 | Unauthenticated | Missing or malformed Authorization header. | Verify format: Authorization: token KEY:SECRET. |
| 403 | Forbidden | Role lacks permission, or endpoint requires a higher-level role (e.g. System Manager). | Contact MedGrid to confirm role assignment. |
| 404 | Not Found | Wrong DocType name, wrong document ID, or typo in endpoint URL. | Double-check the endpoint path and document name. |
| 409 | Conflict | Order already synced to the external system. | Do not resubmit — check the existing external order instead. |
| 417 | Expectation Failed | Business rule violation (e.g. SO not submitted, missing variant ID). | Read _server_messages for the specific condition that needs to be met. |
| 429 | Rate Limited | Too many requests (ShipStation: 40/min; Shopify: cost-bucket). | Implement exponential backoff. MedGrid handles retries automatically on internal calls. |
| 500 | Server Error | Unhandled exception on MedGrid's side. | Report to MedGrid support with timestamp, endpoint, and full error text. |
Integration-Specific Error Codes
| Integration | Error Indicator | Meaning | Fix |
|---|---|---|---|
| ShipStation | HTTP 401 from ShipStation | Invalid API Key/Secret. | Regenerate ShipStation credentials and reconnect via connect_shipstation. |
| ShipStation | HTTP 429 from ShipStation | Rate limit hit (40 req/min). | MedGrid retries automatically with backoff. No action needed unless persistent. |
| Shopify | userErrors array in 200 response | GraphQL mutation rejected (e.g. invalid variant GID, out-of-stock). | Check userErrors[].field and userErrors[].message for details. |
| Shopify OAuth | "HMAC verification failed" | Callback request did not originate from Shopify. | Ensure webhook secret matches what Shopify has configured. |
| Shopify OAuth | "OAuth state expired or invalid" | State nonce expired (10-minute window) or already used. | Restart the install flow — generate a new authorize_url. |
| Shopify | HTTP 401 on test_connection | Access token revoked in Shopify. | Disconnect and reconnect via OAuth to get a fresh token. |
| Shopify | HTTP 402 | Shopify store is frozen or past due on payment. | Merchant must update their Shopify billing before the API will respond. |
Webhook Error Response
{
"status": "error",
"code": "INVALID_TOKEN",
"message": "Authentication failed"
}
Rate Limits & Best Practices
Rate limits prevent API overload. MedGrid manages all external rate limits automatically.
MedGrid API Rate Limits
MedGrid does not enforce a hard rate limit on authenticated API calls from customers and vendors. Burst traffic may be throttled server-side. Implement exponential backoff for any 429 or 5xx response.
External System Rate Limits
| System | Limit | MedGrid Handling |
|---|---|---|
| ShipStation | 40 requests/minute per API key pair. | HTTP 429 triggers exponential backoff, up to 3 retries, before failing the sync with an error. |
| Shopify (GraphQL) | 50 query cost units/second (leaky bucket). Mutations cost more than queries. | MedGrid batches GraphQL calls to stay within the cost budget. Sync jobs process in chunks of 100 items. |
Performance Best Practices
- Cache
list_warehousesresults — warehouses change infrequently (safe to cache for 15 minutes). - Use webhooks for order status updates instead of polling
poll_exoceuticals_statuscontinuously. - Always develop and test against the Sandbox environment before switching to Production.
- Implement exponential backoff in your own code for any
429or500response from MedGrid. - Keep
sync_catalogas a scheduled daily job rather than calling it on every order.