Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
[{"name":"SHOPIFY_ACCESS_TOKEN","prompt":"Shopify Admin API access token (starts with shpat_)","help":"Shopify admin → Settings → Apps and sales channels → Develop apps → Create an app → API credentials. Token shown ONCE on install."},{"name":"SHOPIFY_STORE_DOMAIN","prompt":"Your shop subdomain without protocol (e.g. my-store.myshopify.com)","help":"The permanent myshopify.com domain, not your custom domain."},{"name":"SHOPIFY_API_VERSION","prompt":"Shopify API version (default 2026-01)","help":"Stable quarterly version. Override if you need an older one."},{"name":"BANKR_API_KEY","prompt":"Bankr Agent API key (only required for the Bankr bridge sections)","help":"Generate at https://bankr.bot/api. Used to POST /agent/prompt and poll /agent/job/{id}."}]
The core Shopify sections of this skill are adapted from the upstream NousResearch/hermes-agent skill (MIT, author: community). The four "Bankr bridge" sections at the end are new and specific to the Bankr ecosystem.
Work with Shopify stores directly through curl: list products, manage inventory, pull orders, update customers, read metafields. No SDK, no app framework — just the GraphQL endpoint and a custom-app access token. Then bridge the merchant data to onchain primitives via Bankr.
The REST Admin API is legacy since 2024-04 and only receives security fixes. Use GraphQL Admin for all admin work. Use Storefront GraphQL for read-only customer-facing queries (products, collections, cart).
Prerequisites
In Shopify admin: Settings → Apps and sales channels → Develop apps → Create an app.
Click Configure Admin API scopes, select what you need (examples below), save.
Install app → the Admin API access token appears ONCE. Copy it immediately — Shopify will never show it again. Tokens start with shpat_.
Save to your env file:
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxx
SHOPIFY_STORE_DOMAIN=my-store.myshopify.com
SHOPIFY_API_VERSION=2026-01
BANKR_API_KEY=... # only for the Bankr bridge sections
Heads up: As of January 1, 2026, new "legacy custom apps" created in the Shopify admin are gone. New setups should use the Dev Dashboard (shopify.dev/docs/apps/build/dev-dashboard). Existing admin-created apps keep working. If the user's shop has no existing custom app and it's after 2026-01-01, direct them to Dev Dashboard instead of the admin flow.
Method: always POST, always Content-Type: application/json, body is {"query": "...", "variables": {...}}
HTTP 200 does not mean success. GraphQL returns errors in a top-level errors array and per-field userErrors. Always check both.
IDs are GID strings:gid://shopify/Product/10079467700516, gid://shopify/Variant/..., gid://shopify/Order/.... Pass these verbatim — don't strip the prefix.
Rate limit: calculated via query cost (leaky bucket). Each response has extensions.cost with requestedQueryCost, actualQueryCost, throttleStatus.{currentlyAvailable, maximumAvailable, restoreRate}. Back off when currentlyAvailable drops below your next query's cost. Standard shops = 100 points bucket, 50/s restore; Plus = 1000/100.
Inventory lives on inventory items tied to variants, quantities tracked per location.
# Get inventory for a variant across all locations
shop_gql '
query($id: ID!) {
productVariant(id: $id) {
id sku
inventoryItem {
id tracked
inventoryLevels(first: 10) {
edges { node { location { id name } quantities(names: ["available","on_hand","committed"]) { name quantity } } }
}
}
}
}''{"id":"gid://shopify/ProductVariant/..."}'
REST endpoints still exist but are frozen. Don't write new integrations against /admin/api/.../products.json. Use GraphQL.
Token format check. Admin tokens start with shpat_. Storefront public tokens with shpua_. If you have one and the wrong header, every request returns 401 without a useful error body.
403 with a valid token = missing scope. Shopify returns {"errors":[{"message":"Access denied for ..."}]}. Re-configure Admin API scopes on the app, then reinstall to regenerate the token.
userErrors is empty != success. Also check data.<mutation>.<resource> is non-null. Some failures populate neither — inspect the whole response.
GID vs numeric ID. Legacy REST gave numeric IDs; GraphQL wants full GID strings. To convert: gid://shopify/Product/<numeric>.
Rate limit surprise. A single products(first: 250) with deep nesting can cost 1000+ points and throttle immediately on a standard-plan shop. Start narrow, read extensions.cost, adjust.
Pagination order.products(first: N, reverse: true) sorts by id DESC, not created_at. Use sortKey: CREATED_AT, reverse: true for "newest first."
read_all_orders for historical data. Without it, orders(...) silently caps at the 60-day window. You won't get an error, just fewer results than expected. For Shopify Plus merchants with many orders, request this scope via the app's protected-data settings.
Currencies are strings. Amounts come back as "49.00" not 49.0. Don't jq tonumber blindly if you care about zero-padding.
Multi-currency Money fields have shopMoney (store's currency) AND presentmentMoney (customer's). Pick one consistently.
Safety
Mutations in Shopify are real — they create products, charge refunds, cancel orders, ship fulfillments. Before running productDelete, orderCancel, refundCreate, or any bulk mutation: state clearly what the change is, on which shop, and confirm with the user. There is no staging clone of production data unless the user has a separate dev store.
Bankr Bridges
The sections below are not part of the upstream Hermes skill. They show how to wire Shopify resources to Bankr's onchain primitives — handle resolution, x402 settlement, and webhook-driven token flows — using only what Bankr already exposes.
Bankr's agent natively resolves ENS, Twitter @handle, Farcaster handle, and wallet addresses for transfers and fee recipients. Email is not resolvable.
Bankr can call, host, and settle x402 endpoints; USDC is the unit.
Job pattern is submit → poll: POST /agent/prompt then GET /agent/job/{id}. There are no Bankr-side webhooks.
Shopify's primary customer key is email, which Bankr cannot resolve. The bridge: store a Bankr-resolvable handle on the customer as a metafield, then pass the string verbatim to Bankr.
Recommended metafield definition: namespace: custom, key: handle, type: single_line_text_field. Acceptable values: vitalik.eth, @dwr.eth, @username (Twitter), or a 0x… wallet address.
# Write a handle onto a customer at checkout / signup
shop_gql '
mutation($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields { id key value }
userErrors { field message code }
}
}''{"metafields":[{"ownerId":"gid://shopify/Customer/123","namespace":"custom","key":"handle","type":"single_line_text_field","value":"vitalik.eth"}]}'
Read it back when you need to pay or drop tokens:
HANDLE=$(shop_gql '
query($id: ID!) {
customer(id: $id) { metafield(namespace:"custom", key:"handle") { value } }
}''{"id":"gid://shopify/Customer/123"}' | jq -r '.data.customer.metafield.value')
# Hand off to Bankr — no extra resolution needed.
curl -sS -X POST https://api.bankr.bot/agent/prompt \
-H "Authorization: Bearer ${BANKR_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"send 10 USDC to ${HANDLE} on base\"}"
If the metafield is empty, prompt the customer for one of {ENS, Twitter, Farcaster, wallet} before any onchain action — never guess from email.
Bridge 2 — x402 checkout for a Shopify draft order
Bankr settles x402 endpoints natively. To accept USDC for a Shopify cart, mint a draft order, expose its total behind an x402-priced endpoint, and let Bankr pay it. On 200, mark the draft order as paid (or call draftOrderComplete).
# 1. Create a draft order from a cart
DRAFT=$(shop_gql '
mutation($input: DraftOrderInput!) {
draftOrderCreate(input: $input) {
draftOrder { id totalPriceSet { shopMoney { amount currencyCode } } invoiceUrl }
userErrors { field message }
}
}''{"input":{"lineItems":[{"variantId":"gid://shopify/ProductVariant/...","quantity":1}],"email":"buyer@example.com"}}')
DRAFT_ID=$(echo"$DRAFT" | jq -r '.data.draftOrderCreate.draftOrder.id')
TOTAL=$(echo"$DRAFT" | jq -r '.data.draftOrderCreate.draftOrder.totalPriceSet.shopMoney.amount')
Your service then exposes an HTTP 402 endpoint pricing this draft (USDC on Base) — see the upstream x402 spec. Bankr-side, the agent settles it:
# Bankr-side (agent): pay the x402 endpoint that fronts the draft order
curl -sS -X POST https://api.bankr.bot/agent/prompt \
-H "Authorization: Bearer ${BANKR_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"call x402 endpoint https://shop.example.com/x402/draft/${DRAFT_ID} and settle in USDC on base\"}"
When your x402 server confirms settlement, complete the draft on the Shopify side:
shop_gql '
mutation($id: ID!) {
draftOrderComplete(id: $id, paymentPending: false) {
draftOrder { order { id name } }
userErrors { field message }
}
}'"{\"id\":\"${DRAFT_ID}\"}"
Notes:
Use shopMoney consistently for x402 pricing so currency conversion stays on your side, not Shopify's.
Treat the x402 settlement tx hash as the source of truth — do not complete the draft until you've verified the hash.
Bridge 3 — Shopify webhook → Bankr Submit (loyalty drop on ORDERS_PAID)
Bankr has no inbound webhooks. The pattern is: Shopify webhook → your server → HMAC verify → read customer handle metafield → submit a Bankr job → poll.
# Subscribe once
shop_gql '
mutation($topic: WebhookSubscriptionTopic!, $sub: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(topic: $topic, webhookSubscription: $sub) {
webhookSubscription { id topic }
userErrors { field message }
}
}''{"topic":"ORDERS_PAID","sub":{"callbackUrl":"https://your.server/shopify/orders-paid","format":"JSON"}}'
Webhook handler pseudocode:
# 1. Verify HMAC (reject if it doesn't match)
EXPECTED=$(echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "$APP_SECRET" -binary | base64)
[ "$EXPECTED" = "$X_SHOPIFY_HMAC_SHA256" ] || exit 1
# 2. Pull the buyer's Bankr handle from the order's customer metafield
CUSTOMER_ID=$(echo"$REQUEST_BODY" | jq -r '.customer.admin_graphql_api_id')
HANDLE=$(shop_gql '
query($id: ID!) {
customer(id: $id) { metafield(namespace:"custom", key:"handle") { value } }
}'"{\"id\":\"${CUSTOMER_ID}\"}" | jq -r '.data.customer.metafield.value')
[ -z "$HANDLE" ] || [ "$HANDLE" = "null" ] && exit 0 # silently skip, no handle on file# 3. Submit the loyalty drop to Bankr
JOB=$(curl -sS -X POST https://api.bankr.bot/agent/prompt \
-H "Authorization: Bearer ${BANKR_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"send 100 LOYALTY to ${HANDLE} on base\"}" | jq -r '.job_id')
# 4. Poll until terminalwhile :; do
STATUS=$(curl -sS "https://api.bankr.bot/agent/job/${JOB}" \
-H "Authorization: Bearer ${BANKR_API_KEY}" | jq -r '.status')
case"$STATUS"in
completed|failed|cancelled) break ;;
esacsleep 2
done
Same shape works for: royalty splits on digital goods, Clanker token airdrops to repeat buyers, treasury sweeps when a sales threshold is hit.
Bridge 4 — Scope cheatsheet for the Bankr flows
Minimum Admin API scopes per bridge:
Flow
Required scopes
Identity bridge (read/write handle metafield)
read_customers, write_customers
x402 checkout via draft orders
read_customers, write_draft_orders, read_orders
Webhook → Bankr loyalty drop
read_orders, read_customers, plus webhook subscription on ORDERS_PAID
Bulk export for an offline reconciliation against onchain tx hashes
read_products, read_orders, read_customers
Token only needs what the agent will actually use. Don't grant write_* scopes to a read-only reconciliation agent.
Credit
Core Shopify content (everything above the "Bankr Bridges" header) is adapted, with attribution, from NousResearch/hermes-agent (optional-skills/productivity/shopify/SKILL.md), MIT licensed. The Bankr bridge sections are new and contributed under the same MIT license.