Overview
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
Section titled “api methods”| Method | Description |
|---|---|
api.scheduleNextStep() | Schedule the next step (or fan-out to multiple steps) |
api.waitForEvent() | Pause a run and resume it when an external event arrives |
api.dedupe() | Prevent duplicate processing within a time window for your own business logic or external-event idempotency |
api.getOAuthToken() | Get a valid access token for a connected OAuth service |
api.google.getServiceAccountToken() | Get a Google access token using a service account key (cached 58 min) |
api.google.sheet.* | Google Sheets helpers such as appendRows, readRows, updateRows, and clearRange |
api.csv.* | Import a CSV from a URL, split it into chunks, and process each chunk in a separate step |
api.runStore.* | Store and retrieve values scoped to the current run |
console.log() | Write debug output visible in the test runner and run history |
api.getAttachment() | Retrieve an email attachment as an ArrayBuffer (email-trigger workflows only) |
api.deleteAttachment() | Delete an email attachment from storage (email-trigger workflows only) |
api.sendToFlow() | Fire a JsWorkflow Data Received trigger in Shopify Flow from any step |
api.sendEmail() | Send a notification email to your configured notification address (paid plans) |
Global env object
Section titled “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.MY_API_KEY; // a secret you stored const shop = env.SHOPIFY_STORE; // auto-injected: "mystore.myshopify.com" const ver = env.SHOPIFY_API_VERSION; // auto-injected: e.g. "2026-04" }}Two values are always present regardless of your stored secrets:
| Key | Value |
|---|---|
env.SHOPIFY_STORE | Your store’s myshopify.com domain |
env.SHOPIFY_API_VERSION | 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
Section titled “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 automaticallyconst res = 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: `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
Section titled “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 for full behavioral rules and examples.