Technik
Eine Shopify-Sidekick-App-Erweiterung bauen: was die Doku untertreibt
By Jahangir Alam · 4. Juli 2026 · 10 Min. Lesezeit
Kurzfassung: Eine Sidekick-App-Erweiterung besteht aus drei Ebenen – einer Deklaration (tools.json plus eine einzeilige Zusammenfassung auf App-Ebene), einer dünnen Laufzeit, die Werkzeuge registriert und Ihr Backend aufruft, und Ihrer bestehenden API. Die Doku erklärt die Formen; sie untertreibt die betriebliche Realität. Die beiden Dinge, die uns die meiste Zeit kosteten: Sidekick leitet nur dann an Ihre Werkzeuge weiter, wenn die extensions_summary auf App-Ebene in ein hartes Limit passt, und ein Ergebnis wird nur dann öffenbar, wenn sein Deeplink ein erstklassiges url-Feld ist – nicht in _meta versteckt. Dieser Beitrag ist die Anleitung, die wir gern zuerst gelesen hätten, aus dem Ausliefern der Integration in QuotWay, unserer B2B-Angebots-App.
Was eine Sidekick-App-Erweiterung tatsächlich ist
Sidekick ist der KI-Assistent im Shopify-Adminbereich. Eine App-Erweiterung lässt Sidekick Ihre App aufrufen, um die Frage einer Händlerin zu beantworten – „Welche Angebote warten auf Freigabe?“ – und das Ergebnis inline darzustellen, ohne dass die Händlerin die Seite verlässt.
Es gibt zwei Arten, und die Unterscheidung zählt, bevor Sie eine Zeile Code schreiben:
| Datenerweiterung | Aktionserweiterung | |
|---|---|---|
| Ziel | admin.app.tools.data |
admin.app.intent.link / admin.app.intent.render |
| Tut | Lesen: suchen, nachschlagen, zusammenfassen | Schreiben: eine Änderung vorbereiten, die die Händlerin bestätigt |
| Intent-Vokabular | Keines – freie Werkzeuge | Festes Shopify-Vokabular (E-Mail, Ticket, Bewertung, …) |
| Heute lieferbar? | Ja, für jede App | Nur, wenn ein passender Intent-type existiert |
Wir haben eine Datenerweiterung mit 15 nur lesenden Werkzeugen ausgeliefert (Angebote suchen, Pipeline-Kennzahlen, offene Freigaben, Unternehmenseinblicke und so weiter). Wir haben keine Aktionserweiterung ausgeliefert – und das ist eine bewusste Einschränkung, die man früh verstehen sollte (dazu unten mehr).
Das Denkmodell: drei Ebenen
Fast jeder Fehler, den wir gemacht haben, kam daher, diese zu vermischen. Halten Sie sie getrennt:
1. Deklaration (was Sidekick liest, um zu entscheiden, ob es Sie aufruft). Zwei Dateien plus ein String auf App-Ebene:
shopify.app.tomlbekommt einen[sidekick]-Block mit einerextensions_summary– einer Beschreibung in normaler Sprache, was Ihre App beantworten kann. Das ist der Router. Sidekick liest ihn, um zu entscheiden, ob eine Frage überhaupt zu Ihrer App gehört.tools.jsondeklariert jedes Werkzeug:name,description,inputSchema. Die Beschreibungen sind der Weg, auf dem Sidekick auswählt, welches Werkzeug es aufruft.instructions.mdist eine Anleitung, die Sidekick zur Nutzung der Ergebnisse bekommt.
# 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. Laufzeit (der abgeschottete Klebstoff). Ihr module läuft ohne Oberfläche in Shopifys Sandbox. Es registriert jedes deklarierte Werkzeug und ruft, wenn Sidekick eines aufruft, Ihr Backend ab und bildet die Antwort auf Sidekicks Ergebnisform ab. Halten Sie es dünn – keine Geschäftslogik hier:
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. Backend (die echte API Ihrer App). Die Sandbox ruft per fetch Ihre eigenen Endpunkte auf derselben Domain auf, und Shopify hängt das Sitzungstoken der Händlerin an – Ihre bestehende Authentifizierung, Mandantenabgrenzung und Berechtigungsprüfung gelten also unverändert. Wir haben eine kleine Familie von /api/sidekick/*-Routen ergänzt, die dieselbe Sitzungstoken-Authentifizierung und Rollensteuerung wiederverwenden wie der Rest der App:
export const loader = async ({ request }) =>
withAdminShopContext(request, async ({ shopId, staff }) => {
requirePermission(staff.role, "quote.view");
// ...abfragen, ein schlichtes DTO formen, JSON mit CORS zurückgeben
});
Die Sandbox-Aufrufe sind ursprungsübergreifend, jede Route beantwortet also einen CORS-Preflight (OPTIONS → 204) und gibt großzügige CORS-Header zurück. Ein Access-Control-Allow-Origin mit Platzhalter ist hier unbedenklich, weil die Authentifizierung an einem Bearer-Sitzungstoken hängt, nicht an Cookies.
Der Ergebnisvertrag – und das eine Feld, das Links öffnen lässt
Datenwerkzeuge geben Ressourcenlinks nach dem Model Context Protocol zurück:
{
"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" }
}
]
}
Hier das Wichtigste, das wir gelernt haben, und es ist in der Doku kaum ein Satz: _meta sind reine Anzeigedaten zur Zusammenfassung. Es ist kein Navigationsziel. Wir hatten den Deeplink zuerst in _meta.url – die Karte wurde sauber dargestellt, aber ein Klick tat nichts. Sidekick hatte keine Möglichkeit, die Ressource zu öffnen, weil unser uri ein künstliches gid://application/... war, das es nicht auflösen kann, und die echte URL in Metadaten steckte, die es als Zierde behandelt.
Die Lösung ist ein erstklassiges url-Feld am Ergebnis, mit dem app:-Protokoll für eine eingebettete App:
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}`, // ← erstklassig, öffenbar
_meta: { status: q.statusLabel, total: q.total },
};
}
Shopify löst app://<pfad> gegen die Basis-URL Ihrer eingebetteten App auf und navigiert direkt in Ihre App – ohne Shop-Handle, ohne API-Schlüssel, ohne absolute URL zu bauen. (Ein absolutes admin.shopify.com/store/<store>/apps/<client_id>/<pfad> funktioniert ebenfalls, aber app: ist einfacher und shopunabhängig.) Mit der aus _meta herausgehobenen url liefert „Zeig mir Angebot QW-1007“ jetzt die Zusammenfassung und einen Link, der genau dieses Angebot in der App öffnet.
Die Fallen (das, was uns einen Tag kostete)
| Symptom | Ursache | Lösung |
|---|---|---|
| Sidekick antwortet aus allgemeinem Wissen; null Aufrufe erreichen Ihr Backend | extensions_summary überschreitet das Limit des Deploy-Prüfers und wird still verworfen, der [sidekick]-Block wird also nie registriert |
Halten Sie die Zusammenfassung innerhalb der durchgesetzten Größe (bei uns lag die reale Obergrenze bei 500 Zeichen, nicht bei den „256 Token“, die die Doku nahelegt). Prüfen Sie es, indem Sie Ihre Serverprotokolle nach /api/sidekick-Treffern durchsehen – null Treffer heißt nicht weitergeleitet, kein Backend-Fehler. |
| Die Ergebniskarte erscheint, aber der Link öffnet nicht | Der Deeplink steht in _meta, oder uri ist ein nicht auflösbares gid://application/... |
Setzen Sie eine app://<pfad>-URL (oder eine absolute) in ein erstklassiges url-Feld |
| „Es ist kaputt“ direkt nach dem Deployen | Die Verfügbarkeit von Sidekick-Werkzeugen ist sitzungsbezogen und verzögert | Versuchen Sie es in einem brandneuen Sidekick-Chat; schließen Sie aus einer alten Sitzung nicht auf einen Defekt |
| Backend-Werkzeugaufrufe geben in Produktion 404 oder scheitern an CORS | Sie haben die Erweiterung deployt, aber nicht die Backend-Routen (oder umgekehrt) | Deployen Sie beides gemeinsam: die Erweiterung per shopify app deploy, die Backend-Routen über Ihren Host |
| Ein Push zu Ihrem Host deployt still nicht | (Hostabhängig) etwa bricht eine Einstellung „nur verifizierte Commits“ unsignierte Pushes ab | Signieren Sie Ihre Commits, damit das Backend tatsächlich zusammen mit der Erweiterung ausgeliefert wird |
| Die CLI-Vorschau verhält sich anders als erwartet | Die Werkzeuge laufen über den echten Adminbereich | Testen Sie im Sidekick des Adminbereichs, nicht in der CLI-Vorschau |
Zwei davon lohnen eine Ausführung.
Diagnostizieren Sie das Routing mit Ihren eigenen Protokollen. Wenn Sidekick „nicht funktioniert“, ist das schnellste Signal, ob Ihr Backend überhaupt aufgerufen wurde. Wir haben die Produktionsprotokolle nach /api/sidekick durchsucht und null Treffer gesehen – was CORS, Authentifizierung und Backend-Fehler sofort ausschloss und uns auf den Router (extensions_summary) wies. Stille am Backend heißt, die Frage wurde nie an Ihre App geleitet.
Die Beschreibungen sind das Produkt. Sidekicks Routing ist eine Entscheidung eines Sprachmodells über Ihre extensions_summary und die descriptions Ihrer Werkzeuge. Vage Beschreibungen werden übersprungen. Laden Sie sie mit genau den Wörtern, die eine Händlerin benutzen würde („Angebote“, „RFQs“, „wartet auf Freigabe“, „Abschlussquote“, „Pipeline“) – bleiben Sie aber sachlich. Shopifys Inhaltsrichtlinie prüft diese beim Deployen und zur Laufzeit und verbietet Verkaufsanpreisungen, Wettbewerbsvergleiche und Anweisungen, die Sidekicks Verhalten steuern wollen. Wir mussten eine Zeile aus instructions.md löschen, die Sidekick vorschrieb, was es sagen soll; beschreiben Sie Ihre Daten, schreiben Sie dem Assistenten kein Drehbuch.
Lesen ist heute lieferbar, Schreiben ist gesperrt
Das ist die strategische Einschränkung, um die herum Sie planen sollten. Eine Datenerweiterung (lesen) funktioniert für jede App schon jetzt. Eine Aktionserweiterung (schreiben – erstellen, bearbeiten, senden, umwandeln) bindet an ein festes Shopify-Intent-Vokabular, und shopify app deploy lehnt einen Intent-type ab, der nicht darin enthalten ist. Für eine App für Angebote und Verhandlungen gibt es noch keinen Typ application/quote, wir können also keine native Aktion „dieses Angebot aus Sidekick senden“ ausliefern.
Die pragmatische Brücke – von Shopify-Mitarbeitenden im Entwicklerforum bestätigt – ist das obige url-Feld: Sie können aus Sidekick heraus nicht handeln, aber Sie können ihm einen öffenbaren Deeplink geben, sodass die Händlerin genau auf der Seite landet, auf der sie handeln kann, einen Klick entfernt. Wenn Sie im selben Boot sitzen, ist der richtige Zug, einen Vorschlag für Ihren Intent-Typ in Shopifys RFC-Repository app-intent-types einzureichen und das Erlebnis aus Lesen und Deeplink jetzt auszuliefern. Wir haben beides getan.
Einschränkungen, die man sich merken sollte
- Antwortbudget: Halten Sie jede Werkzeugantwort unter 4.000 Token und zielen Sie auf unter 1 Sekunde. Das sind lesende Werkzeuge, die eine Chatrunde beantworten – geben Sie zurück, was gefragt wurde, nicht Ihren gesamten Objektgraphen.
- Budget je App: Sie haben eine begrenzte Zahl an Werkzeugen (und Intents). Modellieren Sie sie um echte Händlerfragen herum, nicht um Ihre Datenbanktabellen.
- Authentifizierung ist gratis: Die Sandbox nutzt die Sitzung Ihrer App wieder, stützen Sie sich also auf Ihre bestehende Mandantenabgrenzung und Berechtigungsprüfung. Ein lesendes Werkzeug sollte genauso abgesichert sein wie die Seite, die es spiegelt.
Ein vernünftiger erster Meilenstein
- Eine Datenerweiterung mit zwei Werkzeugen, die echten Fragen entsprechen („X suchen“, „ein X nachschlagen“).
- Eine
[sidekick] extensions_summaryinnerhalb des Größenlimits, dazu schlagwortreiche, sachliche Werkzeugbeschreibungen. - Dünner Sandbox-Klebstoff, der zwei
/api/*-Endpunkte perfetchaufruft und Ihre bestehende Authentifizierung samt CORS-Preflight wiederverwendet. - Eine erstklassige
urlan jedem Ergebnis, sodass es die richtige Seite in der App öffnet. - Deployen Sie Erweiterung und Backend gemeinsam; testen Sie im echten Adminbereich in einem frischen Chat.
Das ist ein Tag Arbeit für eine wirklich nützliche Integration – der größte Teil davon ist die Wiederverwendung der API, die Sie ohnehin haben. Der schwierige Teil ist nicht der Code, sondern die drei, vier betrieblichen Tatsachen oben.
Weiterführendes
- Shopify: Sidekick-App-Erweiterungen und App-Daten bereitstellen
- Shopify: das RFC-Repository
app-intent-types(schlagen Sie Ihren Aktions-Intent-Typ vor) - Der händlerseitige Begleitbeitrag: Shopify Sidekick für Ihre B2B-Angebote nutzen
Verwandte Artikel
- TechnikEine Shopify-Flow-Integration bauen: Trigger, Aktionen und die Fallen, die uns einen Zyklus kosteten11 Min. Lesezeit
- Angebote & VerhandlungQuote-to-Order: vom Angebot zur echten Bestellung12 Min. Lesezeit
- Shopify B2BB2B-Steuerbefreiung in Shopify: Unternehmensstandorte, Steuernummern und Angebote9 Min. Lesezeit
So funktioniert QuotWay in Ihrem Shop.