Skip to main content
Technical Preview

Overview

A client-mode connector for the Shopify GraphQL Admin API (version 2026-10). Use it to connect the shop floor to your online store: when a counter or scale reports finished goods, adjust the sellable stock in Shopify, and push the list of open, unfulfilled orders to MQTT so a packing line or pick-to-light system can work through it. Every GraphQL event is a POST to /admin/api/2026-10/graphql.json. The event only decides the MQTT topics; the GraphQL document and its variables travel in the payload as body. Canonical bodies for each event are below. Topics use finance/shopify/<object>/<verb>; replies land on <topic>/response. Set BASE_ADDRESS to your store’s permanent https://<store>.myshopify.com domain (not a custom storefront domain). See the base guide: REST API Connectors.
Auth: every GraphQL call sends the X-Shopify-Access-Token header with access_token from the payload. Get a token in one of these ways:
  • Dev Dashboard app, client credentials. Create an app in the Dev Dashboard in the same Shopify organisation as the store, install it with the scopes below, and publish its client id and secret to ClientCredentialsToken. The token lasts 24 hours (expires_in 86399). This only works for stores in your own organisation; other stores answer shop_not_permitted.
  • Existing admin-created custom app. Its shpat_… Admin API access token does not expire. New admin-created custom apps can no longer be made.
  • Public or custom-distribution app. Run the authorization code grant outside the broker and pass the token.
Scopes: read_orders, read_inventory, write_inventory, read_locations and read_products. Keep credentials in Hub secrets (suggested names SHOPIFY_STORE, SHOPIFY_CLIENT_ID, SHOPIFY_CLIENT_SECRET, SHOPIFY_ACCESS_TOKEN). Never commit them into the .lot.
Idempotency is required. From API version 2026-04, inventoryAdjustQuantities must carry the @idempotent(key: …) directive. Use one key per physical count event (for example line3-counter7-000123) and send the same key when you retry, so a retried message never counts stock twice. Shopify remembers keys for 24 hours. Each change must also set changeFromQuantity: the quantity you expect before the change (Shopify rejects the change with CHANGE_FROM_QUANTITY_STALE if it moved), or null to skip that check.
GraphQL errors come back with HTTP 200. Check the top-level errors array (syntax, THROTTLED, access denied) and the mutation’s userErrors array (validation) in every reply. Only a bad token (401), a wrong store (404) and a few store states (402, 423) change the HTTP status.

Basic Syntax

MQTT examples

Get a token (client credentials)

Publish to finance/shopify/oauth/token:
Reply on finance/shopify/oauth/token/response: {"access_token":"…","scope":"read_inventory,write_inventory,…","expires_in":86399}.

Find the inventory item for a SKU

Publish to finance/shopify/inventory-item/list:
The reply gives the inventory item id, and for each location its id and the available and on_hand quantities. Use these ids in the adjustment. GetLocations lists all locations ({"query":"query Locations($first: Int!) { locations(first: $first) { nodes { id name isActive } } }","variables":{"first":20}}).

Adjust stock from a sensor count

Publish to finance/shopify/inventory/adjust (here: 5 finished units counted on line 3):
delta is the change, not the new total. Use a negative delta to remove stock. reason must be one of Shopify’s reasons, for example correction, cycle_count_available or received. referenceDocumentUri shows up in the store’s inventory history, so point it at your own system (gid://coreflux/…). A good reply has "userErrors": [] and quantityAfterChange for each change.

Publish the pick list

Publish to finance/shopify/order/list:
The reply lists open, unfulfilled orders with SKU, name and quantity for each line. When pageInfo.hasNextPage is true, publish again with "after" set to pageInfo.endCursor. read_orders only reaches recent orders; older ones need the read_all_orders scope.

Smoke test

Publish to finance/shopify/shop/get:

Keeping the token

Client-credentials tokens last 24 hours. These two actions fetch a new one from Hub secrets twice a day and store it:
Restrict who can read the finance/shopify/# topics with a rule, because these payloads carry tokens and client credentials.

Webhooks (companion, not packaged)

Shopify can POST an orders/create webhook to an HTTPS URL. Each delivery carries X-Shopify-Hmac-SHA256, a base64 HMAC-SHA256 of the raw body made with the app’s client secret, and should be rejected if it does not match. A REST API connector in server mode cannot check that signature, so the receiver is not part of this template. Options: put a verifying proxy in front of a separate ENABLE_SERVER connector, use Shopify’s Amazon EventBridge or Google Cloud Pub/Sub delivery, or poll GetOrders on a timer (for example with created_at:>… in q).

Notes

  • API versions are released every quarter and each is supported for at least 12 months. 2026-10 is accessible until October 2027. Change the version in each ENDPOINT when you upgrade; a retired version is answered by the oldest supported one.
  • Rate limits use a calculated query cost in a leaky bucket per app and store; the size and restore rate depend on the store’s plan. Each reply carries extensions.cost. On THROTTLED, wait and retry with the same idempotency key.
  • Common errors: 401 [API] Invalid API key or access token (bad or expired token), ACCESS_DENIED in errors (missing scope), userErrors such as CHANGE_FROM_QUANTITY_STALE or an invalid reason, 404 (wrong store domain).
  • Not covered: the REST Admin API (legacy), bulk operations, product and fulfillment mutations, inventorySetQuantities (sets an absolute quantity).
  • Suggested branches: feat/rest-api-route-shopify, feat/docs-rest-api-shopify (Hugo pushes).
Last modified on October 21, 2026