Overview The api object and global env available in every workflow step. Every executable step method, including start and any named continuation step you schedule later, receives three arguments: async start(data, headers, api) { ... } | Argument | Description | | data | The trigger payload or step payload (order object, HTTP body, scheduled context, or the payload from api.scheduleNextStep()) | | headers | Trigger/request headers when the step was entered from an HTTP-style source. For scheduled runs and most continuation steps, treat this as optional context and do not rely on it unless the trigger docs say it is present. | | api | The JsWorkflows API object, which exposes platform capabilities | api methods | Method | Description | | api.scheduleNextStep() (/workflow-api/scheduling/) | Schedule the next step (or fan-out to multiple steps) | | api.waitForEvent() (/workflow-api/wait-for-event/) | Pause a run and resume it when an external event arrives | | api.dedupe() (/workflow-api/scheduling/#deduplication--apidedupe) | Prevent duplicate processing within a time window for your own business logic or external-event idempotency | | api.getOAuthToken() (/oauth/overview/) | Get a valid access token for a connected OAuth service | | api.google.getServiceAccountToken() (/workflow-api/google-service-account/) | Get a Google access token using a service account key (cached 58 min) | | api.google.sheet. (/workflow-api/google-service-account/#apigooglesheet) | Google Sheets helpers such as appendRows, readRows, updateRows, and clearRange | | api.csv. (/workflow-api/csv/) | Import a CSV from a URL, split it into chunks, and process each chunk in a separate step | | api.runStore.* (/workflow-api/state/) | Store and retrieve values scoped to the current run | | console.log() (/workflow-api/logging/) | Write debug output visible in the test runner and run history | | api.getAttachment() (/workflow-api/attachments/) | Retrieve an email attachment as an ArrayBuffer (email-trigger workflows only) | | api.deleteAttachment() (/workflow-api/attachments/) | Delete an email attachment from storage (email-trigger workflows only) | | api.sendToFlow() (/triggers/shopify-flow/#pushing-data-into-flow-from-any-step) | Fire a JsWorkflow Data Received trigger in Shopify Flow from any step | | api.sendEmail() (/workflow-api/send-email/) | Send a notification email to your configured notification address (paid plans) | Global env object All secret variables you store via More actions → Manage variables in the workflow editor are available as properties on the global env object: export class Workflow { async start(data, headers, api) { const apiKey = env.MYAPIKEY; // a secret you stored const shop = env.SHOPIFYSTORE; // auto-injected: "mystore.myshopify.com" const ver = env.SHOPIFYAPIVERSION; // auto-injected: e.g. "2026-04" } } Two values are always present regardless of your stored secrets: | Key | Value | | env.SHOPIFYSTORE | Your store's myshopify.com domain | | env.SHOPIFYAPIVERSION | The Shopify API version configured for your app | For Shopify webhook triggers, duplicate delivery protection already happens before your workflow code runs. api.dedupe() is still useful when you need your own workflow-specific or business-level deduplication rules. Making HTTP requests Use the global fetch() to call any URL. For Shopify Admin API requests, the platform automatically injects the X-Shopify-Access-Token header when the URL matches your store domain, so no extra configuration is needed: // Shopify GraphQL, token injected automatically const res = await fetch( https://${env.SHOPIFYSTORE}/admin/api/${env.SHOPIFYAPI_VERSION}/graphql.json, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: query { orders(first: 10) { nodes { id name } } } }), } ); const { data } = await res.json(); For OAuth-connected services, retrieve the token with api.getOAuthToken() and add it yourself: const { token } = await api.getOAuthToken('my-slack'); const res = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': Bearer ${token}, }, body: JSON.stringify({ channel: '#orders', text: 'Hello' }), }); Lifecycle hooks Your Workflow class can optionally define two lifecycle hooks that run once when the entire run reaches a terminal state. These hooks do not use the normal (data, headers, api) step signature: export class Workflow { async start(data, headers, api) { ... } // Called when the entire run (all branches) completes successfully async onWorkflowComplete(api) { ... } // Called when a step throws an uncaught error that terminates the run async onWorkflowError(err, api) { ... } } Inside hooks, api is restricted to: getOAuthToken, google, log, dedupe, runStore, and sendEmail. The methods scheduleNextStep, waitForEvent, and csv are not available. The global fetch() API is still available inside hooks. See Lifecycle Hooks (/workflow-api/lifecycle-hooks/) for full behavioral rules and examples.