Brugerdefinerede udløsere
De indbyggede triggere dækker de ændringer, som de fleste butikker lægger vægt på. En brugerdefineret trigger dækker resten: Du vælger en begivenhed fra Shopify, skriver et par linjer JavaScript for at afgøre, om det er relevant, og hvad der skal sendes, og så bliver det en trigger, du kan bruge i Shopify Flow.
Der er tre ting, den løser, som en indbygget trigger ikke kan:
- Skyd kun, hvis din betingelse er opfyldt. »Kun når en ordre på over 500 EUR får mærket »
vip«« er én kodelinje i stedet for en arbejdsgang, der kører på hver eneste ordre og derefter filtrerer. - Brug en overgang, ikke en tilstand. Din kode ser værdien før og efter ændringen, så det er muligt at sige, at »status gik fra udkast til aktiv« eller »lagerbeholdningen faldt til under 5«. En betingelse baseret på den aktuelle værdi kan ikke udtrykke det.
- Send kun de felter, du ønsker. Tilpas begivenheden til de værdier, som din arbejdsgang rent faktisk bruger, i stedet for at læse dem ud én efter én.
En brugerdefineret trigger behøver ikke engang en »Shopify«-begivenhed: Den kan også køre efter en tidsplan og selv afgøre, hvad der er ændret.
Sådan fungerer det
- Vælg, hvad der skal udløse den: en enkelt Shopify-begivenhed eller en tidsplan.
- For en begivenhed skal du vælge den begivenhed, den skal reagere på - enhver »Shopify«-begivenhed, som appen allerede modtager - eller starte med en skabelon, der vælger begivenheden og udfylder koden.
- Indhent en reel nyttelast. Foretag ændringen i din butik, så henter appen den nøjagtige begivenhedstekst.
- Skriv en transformation - returner et objekt for at udløse handlingen, returner »
null« for at springe over. - Test det i forhold til den registrerede begivenhed, og se præcis, hvad din arbejdsgang ville modtage.
- Tænd for den.

Udarbejdelse af transformationen
Din trigger er et JavaScript-modul, der eksporterer en funktion ved navn transform. Den modtager fire argumenter:
payload- den ubehandlede begivenhedstekst fra »Shopify«, præcis som den modtages.topic- hvilken begivenhed der blev udløst, f.eks.PRODUCTS_UPDATE.shop- dit myshopify-domæne.ctx-ctx.log(...)viser en udskrift i logpanelet ved siden af editoren,ctx.shopify(...)udfører en Admin GraphQL-forespørgsel, ogctx.fetch(...)kalder en URL på det offentlige internet.
Det, du returnerer, afgør, hvad der sker:
- Returner et objekt, og udløseren aktiveres med dette objekt.
- Returner
null, og begivenheden springes over. Sådan fungerer filtrering - der er ikke noget særskilt filtersprog, man skal lære. - Lad feltet være tomt, så udløses den ved hver eneste hændelse af den pågældende type.
/**
* Only fire for high-value orders carrying the vip tag.
*/
export async function transform(payload, topic, shop, ctx) {
const total = parseFloat(payload.total_price || "0");
const tags = (payload.tags || "").split(",").map(t => t.trim());
if (total < 500) return null;
if (!tags.includes("vip")) return null;
ctx.log("firing for", payload.name, total);
return {
orderId: payload.admin_graphql_api_id,
orderNumber: payload.name,
total,
currency: payload.currency,
customerEmail: payload.email,
};
}Værdien før ændringen
Shopify
Ved produktopdateringer, ordreopdateringer og kundeopdateringer viser payload._changes en liste over alle de sporede felter, som denne opdatering har ændret, med henholdsvis oldValue og newValue:
- Produkter:
title,handle,description,status,vendor,productType,tags - Bestillinger:
financialStatus,fulfillmentStatus,tags,note,lineItemsogcustomAttributes.<name> - Kunder:
tags,note,state(ENABLED,DISABLED,INVITED,DECLINED)
Listen er tom, når opdateringen ikke vedrørte disse felter - f.eks. en lagerændring - eller når appen ser posten for første gang. Den eksempelbegivenhed, du registrerer i editoren, viser feltet, så du kan se dets faktiske udformning, inden du skriver i det.
/**
* Fire only when a product BECOMES active - not on every later edit of an
* active product, which is what a condition on the current status would do.
*/
export async function transform(payload, topic, shop, ctx) {
const change = (payload._changes || []).find((c) => c.field === "status");
if (!change) return null;
if (change.oldValue !== "draft" || change.newValue !== "active") return null;
return {
productId: payload.admin_graphql_api_id,
title: payload.title,
oldStatus: change.oldValue,
newStatus: change.newValue,
};
}De mere specifikke begivenheder har deres egne gamle og nye værdier, så du behøver ikke _changes der: en prisændring har oldPrice, newPrice og percentChange; en lagerændring har _oldAvailable, _newAvailable og _delta; en metafeltændring har previousValue ved siden af metafield.value.
Start med en skabelon
Når »Fires on« er valgt i editoren, vælger »Start fra en skabelon« begivenheden og indsætter fungerende kode. Rediger konstanterne øverst, test, og gem derefter. Hver skabelon læser en værdi før og efter ændringen:
- Et produkt-, ordre- eller kundefelt skiftede fra én værdi til en anden - status skiftede fra udkast til aktiv, en ordre blev betalt, en konto blev aktiveret.
- Prisen faldt med mere end N procent - en reel prisnedsættelse, ikke bare en tilfældig prisændring.
- Lagerbeholdningen faldt under en tærskel - udløser alarmen én gang, i det øjeblik lagerbeholdningen krydser grænsen, ikke ved hvert salg, mens den allerede er lav.
- Et produktmetafelt har overskredet en tærskelværdi - en vurdering er faldet til under 3, en margin er steget til over 40.
- Ordre betalt af en stor kunde - henter kundens samlede forbrug i din butik og udløser kun, hvis beløbet overstiger det beløb, du har angivet.

Hentning af yderligere data med ctx.shopify
Webhook-indholdet indeholder kun de felter, som Shopify sender. Hvis du har brug for andre oplysninger - f.eks. antallet af en kundes ordrer, lagerbeholdningen for en variant eller et metafelt - skal du sende en forespørgsel til Admin API direkte fra din transformation:
export async function transform(payload, topic, shop, ctx) {
const data = await ctx.shopify(`
query($id: ID!) {
customer(id: $id) { numberOfOrders tags }
}
`, { id: payload.customer.admin_graphql_api_id });
// Only fire for repeat customers
if (data.customer.numberOfOrders < 5) return null;
return {
orderId: payload.admin_graphql_api_id,
orderCount: data.customer.numberOfOrders,
};
}Den returnerer forespørgslens data og kaster en undtagelse, hvis der er fejl i forespørgslen, så fejlen vises i din test i stedet for, at den uden videre ikke returnerer noget.
Tre grænser, det er værd at kende:
- Op til 10 opkald pr. gennemløb. Enrichment kræver en håndfuld; mere end det er som regel en loop. Hent det, du har brug for, i én forespørgsel, hvor det er muligt.
- Den læser, den skriver ikke. Den bruger de tilladelser, du har givet, og appen beder kun om læseadgang. En forespørgsel om ordredata mislykkes, medmindre »Adgang til ordredata« er godkendt på siden »Tilladelser«. Hvis du vil ændre noget i din butik, skal du gøre det i de Flow-handlinger, der følger efter udløseren.
- Din butiks loginoplysninger kommer aldrig ind i din kode. Forespørgslen udføres af appen på dine vegne, så der er ingen adgangstoken i sandkassen, der kan lække.
ctx.fetch(url, options) fungerer på samme måde som browserens fetch for alt på det offentlige internet: en leverandørs lageropdateringer, en valutakurs, dit eget API. Interne og private netværksadresser afvises. Hvis din triggerkode gemmes på GitHub, må du ikke inkludere en API-nøgle i den.
Udløsere, der kører efter en tidsplan
Shopify Flow reagerer, når der sker noget. Den kan ikke reagere, når der ikke sker noget, og den kan ikke se noget uden for din butik. Til det formål skal du vælge »Efter en tidsplan« under »Hvad får denne funktion til at køre«. Der ligger ingen »Shopify«-begivenhed bag en sådan udløser: Din kode kører med et bestemt interval - fra hvert 30. sekund op til én gang om dagen - og afgør selv, hvad der tæller som en ændring.
Det er den samme funktion transform, men med en anden payload og en anden returværdi:
payload.state- uanset hvad din kode returnerede somstateved den forrige kørsel.nullved den første kørsel.payload.nowogpayload.lastRunAt- tidsstempler.- Retur
{ state, events }: den nye tilstand, der skal huskes (op til 32 KB), samt en liste over begivenheder, der skal udløses (op til 100 pr. kørsel). Hver begivenhed udløser Custom Trigger én gang; en begivenhedsresourceId, hvis du har angivet en, bliver rekord-id’et i Flow. Returnull, når der ikke er noget at rapportere.
Sørg for, at den første kørsel kun husker - aldrig udløser. Ved den første kørsel har din kode intet at sammenligne med, så alt vil fremstå som nyt. Gem det, du ser, og returner ingen begivenheder, og aktivering af udløseren vil aldrig oversvømme dine arbejdsgange. Alle skabeloner fungerer på denne måde.

// Fires when a value at a JSON URL changes, with the value before and after.
const URL = "https://api.example.com/stock/1042"
export async function transform(payload, topic, shop, ctx) {
const res = await ctx.fetch(URL, { headers: { accept: "application/json" } })
if (!res.ok) throw new Error("The URL answered with HTTP " + res.status)
const value = String((await res.json()).stock)
// The first run only remembers the value.
const previous = payload.state ? payload.state.value : undefined
if (previous === undefined || previous === value) return { state: { value } }
return {
state: { value },
events: [{ oldValue: previous, newValue: value }],
}
}Skabeloner til planlagte udløsere, under »Start fra en skabelon« på kortet »Planlægning«:
- Produkt, ordre eller kunde, der ikke er blevet opdateret i N dage - forældede kataloggennemgange, fastlåste ordrer, genvindingskampagner. Hver post udløser en handling én gang pr. inaktiv periode, og poster, der allerede var forfaldne, da funktionen blev aktiveret, udløser ikke alle handlinger på én gang.
- En værdi uden for Shopify er blevet ændret - en leverandørs lageropdatering, en valutakurs, en prisliste, med forskellen og procentdelen for tal.
- Nyt indlæg i et RSS- eller Atom-feed - leverandørnyheder, et produktoversigtsfeed, en statusside.
- Ingen ordrer i N timer - en pulsmåler for din butik. Udføres én gang, når ordrerne stopper, og én gang, når de vender tilbage.
Test kører din kode én gang uden at udløse noget og uden at gemme tilstanden, så du kan køre den så mange gange, du vil.
Brug af det i Shopify Flow
Alle brugerdefinerede triggere vises i Flow som den samme trigger: Brugerdefineret trigger. Føj den til en arbejdsgang, og tilføj derefter en betingelse, hvor »Trigger-håndtag er lig med din triggers håndtag«.
Dette navn vises på triggerens side og ændres aldrig, selvom du omdøber triggeren - så din arbejdsgang fortsætter som hidtil.
Hver nøgle, som din transformation returnerer, bliver til et felt i triggeren, og hele objektet er også tilgængeligt som JSON, hvis du hellere vil parse det selv.
Gem koden på GitHub
Du kan forbinde et GitHub-repository, så din triggerkode er under versionskontrol. Ændringer kan gennemgås i en pull-anmodning, du kan se, hvem der har ændret hvad, og du kan fortryde en transformation, der er holdt op med at fungere.
Tilslut den på siden »Developer« under »Connections«. Her vælger du, hvilke repositorier appen skal have adgang til, og du kan til enhver tid tilbagekalde denne adgang på GitHub.
Når et lager er valgt, overføres alle eksisterende triggere straks til det, og synkroniseringen foregår i begge retninger:
| Hvad du laver | Hvad sker der? |
|---|---|
| Opret, rediger eller dupliker en trigger i appen | Filen er blevet lagt ind i dit repository |
| Slet en trigger i appen | Filen er fjernet fra dit repository |
| Send en ændring til den tilknyttede gren | Triggerens kode opdateres i appen |
Hver trigger er en fil, der er opkaldt efter sit handle, og som indeholder præcis det modul, du ser i editoren - uden noget ekstra omkring det. Det betyder, at du kan åbne den i din egen editor, køre den og køre lint på den ligesom enhver anden JavaScript-fil.
Tilbage til en tidligere version
Du behøver ikke at kende Git for at fortryde en ændring. Når et repository er tilknyttet, viser editoren en rullemenu under »Version«, der indeholder en liste over alle tidligere versioner af den pågældende fil med dato og forfatter. Vælg en af dem, så indlæses den i editoren som en ikke-gemt ændring, så du først kan læse den - det er først, når du gemmer, at den genindsættes.
Test, mens du redigerer lokalt
Hvis du redigerer filen i dit eget redigeringsprogram, kan du køre den på den indsamlede eksempelbegivenhed uden først at gemme den i appen. Brug en API-nøgle med udførelsesniveauet fra udviklersiden:
curl -X POST https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
-H "Authorization: Bearer ftk_your_key_here" \
-H "Content-Type: application/json" \
-d "$(jq -Rn --rawfile c flow-triggers/high-value-vip-order.js '{code:$c}')"Du får det samme resultat tilbage, som appens »Test«-knap viser: om koden ville blive udført, output-objektet, dine »ctx.log«-linjer og køretiden.
Dette endpoint udløser intet og gemmer intet, og det bruger ikke noget af din abonnementskvote - så det kan trygt køres ved hver gemning fra en filovervågning. (Knappen »Kør test« i appen udløser dog dit Flow-workflow, så du kan se det køre fra start til slut; det er også gratis.)
Du kan også hente eksempel-payload’en separat ved hjælp af en læsenøgle, gemme den lokalt og køre filen helt offline:
curl https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
-H "Authorization: Bearer ftk_your_key_here"Bruger en brugerdefineret trigger min kvote i abonnementet?▾
Ja, og den tæller alle begivenheder, den overvåger - ikke kun dem, den udløser. Hvis din trigger overvåger »Produktopdatering«, og din butik har 60.000 produktopdateringer om måneden, er det 60.000 begivenheder, selvom din kode kun udløses på 100 af dem.
Vi modtager, fjerner dubletter og sætter hver af disse hændelser i kø, hvorefter vi kører din kode i et isoleret sandkasse-miljø - alt sammen inden din kode beslutter, om den skal udløses. Tallet afspejler dette arbejde.
Sagt på en anden måde: Det koster det samme, som den indbyggede trigger til den samme begivenhed ville have kostet. Der opkræves ikke ekstra for filtrering, og testning er altid gratis. En planlagt trigger tæller én gang pr. kørsel.
Er testen gratis?▾
Ja. Både knappen »Kør test« i appen og API-endpunktet »/test« er undtaget, selvom »Kør test« rent faktisk udløser dit Flow-workflow, så du kan se det køre. Det er kun live-udførelser, der trækker på din kvote, så du kan gentage en transformation så mange gange, du vil.
Hvorfor er payload._changes tom?▾
Enten lå opdateringen uden for de overvågede felter - for eksempel en ændring i lagerbeholdningen eller en variantændring på et produkt - eller også har appen endnu ikke nogen referenceværdi for den pågældende post. Referenceværdien gemmes, første gang appen registrerer en post, så den allerførste opdatering af en post, som appen aldrig før har set, indeholder ingen gammel værdi. Alle efterfølgende opdateringer gør derimod.
Kan min kode ændre data i min butik?▾
Nr. ctx.shopify kører med de læserettigheder, du har tildelt, og appen beder aldrig om skriveadgang. Det er helt bevidst: En trigger, der redigerer den post, den overvåger, starter sig selv igen, hvilket er den sløjfe, som Shopify slår arbejdsgange fra for. Rediger data i de Flow-handlinger, der følger efter triggeren.
Kan jeg skifte håndtaget senere?▾
Nej, og det er helt bevidst. Din Flow-workflow filtrerer på navnet, så hvis du ændrer det, vil det uden varsel stoppe udførelsen af den pågældende workflow. Du kan frit omdøbe udløseren - navnet forbliver det samme.
Navnet er også filnavnet i dit GitHub-repository, så det ændres heller aldrig.
Hvad sker der, hvis min kode indeholder en fejl?▾
Begivenheden springes over, og fejlen registreres i triggeren, så du kan se, hvad der gik galt. En mislykket transformation blokerer aldrig noget andet - dine øvrige triggere, både brugerdefinerede og indbyggede, fortsætter uberørt.
Hvor kører min kode?▾
I et isoleret sandkasse-miljø, adskilt fra resten af appen, med en kort tidsbegrænsning og uden adgang til din butiks loginoplysninger. Den får udelukkende adgang til den hændelsesdata, du har indsamlet, samt det, du henter via ctx.shopify eller ctx.fetch.
Hvad sker der, hvis jeg redigerer filen i GitHub og i appen på samme tid?▾
Den, du gemmer sidst, vinder. Når du gemmer i appen, overføres ændringerne til filen, og når du pusher til den tilknyttede gren, overskrives koden i appen. Hvis du hovedsageligt arbejder i dit repository, bør du betragte appens editor som skrivebeskyttet for at undgå uventede overraskelser.
Kan en fil i mit repository oprette en ny trigger?▾
Nej. En trigger skal også vide, hvilken Shopify-begivenhed den skal lytte efter, og filen indeholder kun kode - hvis man gætter på begivenheden, vil den blive knyttet til den forkerte ting. Opret først triggeren i appen, og rediger derefter filen frit.
Kan den udløses af begivenheder, som appen ikke allerede modtager?▾
Nej. En brugerdefineret trigger overvåger de begivenheder, som appen allerede abonnerer på for din butik, hvilket afhænger af de tilladelser, du har givet. Giver du tilladelse til en ressource, bliver dens begivenheder også tilgængelige for brugerdefinerede triggere. Alt andet skal du køre efter en tidsplan.
Næste skridt
- Udvikler-API, MCP, GitHub og udløsersimulering - administrere og teste triggere fra din egen kode eller en AI-assistent.
- Generer triggerkode med AI - Beskriv udløseren på almindeligt engelsk, og lad AI skrive koden.
- Planer og anvendelse - hvad der regnes som en begivenhed, og hvordan godtgørelsen fungerer.
- Sådan fungerer triggere - de indbyggede triggere og hvordan de udløses.

