HTTP Requests
Workflow steps use the standard global fetch() — the same Web API available in browsers and Cloudflare Workers. There is no api.fetch() wrapper.
Shopify Admin API
Section titled “Shopify Admin API”When your fetch() URL matches your store’s myshopify.com domain, the platform automatically injects the X-Shopify-Access-Token header. You do not need to manage or pass the token yourself.
Your store domain and API version are available on the global env object:
class Workflow { async start(data, headers, api) { // Token is injected automatically — no Authorization header needed 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: `mutation UpdateOrderNote($input: OrderInput!) { orderUpdate(input: $input) { order { id note } userErrors { field message } } }`, variables: { input: { id: `gid://shopify/Order/${data.id}`, note: 'Processed' } }, }), } ); const { data: gqlData } = await res.json(); const { order, userErrors } = gqlData.orderUpdate; if (userErrors.length) { api.log('Errors:', userErrors); return; } api.log('Updated note:', order.note); }}Shopify GraphQL API
Section titled “Shopify GraphQL API”The same automatic token injection applies to all GraphQL requests:
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 GetOrders { orders(first: 10) { nodes { id name } } }`, }), });const { data } = await res.json();OAuth-connected services
Section titled “OAuth-connected services”Use api.getOAuthToken(handle) to get a valid access token for a connected OAuth service, then add it to your request headers manually. The platform automatically refreshes the token if it has expired.
class Workflow { async start(data, headers, api) { const { token, error } = await api.getOAuthToken('my-slack'); if (error) { api.log('OAuth error:', error); return; }
await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ channel: '#orders', text: `New order: ${data.id}` }), }); }}The handle is the name you assigned when connecting the service in OAuth Connections.
External APIs with secrets
Section titled “External APIs with secrets”Store API keys via More actions → Manage variables in the workflow editor and access them via env.*:
const res = await fetch('https://api.example.com/notify', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${env.EXAMPLE_API_KEY}`, }, body: JSON.stringify({ event: 'order_paid', orderId: data.id }),});Sending JSON bodies
Section titled “Sending JSON bodies”Pass a plain JavaScript object as the body by serialising it with JSON.stringify. Always set Content-Type: application/json when doing this:
const res = await fetch('https://api.example.com/endpoint', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'value' }),});