Workflows
Automation workflows with DAG-based execution, scheduling, and webhooks
Workflows enable powerful automation with a DAG (Directed Acyclic Graph) execution model. Unlike simple sequential workflows, Arky workflows can run nodes in parallel, branch conditionally, loop over data, and trigger from webhooks or schedules.
Key Concepts
Workflow Structure
A workflow consists of nodes and edges:
- Nodes: Individual operations (trigger, HTTP request, condition, loop, wait)
- Edges: Connections between nodes that define execution flow
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Trigger │────▶│ HTTP │────▶│ HTTP │
└─────────┘ │ (fetch) │ │ (notify)│
└─────────┘ └─────────┘
Node Types
| Type | Description |
|---|---|
trigger | Entry point - webhook or scheduled |
http | Make HTTP requests to external APIs |
if | Conditional branching based on expressions |
loop | Iterate over arrays |
wait | Pause execution for a duration |
Create Workflow
/v1/businesses/{businessId}/workflows sdk.workflow.createWorkflow() Create a new automation workflow.
const result = await sdk.workflow.createWorkflow({
key: 'order-notification',
status: 'ACTIVE',
nodes: {
trigger: {
type: 'trigger',
event: 'order.created'
},
fetchOrder: {
type: 'http',
method: 'GET',
url: 'https://api.yourapp.com/orders/${trigger.data.orderId}',
headers: {
'Authorization': 'Bearer ${env.API_KEY}'
}
},
sendSlack: {
type: 'http',
method: 'POST',
url: 'https://hooks.slack.com/services/xxx',
body: {
text: 'New order #${fetchOrder.response.orderNumber} - $${fetchOrder.response.total}'
}
}
},
edges: [
{ id: 'e1', source: 'trigger', sourceOutput: 'default', target: 'fetchOrder' },
{ id: 'e2', source: 'fetchOrder', sourceOutput: 'default', target: 'sendSlack' }
]
});Parameters
| Name | Type | Description |
|---|---|---|
key required | string | Unique workflow identifier |
status optional | ACTIVE | DRAFT | ARCHIVED | Workflow status (default: DRAFT) |
nodes required | Record<string, WorkflowNode> | Map of node IDs to node definitions |
edges required | WorkflowEdge[] | Connections between nodes |
schedule optional | string | Cron expression for scheduled execution (e.g., '0 9 * * *' for 9am daily) |
Get Workflow
/v1/businesses/{businessId}/workflows/{id} sdk.workflow.getWorkflow() Retrieve a workflow by ID.
const result = await sdk.workflow.getWorkflow({
id: 'wf_abc123'
});
if (result.ok) {
const workflow = result.val;
console.log(workflow.key, workflow.status);
console.log('Nodes:', Object.keys(workflow.nodes));
}
List Workflows
/v1/businesses/{businessId}/workflows sdk.workflow.getWorkflows() List workflows with filtering and pagination.
const result = await sdk.workflow.getWorkflows({
statuses: ['ACTIVE'],
query: 'notification',
limit: 20,
cursor: null
});
if (result.ok) {
const { items, cursor } = result.val;
items.forEach(workflow => {
console.log(workflow.key, workflow.status);
});
}
Parameters
| Name | Type | Description |
|---|---|---|
ids optional | string[] | Filter by specific workflow IDs |
statuses optional | string[] | Filter by status (ACTIVE, DRAFT, ARCHIVED) |
query optional | string | Search in workflow keys |
limit optional | number | Items per page (max 100) |
cursor optional | string | Pagination cursor |
sortField optional | string | Sort field (createdAt, key) |
sortDirection optional | asc | desc | Sort direction |
Update Workflow
/v1/businesses/{businessId}/workflows/{id} sdk.workflow.updateWorkflow() Update an existing workflow.
const result = await sdk.workflow.updateWorkflow({
id: 'wf_abc123',
key: 'order-notification-v2',
status: 'ACTIVE',
nodes: {
// Updated node definitions
trigger: { type: 'trigger' },
notify: {
type: 'http',
method: 'POST',
url: 'https://api.example.com/notify'
}
},
edges: [
{ id: 'e1', source: 'trigger', sourceOutput: 'default', target: 'notify' }
]
});
Delete Workflow
/v1/businesses/{businessId}/workflows/{id} sdk.workflow.deleteWorkflow() Delete a workflow.
const result = await sdk.workflow.deleteWorkflow({
id: 'wf_abc123'
});
Trigger Workflow
/v1/workflows/trigger/{secret} sdk.workflow.triggerWorkflow() Trigger a workflow execution via webhook. This endpoint does not require authentication - the secret in the URL validates the request.
// The secret comes from your workflow's webhook URL
const result = await sdk.workflow.triggerWorkflow({
secret: 'wh_secret_abc123xyz',
// Pass any data to the workflow
orderId: 'ord_123',
customerEmail: '[email protected]',
items: [
{ productId: 'prod_1', quantity: 2 }
]
});Parameters
| Name | Type | Description |
|---|---|---|
secret required | string | Workflow webhook secret from the trigger URL |
[key: string] optional | any | Any additional data to pass to the workflow |
The trigger data is available in your workflow nodes as trigger.data. For example, if you pass orderId, access it as ${trigger.data.orderId}.
Node Types Reference
Trigger Node
The entry point for workflow execution. Every workflow must have exactly one trigger node.
{
type: 'trigger',
event: 'order.created' // Optional: listen for specific business events
}
HTTP Node
Make HTTP requests to external APIs. Supports template variables from previous nodes.
{
type: 'http',
method: 'POST', // GET, POST, PUT, PATCH, DELETE
url: 'https://api.example.com/endpoint',
headers: {
'Authorization': 'Bearer ${env.API_KEY}',
'Content-Type': 'application/json'
},
body: {
userId: '${trigger.data.userId}',
message: 'Hello from Arky!'
},
timeoutMs: 30000 // Optional timeout in milliseconds
}
Available Variables:
${trigger.data.*}- Data passed to the trigger${env.*}- Environment variables configured in your business${previousNodeId.response.*}- Response from a previous HTTP node
If Node
Conditional branching based on JavaScript expressions.
{
type: 'if',
condition: 'trigger.data.amount > 100'
}
If nodes have two outputs:
true- Executed when condition is truthyfalse- Executed when condition is falsy
edges: [
{ id: 'e1', source: 'checkAmount', sourceOutput: 'true', target: 'sendHighValueAlert' },
{ id: 'e2', source: 'checkAmount', sourceOutput: 'false', target: 'sendStandardNotification' }
]
Loop Node
Iterate over arrays. Each iteration runs the connected nodes.
{
type: 'loop',
array: 'trigger.data.items' // Path to array
}
Inside loop iterations, access the current item via ${loop.item} and index via ${loop.index}.
Wait Node
Pause execution for a specified duration.
{
type: 'wait',
duration: '5m' // Supports: 30s, 5m, 2h, 1d
}
Scheduled Workflows
Run workflows on a schedule using cron expressions.
const result = await sdk.workflow.createWorkflow({
key: 'daily-report',
status: 'ACTIVE',
schedule: '0 9 * * *', // Every day at 9:00 AM UTC
nodes: {
trigger: { type: 'trigger' },
generateReport: {
type: 'http',
method: 'POST',
url: 'https://api.yourapp.com/reports/generate'
},
sendEmail: {
type: 'http',
method: 'POST',
url: 'https://api.yourapp.com/email/send',
body: {
to: '[email protected]',
subject: 'Daily Report',
body: '${generateReport.response.reportUrl}'
}
}
},
edges: [
{ id: 'e1', source: 'trigger', sourceOutput: 'default', target: 'generateReport' },
{ id: 'e2', source: 'generateReport', sourceOutput: 'default', target: 'sendEmail' }
]
});
Common Cron Expressions:
| Expression | Description |
|---|---|
0 9 * * * | Every day at 9:00 AM |
0 */2 * * * | Every 2 hours |
0 9 * * 1 | Every Monday at 9:00 AM |
0 0 1 * * | First day of every month |
*/15 * * * * | Every 15 minutes |
Complete Example: Order Processing
const workflow = await sdk.workflow.createWorkflow({
key: 'process-new-order',
status: 'ACTIVE',
nodes: {
// Entry point
trigger: {
type: 'trigger',
event: 'order.created'
},
// Check order value
checkValue: {
type: 'if',
condition: 'trigger.data.total > 10000' // Over $100
},
// High value: notify sales team
notifySales: {
type: 'http',
method: 'POST',
url: 'https://hooks.slack.com/services/sales-channel',
body: {
text: '🎉 High-value order! $${trigger.data.total / 100} from ${trigger.data.customerEmail}'
}
},
// Always: update inventory
updateInventory: {
type: 'http',
method: 'POST',
url: 'https://api.yourapp.com/inventory/decrement',
body: {
items: '${trigger.data.items}'
}
},
// Send confirmation email
sendConfirmation: {
type: 'http',
method: 'POST',
url: 'https://api.yourapp.com/email/order-confirmation',
body: {
orderId: '${trigger.data.orderId}',
email: '${trigger.data.customerEmail}'
}
}
},
edges: [
// Trigger -> Check Value
{ id: 'e1', source: 'trigger', sourceOutput: 'default', target: 'checkValue' },
// High value -> Notify Sales
{ id: 'e2', source: 'checkValue', sourceOutput: 'true', target: 'notifySales' },
// Both paths -> Update Inventory (runs in parallel with notification)
{ id: 'e3', source: 'trigger', sourceOutput: 'default', target: 'updateInventory' },
// After inventory -> Send Confirmation
{ id: 'e4', source: 'updateInventory', sourceOutput: 'default', target: 'sendConfirmation' }
]
});
Workflows execute nodes in parallel when possible. In the example above, checkValue and updateInventory run simultaneously since they both connect directly to the trigger.