Your First Workflow
This guide builds a simple workflow from scratch so you can learn the basic JsWorkflows pattern before moving on to more advanced templates and operational workflows.
If you prefer, you can also start from a template instead of writing your first workflow yourself. This page focuses on the from-scratch path so you can understand how workflow steps are structured.
Watch the walkthrough
Section titled “Watch the walkthrough”This video follows the same example workflow used in this guide: creating an orders/paid workflow, scheduling a follow-up step, testing the workflow, and checking the run output.
What we’re building
Section titled “What we’re building”A workflow that:
- receives an
orders/paidwebhook - schedules a follow-up step for 5 minutes later
- sends a Slack notification with the order details
This is a simple example, but it teaches a pattern you will use in more advanced workflows too, including approvals, operational checks, delayed follow-up, and multi-step automations.
Why split work into steps
Section titled “Why split work into steps”For Shopify webhook workflows, JsWorkflows acknowledges the incoming webhook before your workflow code runs. Your start step is not responsible for sending the HTTP response back to Shopify.
Even so, it is usually best to keep start focused on orchestration:
- validate the incoming data
- prepare the payload for later steps
- hand the real work off to another step
This matters because:
startis the reserved entry point for a runstartcannot be scheduled again withapi.scheduleNextStep()- delayed or retryable work should happen in later named steps
- scheduled steps run later as separate workflow executions
That makes workflows easier to recover, retry, extend, and reason about when something goes wrong downstream.
The workflow code
Section titled “The workflow code”Create a new workflow in the editor with the orders/paid trigger, then replace the code with:
export class Workflow { /** * Entry step. * Keep this focused on validation, payload preparation, and scheduling. */ async start(data, _headers, api) { const orderId = data.id; const orderName = data.name; const total = data.total_price; const currency = data.currency;
await api.scheduleNextStep({ delay: '5 min', action: 'notifySlack', payload: { orderId, orderName, total, currency }, });
console.log(`Queued Slack notification for ${orderName}`); }
/** * Follow-up step. * This is where delayed or retryable work should happen. */ async notifySlack({ orderId, orderName, total, currency }, _headers, api) { const { token, error } = await api.getOAuthToken('my-slack'); if (error || !token) { console.log('Could not get Slack token:', error || 'Missing Slack token'); return; }
const res = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ channel: 'C0123456789', text: `*New paid order* ${orderName} — ${total} ${currency}`, }), });
if (!res.ok) { console.log(`Slack HTTP ${res.status}:`, await res.text()); return; }
const json = await res.json(); if (!json.ok) { console.log('Slack API error:', json.error || 'unknown_error'); return; }
console.log(`Notified Slack for order ${orderName}`); }}In every step, the third argument api gives your workflow access to JsWorkflows platform helpers such as scheduling, OAuth tokens, logging, and state.
In this example:
api.scheduleNextStep()queues the later Slack stepapi.getOAuthToken('my-slack')loads the access token for a saved Slack OAuth connection
The string 'my-slack' is the OAuth handle. You create it when you connect Slack in JsWorkflows, then reuse that handle in your workflow code.
To create that Slack connection:
- go to Settings → OAuth2 Tokens, or
- in the workflow editor open More actions → Manage OAuth2 tokens
Then connect Slack and give the connection a handle such as my-slack. Your workflow code uses that handle later with api.getOAuthToken(...).
How it works
Section titled “How it works”The start step:
- reads the fields needed from the webhook payload
- schedules
notifySlackfor 5 minutes later - exits after the follow-up work has been queued
The notifySlack step:
- gets the Slack OAuth token by handle
- posts the message to Slack using the channel ID
- logs the result
The scheduled step runs later as a separate execution. It is not the original start step waiting in memory for 5 minutes. The payload passed to api.scheduleNextStep() is what the later step receives when it starts.
This same pattern scales into more advanced workflows:
- delayed follow-up checks
- approval and review flows
- fan-out processing
- retryable external API operations
For Shopify webhook workflows, JsWorkflows already deduplicates duplicate webhook deliveries before your workflow code runs. You would typically use api.dedupe() yourself only when you need business-level deduplication for your own workflow logic.
Test it step by step
Section titled “Test it step by step”-
In the workflow editor, open More actions → Test data. This shows the Request body and Request headers, both of which are editable.
For Shopify webhook triggers, the request body is pre-filled with the topic’s sample payload. You can also inspect the sample at any time through More actions → Sample payload.
-
For a faster first test, temporarily change
delay: '5 min'in the code todelay: 10. That is the minimum allowed delay and lets you verify the full flow quickly before switching it back to 5 minutes. -
Leave the sample payload as-is, or adjust a few fields such as the order name, total, or currency if you want to make the test output easier to recognize.
-
Click More actions → Run test.
The workflow starts from
startexactly as a live run would. ThenotifySlackstep is then scheduled and will run after the configured delay. -
Watch the Live Output panel below the editor. It shows step executions in real time, including status, duration, step name, retries, and payload size.
Enable it
Section titled “Enable it”When you are ready, toggle the workflow Active.
From that point on, every orders/paid event that matches the workflow trigger can start a run.
What to learn from this page
Section titled “What to learn from this page”Your first workflow does not need to be complicated. The important thing is to learn the basic structure:
startis the reserved entry stepstartis best used for validation, payload preparation, and scheduling- later steps do the delayed, retryable, or external work
Once that pattern is clear, the rest of the platform becomes much easier to understand.