HTTP Requests
Workflow methods use the standard global fetch(). There is no api.fetch() wrapper.
fetch() is available in normal workflow steps and in lifecycle hooks.
Shopify Admin GraphQL
Section titled “Shopify Admin GraphQL”When your fetch() URL targets your store’s Admin API domain, the platform automatically injects the X-Shopify-Access-Token header. Do not set that header manually.
Your store domain and Admin API version are available on the global env object:
export class Workflow { async start(data, headers, api) { const orderId = data.admin_graphql_api_id ?? `gid://shopify/Order/${data.id}`; 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: orderId, note: 'Processed' } }, }), } );
if (!res.ok) { throw new Error(`Shopify API ${res.status}: ${await res.text()}`); }
const json = await res.json(); if (json.errors?.length) { console.log('GraphQL errors:', json.errors); return; }
const { order, userErrors } = json.data.orderUpdate; if (userErrors.length) { console.log('User errors:', userErrors); return; }
console.log('Updated note:', order.note); }}Read-only queries use the same endpoint and the same automatic token injection:
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 } } }`, }), });
if (!res.ok) { throw new Error(`Shopify API ${res.status}: ${await res.text()}`);}
const json = await res.json();if (json.errors?.length) { throw new Error(JSON.stringify(json.errors));}
const orders = json.data.orders.nodes;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.
export class Workflow { async start(data, headers, api) { const { token, error } = await api.getOAuthToken('my-slack'); if (error) { console.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(). 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' }),});