A task has a draft you edit and published versions that run. Every write — dashboard, API, MCP or TaskCoPilot — lands on the draft; runs and email batches use the published version until you publish again. The lifecycle matches agent versions.
GET /v1/tasks/{task_id} returns the draft as config, the live version number as published_version (null if never published), its published_at, and the publish history as versions (each with version, published_at, published_by). The list endpoint leaves versions null.

Saving and publishing

A draft can be unfinished: it saves without a prompt, an output field or a provider key. Checks come in two grades:
  • Every write checks what a half-written task can still be judged on: duplicate output names, unpublished tools, unknown {{tokens}}, field shapes.
  • Publish adds what needs a finished config: a provider key for the model, at least one output field, valid operation trees in attached tools, no attached tool containing a handoff, and no email batch that the change would break.
POST /v1/tasks/{task_id}/validate runs the publish checks without publishing and returns errors and warnings. POST /v1/tasks/{task_id}/publish refuses with a 400 listing every error, or freezes the draft as the next version and returns its warnings. Both are listed in errors and limits.

What a publish freezes

  • Tools are pinned. Each tools entry is stored as {"tool_id": "…", "tool_version": 3}. Republishing a tool does not reach the task until the task is published again.
  • MCP servers are not pinned. The integration’s allowed_tools is read live.
  • FAQ content is not pinned. A version freezes which FAQs are attached; their entries are read at the start of each run.
GET /v1/tasks/{task_id}/versions/{version} returns one version’s frozen config, tool pins included.

Rollback

POST /v1/tasks/{task_id}/versions/{version}/rollback makes an earlier version live immediately.
  • No new version is created; published_version points at the older one.
  • The draft is replaced by that version’s config. Unpublished edits are lost.
  • The restored draft is unpinned, so publishing it again pins the tool versions current at that time.
  • updated_at is set to that version’s published_at, so the task does not read as having unpublished edits.
  • The task’s name reverts to that version’s; a 409 if another task now uses it.
  • A run already in flight finishes on the version it started with.

Email batches follow the published version

A batch is refused against a task that has never been published (publish '<name>' before drafting a batch with it). After that, each drafting pass reads the task’s current published version, so rows drafted after a publish use the new version; each row’s run records which version drafted it. A batch’s CSV columns and field_map are fixed once rows are uploaded, so publishing or rolling back is refused while a drafting batch would break: a variable the new config makes required that the CSV has no column for, or an output field removed that field_map uses. DELETE /v1/tasks/{task_id} is refused with a 409 while a batch is still drafting with the task. Deleting a task deletes its versions. Its runs survive with task_id null and task_name and task_version intact.

Endpoints

In the dashboard, History in the task editor compares any two versions, including the draft.

Next

Errors and limits

Every refusal at save, publish and run time.

Running and runs

Which version a run uses, and task_version on the run.