Shopify Webhook
The Shopify Webhook trigger fires your workflow whenever a selected event occurs in your store. Any Shopify webhook topic is supported.
The data object
Section titled “The data object”For Shopify Webhook triggers, data is the raw Shopify webhook payload passed directly as the top-level object. There is no wrapper — you access order fields, customer fields, etc. directly:
class Workflow { async start(data, headers, api) { // data IS the order (for orders/* topics) api.log('Order ID:', data.id); api.log('Total:', data.total_price); api.log('Customer email:', data.email); }}Useful headers
Section titled “Useful headers”The headers object includes all Shopify webhook headers:
| Header | Description |
|---|---|
x-shopify-shop-domain | Your store’s myshopify.com domain |
x-shopify-topic | The webhook topic, e.g. orders/paid |
x-shopify-webhook-id | Unique ID for this delivery |
x-shopify-attempt-number | Which retry attempt this is (1 = first) |
class Workflow { async start(data, headers, api) { const shop = headers['x-shopify-shop-domain']; const topic = headers['x-shopify-topic']; api.log(`Received ${topic} from ${shop}`); }}Common webhook topics
Section titled “Common webhook topics”| Category | Topics |
|---|---|
| Orders | orders/create, orders/updated, orders/paid, orders/fulfilled, orders/cancelled, orders/partially_fulfilled |
| Customers | customers/create, customers/update, customers/delete |
| Products | products/create, products/update, products/delete |
| Inventory | inventory_levels/update, inventory_items/update |
| Refunds | refunds/create |
| Checkouts | checkouts/create, checkouts/update, checkouts/delete |
| Fulfilments | fulfillments/create, fulfillments/update |
| Collections | collections/create, collections/update, collections/delete |
Calling the Shopify API from within the workflow
Section titled “Calling the Shopify API from within the workflow”Use the global fetch() with your store’s myshopify.com URL. The platform automatically injects the X-Shopify-Access-Token header — no manual token management needed. Use env.SHOPIFY_STORE and env.SHOPIFY_API_VERSION which are always available:
class Workflow { async start(data, headers, api) { // Token is injected automatically — env.SHOPIFY_STORE is "mystore.myshopify.com" const resp = await fetch( `https://${env.SHOPIFY_STORE}/admin/api/${env.SHOPIFY_API_VERSION}/graphql.json`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: `mutation UpdateOrderNote($input: OrderInput!) { orderUpdate(input: $input) { order { id note } userErrors { field message } } }`, variables: { input: { id: `gid://shopify/Order/${data.id}`, note: 'Processed by JsWorkflows' } }, }), } );
const { data: gqlData } = await resp.json(); const { order, userErrors } = gqlData.orderUpdate; if (userErrors.length) { api.log('Errors:', userErrors); return; } api.log('Updated note:', order.note); }}Deduplication
Section titled “Deduplication”Shopify retries undelivered webhooks up to 19 times over 48 hours. JsWorkflows automatically deduplicates deliveries using the x-shopify-webhook-id header — if a webhook ID has already been processed, subsequent retries are silently ignored within a 120-second window.