Skip to content

Workflow API Overview

Every step method receives three arguments:

async start(data, headers, api) { ... }
ArgumentDescription
dataThe trigger payload (order object, HTTP body, scheduled context, etc.)
headersHTTP headers from the trigger request
apiThe JsWorkflows API object — platform capabilities
MethodDescription
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
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 — appendRows, readRows, updateRows, clearRange, and more
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
api.log()Write debug output visible in the test runner and run history

All secret variables you store via More actions → Manage variables in the workflow editor are available as properties on the global env object:

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. "2024-01"
}
}

Two values are always present regardless of your stored secrets:

KeyValue
env.SHOPIFY_STOREYour store’s myshopify.com domain
env.SHOPIFY_API_VERSIONThe Shopify API version configured for your app

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 — no extra configuration needed:

// Shopify GraphQL — token injected automatically
const 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' }),
});

Your Workflow class can optionally define two lifecycle hooks that run once when the entire run reaches a terminal state:

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, slack, log, dedupe, and runStore. The methods scheduleNextStep, waitForEvent, and csv are not available.

See Lifecycle Hooks for full behavioral rules and examples.