pending tasks can be updated.
Anything else - running, succeeded, failed, cancelled - is a 409.
See Status & History.
Full replace, not a merge
PUT replaces every client-owned field.
Anything you omit is reset to its default, exactly as if you had created the task with that body - omit headers and the old headers are gone, omit retries and it drops to 0.
Send the complete task every time.
The server-owned fields are never touched: id, idempotency_key, status, attempts and finished_at carry over as they were.
The key in particular is set once at creation and is not settable here - it names the task, not what the task does.
That matters because pending does not mean “never fired” - a task re-queued after a crash is pending with attempt history already logged, and updating it must not erase the evidence that a delivery may already have gone out.
Request fields
Identical to create.
Unlike create,
PUT never deduplicates and ignores Idempotency-Key - the id in the path already says which task you mean.
Example
Responses
Updating a task that is about to run
The scheduler picks up work slightly ahead of time, so an update can land while a task is already queued for delivery. That case is handled rather than raced: the scheduler re-reads the task immediately before firing, so a200 response means the update is what will be delivered.
If you moved execute_at, the queued run is dropped and the task fires at its new time.
If you changed anything else, the delivery goes out with the new url, headers, payload and retry settings.