Creating Workflows

This guide walks through building a complete workflow from scratch — a template with role slots, an instance with tasks and dependencies, and monitoring it to completion.

The pattern

Template (blueprint) → Instance (live run) → Tasks (work items)

A template is optional. You can also create instances directly and add tasks programmatically without any template.

Option A: Template-first

Use a template when you'll run the same workflow shape repeatedly.

1. Define the template

typescript
import { Clockwork } from '@clockwork/sdk'
 
const client = new Clockwork({ apiKey: process.env.CLOCKWORK_API_KEY })
 
const { data: template } = await client.workflowTemplates.create({
  name: 'New Client Onboarding',
  description: 'Standard workflow for onboarding a new client',
  roles: [
    { slotName: 'account-manager', required: true },
    { slotName: 'legal', required: true },
  ],
})

2. Publish a version with a manifest

typescript
await client.workflowTemplates.publish(template.id, {
  manifest: {
    nodes: [
      { id: 'n1', name: 'Send welcome email', durationMinutes: 15 },
      { id: 'n2', name: 'Schedule kickoff call', durationMinutes: 30 },
      { id: 'n3', name: 'Collect signed NDA', durationMinutes: 60 },
      { id: 'n4', name: 'Set up client portal', durationMinutes: 45 },
    ],
    edges: [
      { from: 'n1', to: 'n2' },
      { from: 'n2', to: 'n3' },
      { from: 'n2', to: 'n4' },
    ],
  },
  roles: [
    { slotName: 'account-manager', required: true },
    { slotName: 'legal', required: true },
  ],
})

3. Instantiate for each new client

typescript
const { data: instance } = await client.workflowInstances.create({
  templateId: template.id,
  subjectType: 'client',
  subjectId: 'client_acme',
})
 
// Fill role slots with real people
await client.roles.fill(instance.id, 'account-manager', 'res_alice')
await client.roles.fill(instance.id, 'legal', 'res_bob')

Option B: Programmatic (no template)

Use this when each workflow is unique or generated dynamically.

typescript
// 1. Create a bare instance
const { data: instance } = await client.workflowInstances.create({
  subjectType: 'deal',
  subjectId: 'deal_987',
})
 
// 2. Add tasks
const { data: t1 } = await client.tasks.create({
  name: 'Due diligence review',
  workflowInstanceId: instance.id,
  durationMinutes: 240,
  deadline: '2026-07-15T17:00:00Z',
  priority: 'high',
})
 
const { data: t2 } = await client.tasks.create({
  name: 'Legal sign-off',
  workflowInstanceId: instance.id,
  durationMinutes: 60,
})
 
const { data: t3 } = await client.tasks.create({
  name: 'Send countersigned agreement',
  workflowInstanceId: instance.id,
  durationMinutes: 15,
})
 
// 3. Define ordering
await client.dependencies.create({ fromTaskId: t1.id, toTaskId: t2.id })
await client.dependencies.create({ fromTaskId: t2.id, toTaskId: t3.id })
 
// 4. Assign tasks
await client.tasks.assign(t1.id, 'user_analyst')
await client.tasks.assign(t2.id, 'user_legal')

Monitoring progress

typescript
// Check instance status
const { data: instance } = await client.workflowInstances.get('wi_abc123')
console.log(instance.status) // 'running'
 
// List tasks with their scheduling info
const { data: tasks } = await client.tasks.list({ workflowInstanceId: 'wi_abc123' })
 
for (const task of tasks) {
  console.log(
    task.name,
    task.status,
    task.isCritical ? '⚠ CRITICAL' : '',
    task.slackSec !== null ? `${Math.round(task.slackSec / 3600)}h float` : '',
  )
}

Handling completion

typescript
// When a user completes a task
const { data: result } = await client.tasks.complete('tsk_abc123')
 
// Log which tasks just became available
for (const unlocked of result.unlockedTasks) {
  console.log(`Now ready: ${unlocked.name}`)
  await notifyAssignee(unlocked.id)
}

Next steps