bankr-shopify

Par bankrbot · skills

APIs GraphQL Admin & Storefront de Shopify via curl, avec des bridges natifs Bankr. Gérez les produits, commandes, clients, stocks, metafields, webhooks et opérations en masse, puis connectez les données marchandes à des primitives onchain — stockez un handle résolvable par Bankr (ENS, Twitter, Farcaster, wallet) sur chaque client sous forme de metafield, exposez des draft orders Shopify derrière des endpoints x402 que Bankr règle en USDC, et transformez les webhooks ORDERS_PAID en jobs d'agent Bankr pour des loyalty drops ou des royalty splits. Déclencheurs : « Shopify products », « Shopify orders », « create draft order », « loyalty drop on order », « x402 checkout », « tokengate Shopify ». Contenu Shopify de base adapté avec attribution de NousResearch/hermes-agent (MIT).

npx skills add https://github.com/bankrbot/skills --skill bankr-shopify

Shopify — Admin & Storefront GraphQL APIs (Bankr Edition)

Les sections Shopify de cette skill sont adaptées de la skill upstream NousResearch/hermes-agent (MIT, author: community). Les quatre sections « Bankr Bridges » à la fin sont nouvelles et spécifiques à l'écosystème Bankr.

Travaillez directement avec les stores Shopify via curl : lister les produits, gérer l'inventaire, extraire les commandes, mettre à jour les clients, lire les metafields. Pas de SDK, pas de framework d'app — juste l'endpoint GraphQL et un token d'accès d'app personnalisée. Puis reliez les données marchands aux primitives onchain via Bankr.

L'API Admin REST est obsolète depuis 2024-04 et ne reçoit que les correctifs de sécurité. Utilisez GraphQL Admin pour tous les travaux d'administration. Utilisez Storefront GraphQL pour les requêtes en lecture seule côté client (produits, collections, panier).

Prérequis

  1. Dans l'admin Shopify : Paramètres → Applications et canaux de vente → Développer des applications → Créer une application.
  2. Cliquez sur Configurer les scopes de l'API Admin, sélectionnez ce dont vous avez besoin (exemples ci-dessous), enregistrez.
  3. Installez l'application → le token d'accès de l'API Admin apparaît UNE SEULE FOIS. Copiez-le immédiatement — Shopify ne le montrera plus jamais. Les tokens commencent par shpat_.
  4. Définissez ces variables dans vos paramètres Bankr (icône engrenage → Env Vars) :
    • SHOPIFY_ACCESS_TOKEN — token admin (commence par shpat_)
    • SHOPIFY_STORE_DOMAINmy-store.myshopify.com (le domaine myshopify permanent, pas votre domaine personnalisé)
    • SHOPIFY_API_VERSION — par défaut 2026-01
    • BANKR_API_KEY — pour les sections Bankr bridge ; générez-le sur https://bankr.bot/api

Attention : À partir du 1er janvier 2026, les nouvelles « apps personnalisées héritées » créées dans l'admin Shopify disparaissent. Les nouvelles installations devraient utiliser le Dev Dashboard (shopify.dev/docs/apps/build/dev-dashboard). Les apps admin existantes continuent à fonctionner. Si le store de l'utilisateur n'a pas d'app personnalisée existante et que c'est après le 2026-01-01, dirigez-le vers le Dev Dashboard au lieu du flux admin.

Scopes courants par tâche :

  • Produits / collections : read_products, write_products
  • Inventaire : read_inventory, write_inventory, read_locations
  • Commandes : read_orders, write_orders (30 plus récentes sans read_all_orders)
  • Clients : read_customers, write_customers
  • Brouillons de commande : read_draft_orders, write_draft_orders
  • Expéditions : read_fulfillments, write_fulfillments
  • Metafields / metaobjects : couverts par les scopes des ressources correspondantes

Bases de l'API

  • Endpoint : https://$SHOPIFY_STORE_DOMAIN/admin/api/$SHOPIFY_API_VERSION/graphql.json
  • En-tête d'auth : X-Shopify-Access-Token: $SHOPIFY_ACCESS_TOKEN (PAS Authorization: Bearer)
  • Méthode : toujours POST, toujours Content-Type: application/json, le corps est {"query": "...", "variables": {...}}
  • HTTP 200 ne signifie pas succès. GraphQL retourne les erreurs dans un tableau errors de haut niveau et des userErrors par champ. Vérifiez toujours les deux.
  • Les IDs sont des chaînes GID : gid://shopify/Product/10079467700516, gid://shopify/Variant/..., gid://shopify/Order/.... Passez-les tels quels — ne supprimez pas le préfixe.
  • Rate limit : calculé via le coût de la requête (leaky bucket). Chaque réponse a extensions.cost avec requestedQueryCost, actualQueryCost, throttleStatus.{currentlyAvailable, maximumAvailable, restoreRate}. Reculez quand currentlyAvailable tombe en dessous du coût de votre prochaine requête. Shops standard = bucket de 100 points, restauration 50/s ; Plus = 1000/100.

Motif curl de base (réutilisable) :

shop_gql() {
  local query="$1"
  local variables="${2:-{}}"
  curl -sS -X POST \
    "https://${SHOPIFY_STORE_DOMAIN}/admin/api/${SHOPIFY_API_VERSION:-2026-01}/graphql.json" \
    -H "Content-Type: application/json" \
    -H "X-Shopify-Access-Token: ${SHOPIFY_ACCESS_TOKEN}" \
    --data "$(jq -nc --arg q "$query" --argjson v "$variables" '{query: $q, variables: $v}')"
}

Canalisez à travers jq pour une sortie lisible. -sS garde les erreurs visibles mais masque la barre de progression.

Découverte

Info du shop + version actuelle de l'API

shop_gql '{ shop { name myshopifyDomain primaryDomain { url } currencyCode plan { displayName } } }' | jq

Lister toutes les versions d'API supportées

shop_gql '{ publicApiVersions { handle supported } }' | jq '.data.publicApiVersions[] | select(.supported)'

Produits

Rechercher des produits (20 premiers correspondant à la requête)

shop_gql '
query($q: String!) {
  products(first: 20, query: $q) {
    edges { node { id title handle status totalInventory variants(first: 5) { edges { node { id sku price inventoryQuantity } } } } }
    pageInfo { hasNextPage endCursor }
  }
}' '{"q":"hoodie status:active"}' | jq

La syntaxe de requête supporte title:, sku:, vendor:, product_type:, status:active, tag:, created_at:>2025-01-01. Grammaire complète : https://shopify.dev/docs/api/usage/search-syntax

Paginer les produits (cursor)

shop_gql '
query($cursor: String) {
  products(first: 100, after: $cursor) {
    edges { cursor node { id handle } }
    pageInfo { hasNextPage endCursor }
  }
}' '{"cursor":null}'
# appels suivants : passez le endCursor précédent

Obtenir un produit avec variantes + metafields

shop_gql '
query($id: ID!) {
  product(id: $id) {
    id title handle descriptionHtml tags status
    variants(first: 20) { edges { node { id sku price compareAtPrice inventoryQuantity selectedOptions { name value } } } }
    metafields(first: 20) { edges { node { namespace key type value } } }
  }
}' '{"id":"gid://shopify/Product/10079467700516"}' | jq

Créer un produit avec une variante

shop_gql '
mutation($input: ProductCreateInput!) {
  productCreate(product: $input) {
    product { id handle }
    userErrors { field message }
  }
}' '{"input":{"title":"Test Hoodie","status":"DRAFT","vendor":"Bankr","productType":"Apparel","tags":["test"]}}'

Les variantes ont maintenant leurs propres mutations dans les versions récentes :

# Ajouter des variantes après la création du produit
shop_gql '
mutation($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
  productVariantsBulkCreate(productId: $productId, variants: $variants) {
    productVariants { id sku price }
    userErrors { field message }
  }
}' '{"productId":"gid://shopify/Product/...","variants":[{"optionValues":[{"optionName":"Size","name":"M"}],"price":"49.00","inventoryItem":{"sku":"HD-M","tracked":true}}]}'

Mettre à jour le prix / SKU

shop_gql '
mutation($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
  productVariantsBulkUpdate(productId: $productId, variants: $variants) {
    productVariants { id sku price }
    userErrors { field message }
  }
}' '{"productId":"gid://shopify/Product/...","variants":[{"id":"gid://shopify/ProductVariant/...","price":"55.00"}]}'

Commandes

Lister les commandes récentes (30 dernières par défaut sans read_all_orders)

shop_gql '
{
  orders(first: 20, reverse: true, query: "financial_status:paid") {
    edges { node {
      id name createdAt displayFinancialStatus displayFulfillmentStatus
      totalPriceSet { shopMoney { amount currencyCode } }
      customer { id displayName email }
      lineItems(first: 10) { edges { node { title quantity sku } } }
    } }
  }
}' | jq

Filtres de requête utiles pour les commandes : financial_status:paid|pending|refunded, fulfillment_status:unfulfilled|fulfilled, created_at:>2025-01-01, tag:gift, email:foo@example.com.

Récupérer une commande unique avec adresse d'expédition

shop_gql '
query($id: ID!) {
  order(id: $id) {
    id name email
    shippingAddress { name address1 address2 city province country zip phone }
    lineItems(first: 50) { edges { node { title quantity variant { sku } originalUnitPriceSet { shopMoney { amount currencyCode } } } } }
    transactions { id kind status amountSet { shopMoney { amount currencyCode } } }
  }
}' '{"id":"gid://shopify/Order/...."}' | jq

Clients

# Rechercher
shop_gql '
{
  customers(first: 10, query: "email:*@example.com") {
    edges { node { id email displayName numberOfOrders amountSpent { amount currencyCode } } }
  }
}'

# Créer
shop_gql '
mutation($input: CustomerInput!) {
  customerCreate(input: $input) {
    customer { id email }
    userErrors { field message }
  }
}' '{"input":{"email":"test@example.com","firstName":"Test","lastName":"User","tags":["api-created"]}}'

Inventaire

L'inventaire réside sur les éléments d'inventaire liés aux variantes, avec les quantités suivies par localisation.

# Obtenir l'inventaire d'une variante dans toutes les localisations
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/..."}'

Ajuster le stock (delta) — utilise inventoryAdjustQuantities :

shop_gql '
mutation($input: InventoryAdjustQuantitiesInput!) {
  inventoryAdjustQuantities(input: $input) {
    inventoryAdjustmentGroup { reason changes { name delta } }
    userErrors { field message }
  }
}' '{
  "input": {
    "reason": "correction",
    "name": "available",
    "changes": [{"delta": 5, "inventoryItemId": "gid://shopify/InventoryItem/...", "locationId": "gid://shopify/Location/..."}]
  }
}'

Définir un stock absolu (pas un delta) — inventorySetQuantities :

shop_gql '
mutation($input: InventorySetQuantitiesInput!) {
  inventorySetQuantities(input: $input) {
    inventoryAdjustmentGroup { id }
    userErrors { field message }
  }
}' '{"input":{"reason":"correction","name":"available","ignoreCompareQuantity":true,"quantities":[{"inventoryItemId":"gid://shopify/InventoryItem/...","locationId":"gid://shopify/Location/...","quantity":100}]}}'

Metafields & Metaobjects

Les metafields attachent des données personnalisées aux ressources (produits, clients, commandes, shop).

# Lire
shop_gql '
query($id: ID!) {
  product(id: $id) {
    metafields(first: 10, namespace: "custom") {
      edges { node { key type value } }
    }
  }
}' '{"id":"gid://shopify/Product/..."}'

# Écrire (fonctionne pour tout type de propriétaire)
shop_gql '
mutation($metafields: [MetafieldsSetInput!]!) {
  metafieldsSet(metafields: $metafields) {
    metafields { id key namespace }
    userErrors { field message code }
  }
}' '{"metafields":[{"ownerId":"gid://shopify/Product/...","namespace":"custom","key":"care_instructions","type":"multi_line_text_field","value":"Wash cold. Tumble dry low."}]}'

Storefront API (lecture publique uniquement)

Endpoint différent, token différent, utilisé pour les apps côté client / configurations headless style hydrogen. Les en-têtes diffèrent :

  • Endpoint : https://$SHOPIFY_STORE_DOMAIN/api/$SHOPIFY_API_VERSION/graphql.json
  • En-tête d'auth (publique) : X-Shopify-Storefront-Access-Token: <public token> — intégrable dans le navigateur
  • En-tête d'auth (privée) : Shopify-Storefront-Private-Token: <private token> — serveur uniquement
curl -sS -X POST \
  "https://${SHOPIFY_STORE_DOMAIN}/api/${SHOPIFY_API_VERSION:-2026-01}/graphql.json" \
  -H "Content-Type: application/json" \
  -H "X-Shopify-Storefront-Access-Token: ${SHOPIFY_STOREFRONT_TOKEN}" \
  -d '{"query":"{ shop { name } products(first: 5) { edges { node { id title handle } } } }"}' | jq

Opérations en bloc

Pour les dumps plus grands que ce que les rate limits permettent (catalogue complet de produits, toutes les commandes d'une année) :

# 1. Démarrer la requête en bloc
shop_gql '
mutation {
  bulkOperationRunQuery(query: """
    { products { edges { node { id title handle variants { edges { node { sku price } } } } } } }
  """) {
    bulkOperation { id status }
    userErrors { field message }
  }
}'

# 2. Interroger le statut
shop_gql '{ currentBulkOperation { id status errorCode objectCount fileSize url partialDataUrl } }'

# 3. Quand status=COMPLETED, téléchargez le fichier JSONL
curl -sS "$URL" > products.jsonl

Chaque ligne JSONL est un nœud, et les connexions imbriquées sont émises en tant que lignes séparées avec __parentId. Réassemblez côté client si nécessaire.

Webhooks

Abonnez-vous aux événements pour ne pas avoir à interroger :

shop_gql '
mutation($topic: WebhookSubscriptionTopic!, $sub: WebhookSubscriptionInput!) {
  webhookSubscriptionCreate(topic: $topic, webhookSubscription: $sub) {
    webhookSubscription { id topic endpoint { __typename ... on WebhookHttpEndpoint { callbackUrl } } }
    userErrors { field message }
  }
}' '{"topic":"ORDERS_CREATE","sub":{"callbackUrl":"https://example.com/webhook","format":"JSON"}}'

Vérifiez le HMAC du webhook entrant avec le secret client de l'app (pas le token d'accès) :

echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "$APP_SECRET" -binary | base64
# Comparez à l'en-tête X-Shopify-Hmac-Sha256

Pièges

  • Les endpoints REST existent toujours mais sont gelés. N'écrivez pas de nouvelles intégrations contre /admin/api/.../products.json. Utilisez GraphQL.
  • Vérification du format du token. Les tokens admin commencent par shpat_. Les tokens publics Storefront par shpua_. Si vous en avez un et le mauvais en-tête, chaque requête retourne 401 sans corps d'erreur utile.
  • 403 avec un token valide = scope manquant. Shopify retourne {"errors":[{"message":"Access denied for ..."}]}. Reconfigurez les scopes de l'API Admin sur l'app, puis réinstallez pour régénérer le token.
  • userErrors vide ≠ succès. Vérifiez aussi que data.<mutation>.<resource> n'est pas null. Certains échecs ne remplissent ni l'un ni l'autre — inspectez la réponse entière.
  • GID vs ID numérique. Le REST hérité donnait les IDs numériques ; GraphQL veut les chaînes GID complètes. Pour convertir : gid://shopify/Product/<numeric>.
  • Surprise de rate limit. Une seule products(first: 250) avec imbrication profonde peut coûter 1000+ points et throttle immédiatement sur un shop de plan standard. Commencez étroit, lisez extensions.cost, ajustez.
  • Ordre de pagination. products(first: N, reverse: true) trie par id DESC, pas created_at. Utilisez sortKey: CREATED_AT, reverse: true pour « plus récent d'abord ».
  • read_all_orders pour les données historiques. Sans elle, orders(...) plafonne silencieusement à la fenêtre de 60 jours. Vous n'obtenez pas d'erreur, juste moins de résultats que prévu. Pour les marchands Shopify Plus avec de nombreuses commandes, demandez ce scope via les paramètres de données protégées de l'app.
  • Les devises sont des chaînes. Les montants reviennent sous forme "49.00" et non 49.0. Ne faites pas jq tonumber à l'aveugle si vous vous souciez du remplissage par zéro.
  • Les champs Money multi-devises ont shopMoney (devise du store) ET presentmentMoney (devise du client). Choisissez-en un de manière cohérente.

Sécurité

Les mutations dans Shopify sont réelles — elles créent des produits, facturent des remboursements, annulent les commandes, expédient les expéditions. Avant d'exécuter productDelete, orderCancel, refundCreate, ou toute mutation en bloc : déclarez clairement ce qu'est le changement, sur quel shop, et confirmez avec l'utilisateur. Il n'y a pas de clone intermédiaire des données de production à moins que l'utilisateur n'ait un store de développement séparé.


Bankr Bridges

Les sections ci-dessous ne font pas partie de la skill Hermes upstream. Elles montrent comment connecter les ressources Shopify aux primitives onchain de Bankr — résolution de handles, règlement x402, et flux de tokens pilotés par webhook — en utilisant uniquement ce que Bankr expose déjà.

D'après la documentation Bankr :

  • L'agent de Bankr résout nativement ENS, Twitter @handle, Farcaster handle, et les adresses de portefeuille pour les transferts et les destinataires de frais. L'email ne peut pas être résolu.
  • Bankr peut appeler, héberger et régler les endpoints x402 ; USDC est l'unité.
  • Le motif Job est soumettre → interroger : POST /agent/prompt puis GET /agent/job/{id}. Il n'y a pas de webhooks côté Bankr.
  • Outils MCP : bankr_agent_submit_prompt, bankr_agent_get_job_status, bankr_agent_cancel_job.

Bridge 1 — Identité (Client Shopify ↔ Bankr handle)

La clé cliente principale de Shopify est l'email, que Bankr ne peut pas résoudre. Le bridge : stocker un handle résolvable par Bankr sur le client en tant que metafield, puis passer la chaîne telle quelle à Bankr.

Définition de metafield recommandée : namespace: custom, key: handle, type: single_line_text_field. Valeurs acceptables : vitalik.eth, @dwr.eth, @username (Twitter), ou une adresse portefeuille 0x….

# Écrire un handle sur un client à la caisse / inscription
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"}]}'

Relisez-le quand vous devez payer ou déposer des 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')

# Transférez à Bankr — aucune résolution supplémentaire nécessaire.
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\"}"

Si le metafield est vide, demandez au client un parmi {ENS, Twitter, Farcaster, portefeuille} avant toute action onchain — ne devinez jamais à partir de l'email.

Bridge 2 — Checkout x402 pour un brouillon de commande Shopify

Bankr règle nativement les endpoints x402. Pour accepter l'USDC pour un panier Shopify, créez un brouillon de commande, exposez son total derrière un endpoint tarifié en x402, et laissez Bankr le payer. Au 200, marquez le brouillon comme payé (ou appelez draftOrderComplete).

# 1. Créer un brouillon de commande à partir d'un panier
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')

Votre service expose ensuite un endpoint HTTP 402 tarifant ce brouillon (USDC sur Base) — voir la spec x402 upstream. Côté Bankr, l'agent le règle :

# Côté Bankr (agent) : payer l'endpoint x402 qui fronts le brouillon
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\"}"

Quand votre serveur x402 confirme le règlement, complétez le brouillon côté Shopify :

shop_gql '
mutation($id: ID!) {
  draftOrderComplete(id: $id, paymentPending: false) {
    draftOrder { order { id name } }
    userErrors { field message }
  }
}' "{\"id\":\"${DRAFT_ID}\"}"

Notes :

  • Utilisez shopMoney de manière cohérente pour la tarification x402 afin que la conversion de devises reste de votre côté, pas celui de Shopify.
  • Traitez le hash tx du règlement x402 comme source de vérité — ne complétez pas le brouillon jusqu'à ce que vous ayez vérifié le hash.

Bridge 3 — Webhook Shopify → Bankr Submit (drop de loyauté sur ORDERS_PAID)

Bankr n'a pas de webhooks entrants. Le motif est : webhook Shopify → votre serveur → vérifier HMAC → lire le metafield handle du client → soumettre un job Bankr → interroger.

# S'abonner une fois
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"}}'

Pseudocode du gestionnaire webhook :

# 1. Vérifier le HMAC (rejeter s'il ne correspond pas) — utilisez une comparaison en temps constant dans le code réel
EXPECTED=$(echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "$APP_SECRET" -binary | base64)
[ "$EXPECTED" = "$X_SHOPIFY_HMAC_SHA256" ] || exit 1

# 2. Extraire le handle Bankr de l'acheteur du metafield client de la commande
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   # ignorer silencieusement, pas de handle en dossier

# 3. Soumettre le drop de loyauté à 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. Interroger jusqu'à terminal
while :; 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 ;;
  esac
  sleep 2
done

Même motif fonctionne pour : splits de royalties sur les biens numériques, airdrops de tokens Clanker aux acheteurs réguliers, sweeps de trésorerie quand un seuil de ventes est atteint. Ajoutez une clé d'idempotence sur l'id de commande Shopify afin qu'un webhook rejoué ne puisse pas déclencher une soumission Bankr dupliquée.

Bridge 4 — Antisèche de scopes pour les flows Bankr

Scopes Admin API minimaux par bridge :

Flow Scopes requis
Bridge d'identité (lire/écrire handle metafield) read_customers, write_customers
Checkout x402 via brouillons de commande read_customers, write_draft_orders, read_orders
Webhook → Bankr loyalty drop read_orders, read_customers, plus abonnement webhook sur ORDERS_PAID
Export en bloc pour une réconciliation hors ligne contre les hash tx onchain read_products, read_orders, read_customers

Le token n'a besoin que de ce que l'agent utilisera réellement. Ne concédez pas les scopes write_* à un agent en lecture seule pour la réconciliation.


Crédit

Le contenu Shopify de base (tout ce qui précède l'en-tête « Bankr Bridges ») est adapté, avec attribution, de NousResearch/hermes-agent (optional-skills/productivity/shopify/SKILL.md), sous licence MIT. Les sections Bankr bridge sont nouvelles et contribuées sous la même licence MIT.

Skills similaires