Add support for idempotent workflow creation
@pranaygp is already working on this.
Since Nov 5, 2025.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 365
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 169
Description
Problem
Currently, when starting a workflow using start(), there is no way to provide a custom runId. The system always auto-generates a unique ID. This creates problems in several common scenarios:
Use Cases Affected
-
Webhook Deduplication: When webhook providers send duplicate events (common with retry logic), each call to
start()creates a new workflow run, even if it's processing the same event. -
Business Entity Uniqueness: Applications often need to ensure only one workflow runs per business entity (e.g., one order processing workflow per order ID). Currently, there's no built-in way to enforce this.
-
Retry-Safe Triggers: In distributed systems, workflow triggers may be retried. Without idempotency support, retries create duplicate workflow runs.
Example Scenario
// Webhook handler receives same event twice due to retry
async function handleOrderWebhook(orderId: string) {
// Problem: Both calls create separate workflow runs!
const run1 = await start(processOrderWorkflow, [orderId]);
const run2 = await start(processOrderWorkflow, [orderId]); // Duplicate!
}
Proposed Solution
Add optional runId field to StartOptions:
interface StartOptions {
deploymentId?: string;
runId?: string; // NEW: Enable idempotent workflow creation
}
Usage Example
async function handleOrderWebhook(orderId: string) {
const customRunId = `wrun_order_${orderId}`;
// First call creates workflow
const run1 = await start(processOrderWorkflow, [orderId], {
runId: customRunId
});
// Second call returns existing run (idempotent)
const run2 = await start(processOrderWorkflow, [orderId], {
runId: customRunId
});
console.log(run1.runId === run2.runId); // true
}
Implementation Requirements
world.runs.create()should accept optionalrunId- If
runIdexists, return existing run instead of throwing error - If
runIdnot provided, auto-generate as before (backward compatible) - All storage backends (world-local, world-postgres, world-vercel) should support this
Benefits
- No breaking changes: Fully backward compatible
- Simpler code: No external deduplication needed
- Safer: Built-in idempotency guarantees
- Standard pattern: Matches idempotency key patterns from Stripe, AWS, etc.
Related
This follows the same pattern already used for step-level idempotency with stepId (see docs).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.