Batch Graphs
Clockwork recomputes the schedule every time a task or dependency changes. Building a job one call at a time (a print order, a project plan, an event run-of-show) means one request and one recompute per task and per dependency. workflowInstances.createWithGraph creates the whole job in one request, one transaction and one schedule computation.
Planning v2 availability. Merged to
mainand 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 flagCLOCKWORK_PLANNING_V2is 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 answer404. 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.
Batch endpoints return 404 while the platform flag CLOCKWORK_PLANNING_V2 is off.
This guide shows the shape of a batch request. Every field, default, limit and error is listed in the createWithGraph reference. For a worked example with an anchor, resources and leveling see Schedule a whole workflow in one call.
Create a job
Runnable: docs-examples/guide-batch-graph.mts.
const { data } = await client.workflowInstances.createWithGraph({
instance: { name: 'Poster run' },
resources: [
{ ref: 'press', name: 'Press', type: 'asset', capacity: 2 },
{ ref: 'operator', name: 'Operator', type: 'human', capacity: 1 },
],
tasks: [
{ ref: 'plates', name: 'Make plates', durationMinutes: 30 },
{ ref: 'print', name: 'Print posters', durationMinutes: 90, resourceRefs: ['press'] },
{ ref: 'pack', name: 'Pack and label', durationMinutes: 20, resourceRefs: ['operator'] },
],
dependencies: [
{ from: 'plates', to: 'print', lagMinutes: 15 }, // 15 minutes for the plates to cool
{ from: 'print', to: 'pack' },
],
})
data.refs // { plates: '…uuid', print: '…uuid', pack: '…uuid' }
data.resourceRefs // { press: '…uuid', operator: '…uuid' }
data.schedule.tasks // per-task plannedStart / plannedEnd / latestStart / slackSec / isCritical
data.schedule.requiredStart
data.schedule.makespanMinutes // 155: 30 + 15 lag + 90 + 20
data.schedule.criticalPath
data.schedule.conflictsref values are keys you choose (1 to 100 characters); they must be unique within the request and are how tasks, dependencies and resources refer to each other. Every field is validated before anything is written: an unknown field is a 400, not silently ignored.
resourceRefs links a resource to a task. To make a task claim capacity (both slots of the press, an operator only at the start and end) use requirements; see the reference and Resource Requirements.
Limits
| Limit | Value |
|---|---|
| Tasks per request | 500 (at least 1) |
| Dependencies per request | 2,000 |
| Resources per request | 100 |
Going over a limit is a 400 VALIDATION_ERROR (for example "Array must contain at most 500 element(s)"). The request counts as one request toward your rate limit, however large it is.
Errors and atomicity
If anything is wrong, nothing is created:
| Status | code | Meaning | Extra fields |
|---|---|---|---|
400 | VALIDATION_ERROR | Malformed body, unknown field, or a limit exceeded | details |
422 | UNKNOWN_REF | A dependency points at a task ref that does not exist, or a task points at a resource ref that does not exist | refs (the offending refs) |
422 | DUPLICATE_REF | Two tasks (or two resources) share a ref | refs |
422 | CYCLE_DETECTED | The dependencies form a cycle | refs (the tasks on the cycle) |
422 | ANCHOR_REQUIRED | schedulingDirection: 'backward' or an anchorWindow without instance.anchor | refs |
422 | REQUIREMENT_EXCEEDS_CAPACITY | A task needs more units than the resource's capacity | refs |
The reference-style problems are all listed in errors; code/refs describe the first. The full table, with exact messages, is in the reference.
Adding to an existing job
Both calls below are also behind the flag.
// Many tasks at once: one schedule computation for the batch.
// `ref` is optional here and is only echoed back in `refs`.
const { data } = await client.tasks.createBatch({
workflowInstanceId: instanceId,
tasks: [
{ ref: 'proof', name: 'Colour proof', durationMinutes: 25 },
{ name: 'Courier pickup', durationMinutes: 10 },
],
})
// Many dependencies at once, by task id.
await client.dependencies.createBatch({
dependencies: [
{ fromTaskId: data.refs.proof, toTaskId: ids.print },
{ fromTaskId: ids.pack, toTaskId: data.tasks[1].id, lagMinutes: 10 },
],
})tasks.createBatchplans the new tasks together, on their own: they are not placed against the job's existing tasks, and a dependency between them is added afterwards withdependencies.createBatch.resourceRefis only valid in a whole-graph request; here useresourceIdorpoolIdinrequirements.dependencies.createBatchchecks the whole batch for cycles together with the existing graph and returns422 CYCLE_DETECTEDwith the taskidson the cycle (the message isDependency cycle: <id> -> <id> -> …). Each affected connected component is recomputed once.
MCP
The create_workflow_graph tool wraps the same endpoint so an agent can build a job in a single call. There is no MCP tool for the dependency batch.