CLI for the Shopify Admin GraphQL API. Designed for agentic use — all output is JSON, all parameters are flags, no interactivity, no colors.
npm install -g nori-shopify-cliOr run from a checkout:
npm install
npm run dev -- shop getThree environment variables:
| Variable | Required | Description |
|---|---|---|
SHOPIFY_SHOP |
yes | Shop subdomain (my-store) or full domain (my-store.myshopify.com) |
SHOPIFY_ADMIN_TOKEN |
yes | Admin API access token (shpat_...) |
SHOPIFY_API_VERSION |
no | Admin API version, defaults to 2026-04 |
Get a token from a custom app: Shopify admin → Settings → Apps and sales channels → Develop apps → create or open an app → Configure Admin API scopes → Install app → reveal the Admin API access token.
The scopes you grant the app determine what the CLI can do. Commonly needed: read_orders (plus read_all_orders for orders older than 60 days), read_customers, read_products, write_products, read_inventory, write_inventory, read_discounts, write_discounts, read_reports (for analytics).
nori-shopify --help| Command | Description |
|---|---|
shop get |
Shop metadata (name, domains, plan, currency, timezone) |
orders list |
List orders; --query, --first, --after, --sort-key, --reverse |
orders get <id> |
Full order with line items |
customers list |
List customers with order counts and total spend |
customers get <id> |
Single customer |
products list |
List products |
products get <id> |
Product with variants |
products create |
Create a product (--title required) |
products update <id> |
Update product fields |
inventory locations |
List locations |
inventory levels --item-id <id> |
Stock levels per location for an inventory item |
inventory adjust |
Adjust quantity (--item-id, --location-id, --delta) |
discounts list |
List code and automatic discounts |
discounts create-basic-code |
Create a discount code (--percentage or --amount + --currency) |
analytics query <shopifyql> |
Run a ShopifyQL aggregate query |
graphql --query <doc> |
Raw Admin GraphQL passthrough (--variables as JSON) |
IDs can be bare numbers (1001) or full GIDs (gid://shopify/Order/1001); bare numbers are wrapped automatically.
The graphql passthrough returns the raw data payload and does not inspect mutation userErrors — check them yourself when running mutations through it.
# Paid orders since January
nori-shopify orders list --query "financial_status:paid created_at:>'2026-01-01'"
# Top customers by spend
nori-shopify customers list --sort-key CREATED_AT --reverse --first 25
# Monthly sales for the last 3 months
nori-shopify analytics query "FROM sales SHOW total_sales GROUP BY month SINCE -3m ORDER BY month"
# Receive 10 units at a location
nori-shopify inventory adjust --item-id 5 --location-id 2 --delta 10 --name on_hand --reason received
# 10% off discount code
nori-shopify discounts create-basic-code --title "Summer Sale" --code SUMMER10 \
--percentage 10 --starts-at 2026-06-01T00:00:00Z
# Anything else via raw GraphQL
nori-shopify graphql --query 'query($id: ID!) { order(id: $id) { name } }' \
--variables '{"id": "gid://shopify/Order/1001"}'List commands return { nodes, pageInfo }. When pageInfo.hasNextPage is true, pass pageInfo.endCursor to --after to fetch the next page.
npm install
npm test # vitest
npm run build # tsc -> dist/See APPLICATION_SPEC.md for the full behavioral spec and docs.md for architecture notes.
Apache 2.0, see LICENSE and LICENSE-ADDENDUM.txt.