· via Vercel blog
Vercel Workflow SDK 5.3.1 adds atomic idempotent starts, closing duplicate-run race
Vercel's Workflow SDK can now admit a run and claim its hook in one atomic step, so duplicate starts fail safely instead of racing past a token lookup. The fix ships in workflow 5.3.1.

Vercel has shipped version 5.3.1 of its Workflow SDK with a change to how runs begin: start() can now admit a run and claim its hook in a single atomic step. According to the Vercel changelog, duplicate workflow starts now fail safely, closing a documented duplicate-start race that existed in the previous two-step pattern.
The race being closed
Before this release, idempotent starts were built by looking up an existing hook with getHookByToken() and then calling start(). Because the lookup and the start were separate operations, the changelog describes a gap in which two concurrent requests carrying the same token could both pass the lookup and both launch runs.
The new API collapses those steps into one. Callers pass an experimental_hook: { token } option to start(), and the SDK admits the run and claims the hook atomically. Exactly one run can own a given token; a second start() with the same token throws a HookConflictError carrying the owner's conflictingRunId, and the duplicate never becomes a run at all.
Duplicates get routed, not dropped
A failed duplicate start does not have to be a dead end. The changelog demonstrates a request handler that catches HookConflictError and forwards the duplicate's payload to the run that owns the token using resumeHook(token, payload), returning that run's ID with a started: false flag.
Because the hook is claimed the moment the run is admitted, resumeHook works even before the run begins executing, and Vercel says early payloads are buffered and delivered in order. Inside the workflow, createHook({ token }) with the same token binds to the hook claimed at start: Vercel describes this binding as guaranteed, since it registers no new hook, adds no workflow step and resolves getConflict() to null.
The token is the idempotency key
The token is expected to come from the caller's own domain: an order ID, an invoice ID, an import ID or a request ID. Vercel says the token alone guarantees idempotency, and the workflow does not have to open the hook at all. If it never does, the token served purely as an idempotency key and the run completes normally.
For protection after a run finishes, experimental_minRetention keeps the token unavailable once the run has ended, with the retention window counted from start(), so late duplicates are refused rather than spawning a fresh run. Both the start option and the retention control are still marked experimental in this release. Vercel directs users to upgrade to workflow 5.3.1 and read the idempotency documentation to adopt the feature.
Why it matters
Idempotency is table stakes for anything driven by webhooks, client retries or double-clicks, and until now Workflow SDK users had to assemble it from a two-step lookup-and-start pattern that carried a known race. Moving the guarantee into the platform means one token maps to exactly one run, with a machine-readable conflict error in place of a silent duplicate.
The design also gives redundant requests somewhere useful to go: instead of discarding a duplicate, an application can hand its payload to the run that owns the token and have it consumed in order. That turns an error path into a normal part of request handling, which matters for order processing, imports and any workflow that receives updates keyed by a domain identifier. It is a small API addition with an outsized effect on correctness.
- #vercel
- #workflow-sdk
- #idempotency
- #developer-tools