Dependencies

A Dependency defines an ordering constraint between two tasks. When you add a dependency from task A to task B, Clockwork ensures B cannot start until A reaches the required state — and re-schedules B automatically whenever A's timing changes.

Planning v2 availability. Merged to main and deployed to production (platform.clockwork-co.com; F1 to F7 and F9 to F11 on 2026-10-09, F12 pool assignment and the security fixes on 2026-10-12). The platform flag CLOCKWORK_PLANNING_V2 is currently on globally, in production and in preview, as of 2026-10-12: every organization gets planning-v2 behavior, and with the flag off the planning-v2 endpoints answer 404. Per-organization gating (CLOCKWORK_PLANNING_V2=orgs) is merged but not enabled in production, so the global flag is the only access control in effect. Exact dates and commits: Changelog.

When to use client.dependencies

  • Modeling sequential workflows where step N must finish before step N+1 starts
  • Expressing parallel branches that converge at a common later task
  • Adding lag time between tasks (e.g., "wait 24 hours after sending the email before following up")
  • Using start-to-start dependencies for tasks that should begin together

Methods

MethodDescription
create(input)Define ordering between two tasks
createBatch(input)Link up to 2,000 task pairs in one atomic request (one request toward rate limits)
get(depId)Fetch a dependency
update(depId, input)Update lag, maximum lag or dependency type
delete(depId)Remove the constraint
list(opts?)List dependencies, optionally those touching one task

dependencies.create(input)

Parameters

ParameterTypeRequiredDescription
fromTaskIdstringyesThe predecessor task
toTaskIdstringyesThe successor task
typeDependencyTypenoDependency type (default: 'finish_to_start')
lagMinutesintegernoMinimum wait between predecessor and successor, default 0 (the server accepts a negative value, an overlap; update does not)
lagSecondsinteger >= 0noFiner-grained lag. In the REST body and createBatch entries; not in the SDK's CreateDependencyInput type
maxLagMinutesintegernoMaximum wait (must be >= lagMinutes). See Maximum lag

Validation: a body that fails the schema (a non-UUID id, maxLagMinutes below lagMinutes) is 400 VALIDATION_ERROR; an unknown task is 404 NOT_FOUND; linking a task you cannot see is 403 FORBIDDEN; a link that would close a cycle is 400 CYCLE_DETECTED.

Dependency types

TypeMeaning
finish_to_startB starts after A finishes (most common)
start_to_startB starts no earlier than A starts
finish_to_finishB finishes no earlier than A finishes
start_to_finishB finishes no earlier than A starts

Maximum lag

maxLagMinutes bounds the gap the dependency type relates — for finish_to_start, how long after A finishes B may start. "Bake no more than 24 hours after assembling" is maxLagMinutes: 1440.

typescript
await client.dependencies.create({ fromTaskId: assemble, toTaskId: bake, maxLagMinutes: 1440 })
await client.dependencies.update(depId, { maxLagMinutes: null }) // clear it

maxLagMinutes is accepted on create, createBatch, update and on dependencies entries of createWithGraph. update takes null to clear it (sending the field absent leaves it unchanged), and re-checks the rule against the resulting values: lowering lagMinutes never fails, but raising it above an existing maxLagMinutes, or setting maxLagMinutes below lagMinutes, is 422 VALIDATION_ERROR. Creating one with maxLagMinutes < lagMinutes is 400 VALIDATION_ERROR (schema check).

How it is honored depends on how the job is scheduled:

SchedulingBehavior
Batch-graph, anchored (backward) and other planner-managed jobsThe planner keeps the gap within the limit. If the successor is held late, the predecessor is delayed (unstarted work only); in backward mode a predecessor pulled early by another successor pulls this successor early too.
Incremental edits on other jobsDetection only: a max_lag_exceeded conflict is raised on the successor, with the overrun in slackViolationSec.
Predecessor already started or doneIt cannot move, so the limit cannot be honored: you get a max_lag_exceeded conflict.

Started and completed tasks never move.

Example

typescript
// Basic sequential dependency
await client.dependencies.create({
  fromTaskId: 'tsk_setup',
  toTaskId: 'tsk_training',
})
 
// With a 24-hour lag (send email, then follow up the next day)
await client.dependencies.create({
  fromTaskId: 'tsk_send_email',
  toTaskId: 'tsk_follow_up',
  type: 'finish_to_start',
  lagMinutes: 1440, // 24 hours
})
 
// Parallel tasks that must start together
await client.dependencies.create({
  fromTaskId: 'tsk_kickoff',
  toTaskId: 'tsk_design_track',
  type: 'start_to_start',
})
await client.dependencies.create({
  fromTaskId: 'tsk_kickoff',
  toTaskId: 'tsk_engineering_track',
  type: 'start_to_start',
})

dependencies.createBatch(input)

Links many task pairs in one request (1 to 2,000 entries). The whole batch is checked for cycles together with the existing graph (422 CYCLE_DETECTED, with the task ids on the cycle), inserted in one statement, and each affected connected component is recomputed once. Counts as one request toward rate limits.

Each entry takes fromTaskId, toTaskId and optionally type, lagMinutes, lagSeconds and maxLagMinutes. The body is strict: an unknown field is 400 VALIDATION_ERROR. An unknown task id is 404 NOT_FOUND (with the missing ids); a task you cannot see is 403 FORBIDDEN. The endpoint answers 404 while planning v2 is off.

typescript
await client.dependencies.createBatch({
  dependencies: [
    { fromTaskId: a, toTaskId: b },
    { fromTaskId: b, toTaskId: c, lagMinutes: 10 },
  ],
})

dependencies.update(depId, input)

typescript
await client.dependencies.update('dep_abc123', {
  lagMinutes: 2880, // increase to 48 hours
})

update accepts lagMinutes (integer >= 0), type and maxLagMinutes (integer or null). fromTaskId and toTaskId cannot be changed: delete and re-create the dependency. It recomputes the schedule. A dependency you cannot see is 404 NOT_FOUND.

dependencies.list(opts?)

typescript
// Find all dependencies involving a task (as predecessor or successor)
const { data: deps } = await client.dependencies.list({ taskId: 'tsk_abc123' })

Cycle detection

Adding a dependency that would create a cycle throws a ClockworkError with code CYCLE_DETECTED: status 400 from create, status 422 from createBatch and createWithGraph. Always validate your graph structure when building workflows programmatically.

typescript
try {
  await client.dependencies.create({ fromTaskId: 'tsk_b', toTaskId: 'tsk_a' })
} catch (err) {
  if (err instanceof ClockworkError && err.code === 'CYCLE_DETECTED') {
    console.error('This dependency would create a cycle')
  }
}

Type reference

typescript
type DependencyType = 'finish_to_start' | 'start_to_start' | 'finish_to_finish' | 'start_to_finish'
 
interface Dependency {
  id: string
  fromTaskId: string
  toTaskId: string
  type: DependencyType
  lagMinutes?: number | null
  maxLagMinutes?: number | null   // null / absent = unbounded
  createdAt: string
}
 
interface CreateDependencyInput {
  fromTaskId: string
  toTaskId: string
  type?: DependencyType
  lagMinutes?: number
  maxLagMinutes?: number
}
 
interface UpdateDependencyInput {
  lagMinutes?: number
  type?: DependencyType
  maxLagMinutes?: number | null    // null clears it
}
  • Tasks — the tasks being linked
  • Conflicts — dependency violations surface as conflicts (max_lag_exceeded)
  • Batch Graphs — create tasks and dependencies together