Aller au contenu

Ingénierie

Construire une extension d'application Shopify Sidekick : ce que la doc sous-estime

Par Jahangir Alam · 4 juillet 2026 · Mis à jour 7 octobre 2026 · 10 minutes de lecture

Dernière vérification
API Shopify
2026-10
Public
Développeurs d'applications Shopify qui construisent une extension Sidekick
Périmètre
Les extensions d'application Sidekick en API 2026-10 : les trois couches, le contrat lien de ressource et lien profond, le budget par application (20 outils, 5 intentions, 4 000 jetons, 1 seconde pour les données et 3 pour les actions) et l'état actuel du vocabulaire d'intentions

En bref : une extension d'application Sidekick comporte trois couches - une déclaration (tools.json plus un résumé d'une ligne au niveau de l'application), un runtime léger qui déclare les outils et appelle votre serveur, et votre API existante. La documentation explique les formes ; elle sous-estime la réalité opérationnelle. Les deux choses qui nous ont coûté le plus de temps : Sidekick n'achemine vers vos outils que si le extensions_summary de votre application tient sous une limite stricte, et un résultat ne devient ouvrable que si son lien profond est un champ url de premier rang - et non glissé dans _meta. Cet article est le parcours que nous aurions aimé lire en premier, issu de la livraison de l'intégration dans QuotWay, notre application de devis B2B.

Mise à jour du 31 août 2026. La limite sur les extensions d'action décrite plus bas a depuis été levée. Nous avons déposé une demande, et Shopify a ajouté le type d'intention application/quote à la plateforme en août 2026 : un devis dans une réponse Sidekick est désormais ouvrable. La suite, y compris ce que nous avions mal compris du fonctionnement des intentions, se trouve dans nous avons demandé à Shopify un nouveau type d'intention Sidekick. Le reste de cet article est laissé tel qu'il a été écrit en juillet 2026.

Ce qu'est réellement une extension d'application Sidekick

Sidekick est l'assistant IA de l'interface d'administration Shopify. Une extension d'application permet à Sidekick d'appeler votre application pour répondre à la question d'un marchand - « quels devis attendent une validation ? » - et d'afficher le résultat en ligne, sans que le marchand quitte la page.

Il en existe deux sortes, et la distinction compte avant d'écrire la moindre ligne de code :

Extension de données Extension d'action
Cible admin.app.tools.data admin.app.intent.link / admin.app.intent.render
Rôle Lire : chercher, consulter, résumer Écrire : préparer un changement que le marchand confirme
Vocabulaire d'intentions Aucun - outils libres Vocabulaire Shopify fixe (email, ticket, review, …)
Livrable aujourd'hui ? Oui, pour toute application Seulement s'il existe un type d'intention correspondant

Nous avons livré une extension de données avec 15 outils en lecture seule (rechercher des devis, indicateurs de pipeline, validations en attente, enseignements par entreprise, etc.). À l'époque, nous n'avons pas livré d'extension d'action - une limite qu'il valait mieux comprendre tôt (plus bas), et qui a depuis été levée.

Le modèle mental : trois couches

Presque toutes nos erreurs sont venues d'une confusion entre ces couches. Gardez-les distinctes.

1. La déclaration (ce que Sidekick lit pour décider s'il vous appelle). Deux fichiers et une chaîne au niveau de l'application :

  • shopify.app.toml reçoit un bloc [sidekick] avec un extensions_summary - une description en langage courant de ce à quoi votre application sait répondre. C'est le routeur : Sidekick le lit pour décider si une question donnée relève ou non de votre application.
  • tools.json déclare chaque outil : name, description, inputSchema. Les descriptions sont ce qui permet à Sidekick de choisir quel outil appeler.
  • instructions.md est une consigne remise à Sidekick sur la façon d'utiliser les résultats.
# shopify.app.toml
[sidekick]
extensions_summary = "QuotWay answers questions about B2B quotes: search and filter by status/buyer/company/amount, look up a quote's status and next step, list quotes pending approval, report pipeline analytics, and summarize B2B companies."
# extensions/sidekick-data/shopify.extension.toml
api_version = "2026-04"

[[extensions]]
name = "QuotWay quote tools"
handle = "quotway-sidekick-data"
type = "ui_extension"

  [[extensions.targeting]]
  module = "./src/index.js"
  target = "admin.app.tools.data"
  tools = "./tools.json"
  instructions = "./instructions.md"

2. Le runtime (la glu en bac à sable). Votre module s'exécute sans interface dans le bac à sable de Shopify. Il déclare chaque outil et, quand Sidekick en appelle un, interroge votre serveur et transpose la réponse dans la forme de résultat attendue. Gardez-le léger, sans logique métier :

export default () => {
  shopify.tools.register("pending_approvals", async () => {
    const res = await fetch("/api/sidekick/quotes?status=awaiting_approval", {
      headers: { Accept: "application/json" },
    });
    const data = res.ok ? await res.json() : null;
    return { results: (data?.quotes ?? []).map(quoteLink) };
  });
};

3. Le serveur (l'API réelle de votre application). Le bac à sable interroge vos propres points d'entrée, sur le même domaine, et Shopify y attache le jeton de session du marchand : votre authentification, votre cloisonnement par locataire et vos contrôles de droits existants s'appliquent donc inchangés. Nous avons ajouté une petite famille de routes /api/sidekick/* qui réutilisent la même authentification par jeton de session et les mêmes droits que le reste de l'application :

export const loader = async ({ request }) =>
  withAdminShopContext(request, async ({ shopId, staff }) => {
    requirePermission(staff.role, "quote.view");
    // ...requête, mise en forme d'un DTO simple, JSON avec CORS
  });

Les appels du bac à sable sont inter-origines : chaque route répond donc à une requête préliminaire CORS (OPTIONS → 204) et renvoie des en-têtes CORS. Le guide de Shopify demande d'ajouter l'origine du bac à sable, https://extensions.shopifycdn.com, à votre liste d'origines autorisées ; un Access-Control-Allow-Origin générique fonctionne aussi et est sans risque ici, car l'authentification passe par un jeton de session en en-tête, et non par des cookies.

Le contrat de résultat - et le champ qui rend les liens ouvrables

Les outils de données renvoient des liens de ressource au format Model Context Protocol :

{
  "results": [
    {
      "type": "resource_link",
      "uri": "gid://application/quotway.quote/123",
      "name": "QW-1042 — Acme Co.",
      "mimeType": "application/vnd.quotway.quote",
      "_meta": { "status": "Proposal sent", "total": "USD 5,000.00" }
    }
  ]
}

Voici la chose la plus importante que nous ayons apprise, et c'est à peine une phrase dans la documentation : _meta sert uniquement à l'affichage. Ce n'est pas une cible de navigation. Nous avions d'abord mis le lien profond dans _meta.url : la fiche s'affichait correctement, mais cliquer dessus ne faisait rien. Sidekick n'avait aucun moyen d'ouvrir la ressource, parce que notre uri était un gid://application/... synthétique qu'il ne sait pas résoudre, et que la vraie URL était enfouie dans des métadonnées qu'il traite comme décoratives.

Le correctif est un champ url de premier rang sur le résultat, utilisant le protocole app: pour une application intégrée :

function quoteLink(q) {
  return {
    type: "resource_link",
    uri: `gid://application/quotway.quote/${q.id}`,
    name: `${q.reference} — ${q.buyerName}`,
    mimeType: "application/vnd.quotway.quote",
    url: `app://app/quotes/${q.id}`, // ← de premier rang, ouvrable
    _meta: { status: q.statusLabel, total: q.total },
  };
}

Shopify résout app://<chemin> par rapport à l'URL de base de votre application intégrée et navigue directement dedans - sans handle de boutique, sans clé d'API, sans construction d'URL absolue. (Une URL absolue admin.shopify.com/store/<boutique>/apps/<client_id>/<chemin> fonctionne aussi, mais app: est plus simple et indépendant de la boutique.) Avec l'url sortie de _meta, « montre-moi le devis QW-1007 » renvoie désormais le résumé et un lien qui ouvre ce devis précis dans l'application.

Le guide des extensions de données de Shopify, relu le 7 octobre 2026, documente désormais deux formes de résultat distinctes : un lien de ressource (uri, name, mimeType), qui devient actionnable quand son mimeType correspond à un type d'intention enregistré, et un lien d'application intégrée, qui est une url seule. Le résultat combiné ci-dessus est ce que nous avons livré quand cet article a été écrit ; maintenant que application/quote existe, la voie de l'intention est celle qui est documentée - l'article sur les types d'intention montre le changement de deux lignes.

Les pièges (ce qui nous a coûté une journée)

Symptôme Cause Correctif
Sidekick répond avec des connaissances génériques ; aucun appel n'atteint votre serveur extensions_summary dépasse la limite du validateur de déploiement et est silencieusement ignoré, si bien que le bloc [sidekick] ne s'enregistre jamais Gardez le résumé sous la taille réellement imposée (pour nous, le plafond était de 500 caractères, et non les « 256 jetons » que la documentation laisse entendre). Vérifiez dans vos journaux serveur les appels à /api/sidekick : zéro appel signifie pas acheminé, pas un bug serveur.
La fiche de résultat s'affiche mais le lien ne s'ouvre pas Le lien profond est dans _meta, ou l'uri est un gid://application/... non résoluble Mettez une URL app://<chemin> (ou absolue) dans un champ url de premier rang
« C'est cassé » juste après un déploiement La disponibilité des outils Sidekick dépend de la session et de la propagation Réessayez dans une conversation Sidekick toute neuve ; ne concluez pas à une panne depuis une session périmée
Les appels d'outils échouent en 404 ou en CORS en production Vous avez déployé l'extension mais pas les routes serveur (ou l'inverse) Déployez les deux ensemble : l'extension via shopify app deploy, les routes via votre hébergeur
Une poussée vers votre hébergeur ne déploie rien, en silence (Propre à l'hébergeur) par exemple un réglage « exiger des commits vérifiés » qui annule les poussées non signées Signez vos commits, pour que le serveur parte bien en même temps que l'extension
L'aperçu en ligne de commande se comporte différemment Les outils passent par la véritable interface d'administration Testez dans le Sidekick de l'interface d'administration, pas dans l'aperçu en ligne de commande

Deux de ces points méritent un développement.

Diagnostiquez l'acheminement avec vos propres journaux. Quand Sidekick « ne marche pas », le signal le plus rapide est de savoir si votre serveur a été appelé. Nous avons cherché /api/sidekick dans les journaux de production et vu zéro appel - ce qui a immédiatement écarté le CORS, l'authentification et les bugs serveur, et pointé vers le routeur (extensions_summary). Un silence côté serveur signifie que la question n'a jamais été acheminée vers votre application.

Les descriptions sont le produit. L'acheminement de Sidekick est une décision de modèle de langage prise sur votre extensions_summary et les description de vos outils. Des descriptions vagues sont ignorées. Chargez-les des mots exacts qu'emploierait un marchand (« devis », « appels d'offres », « en attente de validation », « taux de conversion », « pipeline ») - tout en restant factuel. La politique de contenu de Shopify les analyse au déploiement et à l'exécution, et interdit les incitations à la vente, les comparaisons avec des concurrents, et les consignes qui tentent d'orienter le comportement de Sidekick. Nous avons dû supprimer une ligne d'instructions.md qui dictait à Sidekick ce qu'il devait dire : décrivez vos données, ne scénarisez pas l'assistant.

La lecture est livrable aujourd'hui ; l'écriture est conditionnée

C'est la limite stratégique à anticiper. Une extension de données (lecture) fonctionne dès maintenant pour n'importe quelle application. Une extension d'action (écriture : créer, modifier, envoyer, convertir) se rattache à un vocabulaire d'intentions Shopify fixe, et shopify app deploy rejette un type d'intention qui n'y figure pas. Au moment de la rédaction, il n'existait pas de type application/quote : nous ne pouvions donc pas livrer d'action native « ouvrir ce devis depuis Sidekick ». Shopify l'a ajouté en août 2026 après notre demande, et au 7 octobre 2026 le guide des actions d'application de Shopify liste quinze types - onze application/* (ad, campaign, email, faq, loyalty-program, optimization-plan, quote, return, review, shipment, ticket) et quatre shopify/* (Customer, Discount, Order, Product). Les types application/* acceptent exactement create et edit ; shopify/Customer, shopify/Order et shopify/Product prennent import et import+bulk, et shopify/Discount, ajouté le 30 septembre 2026 pour les applications de réduction construites sur une Shopify Function, prend create et edit.

Le pont pragmatique - confirmé par Shopify sur le forum développeurs - est le champ url ci-dessus : vous ne pouvez pas agir depuis Sidekick, mais vous pouvez lui donner un lien profond ouvrable, pour que le marchand arrive sur la page exacte où agir, à un clic. Si vous êtes dans la même situation, la bonne démarche est de déposer une proposition pour votre type d'intention dans le dépôt de demandes app-intent-types de Shopify, et de livrer dès maintenant l'expérience lecture plus lien profond. Nous avons fait les deux.

Des contraintes à retenir

  • Budget de réponse : gardez chaque réponse d'outil sous 4 000 jetons. Une extension de données doit répondre en 1 seconde ; une action dispose de 3 secondes. Ce sont des outils de lecture qui répondent à un tour de conversation : renvoyez ce qui a été demandé, pas tout votre graphe d'objets.
  • Budget par application : 20 outils sur l'ensemble de vos extensions, et 5 intentions. C'est toute l'allocation, et ce sont les outils qui manquent en premier : une extension de données riche plus quelques outils de préparation d'action vous amènent dans les hauts de la vingtaine. Modélisez-les autour des vraies questions des marchands, pas autour de vos tables de base de données, et budgétez les outils avant de concevoir les intentions.
  • L'authentification est offerte : le bac à sable réutilise la session de votre application ; appuyez-vous donc sur votre cloisonnement et vos contrôles de droits existants. Un outil de lecture doit être exactement aussi verrouillé que la page qu'il reflète.

Un premier jalon raisonnable

  1. Une extension de données, deux outils correspondant à de vraies questions (« chercher X », « consulter un X »).
  2. Un [sidekick] extensions_summary sous la limite de taille, et des descriptions d'outils factuelles et riches en mots-clés.
  3. Une glu légère dans le bac à sable qui interroge deux points d'entrée /api/* en réutilisant votre authentification existante et la requête préliminaire CORS.
  4. Une url de premier rang sur chaque résultat, pour qu'il ouvre la bonne page dans l'application.
  5. Déployez l'extension et le serveur ensemble ; testez dans la véritable interface d'administration, dans une conversation neuve.

C'est une journée de travail pour une intégration réellement utile - dont l'essentiel consiste à réutiliser l'API que vous avez déjà. Le difficile n'est pas le code : ce sont les trois ou quatre faits opérationnels ci-dessus.

Si vous êtes marchand plutôt que développeur : l'extension décrite ici est livrée dans QuotWay sous la forme de Shopify Sidekick pour les devis B2B, sur tous les forfaits - voir les forfaits et tarifs.

Pour aller plus loin

Revérifié sur les pages de Shopify le 7 octobre 2026 (les budgets ci-dessus viennent de la première, le vocabulaire du guide des actions d'application) :

Articles liés

Découvrez comment QuotWay gère cela sur votre boutique.

Nous souhaitons déposer des cookies de mesure d'audience pour comprendre comment le site est utilisé. Ils ne sont pas nécessaires : refuser ne change rien au fonctionnement du site, et vous pouvez revenir sur votre choix à tout moment depuis notre page de confidentialité.