getWorld
Retrieves the World instance for direct access to workflow storage, queuing, and streaming backends. This async function returns a Promise<World> which provides low-level access to manage workflow runs, steps, events, and hooks.
Use this function when you need direct access to the underlying workflow infrastructure, such as listing all runs, querying events, or implementing custom workflow management logic.
API signature
Parameters
This function does not accept any parameters.
Returns
Returns a Promise<World> object:
| Name | Type | Description |
|---|---|---|
analytics | Analytics | Optional analytics read namespace for observability surfaces.
These APIs return metadata-only rows intended for UI/CLI listing and
trace views. Payload-bearing fields remain on the canonical runtime
storage APIs (runs, steps, events, hooks) and their RemoteRef
resolution path. |
specVersion | number | The Workflow protocol spec version this World implements, and the version
stamped on every run it creates.
Declare SPEC_VERSION_CURRENT rather than a literal. The runtime checks
this against [SPEC_VERSION_CURRENT, SPEC_VERSION_MAX_SUPPORTED] before it
creates or replays anything, and refuses a World outside that range: below
the floor the World allocates event ids the runtime cannot read positions
out of (see the event log contract above), above the ceiling it speaks a
spec this runtime has not learned. |
capabilities | WorldCapabilities | Feature capabilities this World implementation supports. See WorldCapabilities. Absent (or absent members) means "unsupported": runtime optimizations gated on a capability fail closed. |
validateRunExecutionContext | (value: Record<string, unknown>) => void | Validates a dynamic run's complete execution context against
World-specific limits. start() calls it only for dynamic starts, before
any durable start side effect, so implementations throw to refuse the
start. |
getRuntimeDeadline | () => Promise<Date | undefined> | Absolute wall-clock time when the current function invocation will be terminated by the hosting platform, if known. Used to optimize runtime behavior. |
start | () => Promise<void> | A function that will be called to start any background tasks needed by the World implementation. For example, in the case of a queue backed World, this would start the queue processing. |
close | () => Promise<void> | Release any resources held by the World implementation (connection pools, listeners, etc.).
After calling close(), the World instance should not be used again.
This is important for CLI commands and short-lived processes that need to exit cleanly
without relying on process.exit(). |
resolveLatestDeploymentId | () => Promise<string> | Resolve the most recent deployment ID for the current deployment's environment.
Used when deploymentId: 'latest' is passed to start(). The implementation
determines the latest deployment that shares the same environment (e.g., same
"production" target or same git branch for "preview" deployments) as the
current deployment.
Not all World implementations support this: it is only implemented by
world-vercel where deployment routing is meaningful. |
getEncryptionKeyForRun | { (run: { runId: string; deploymentId: string; workflowName: string; attributes: Record<string, string>; createdAt: Date; updatedAt: Date; status: "pending" | "running"; ... 10 more ...; completedAt?: undefined; } | { ...; } | { ...; } | { ...; }): Promise<...>; (runId: string, context?: Record<...> | undefined): Pr... | Retrieve the AES-256 encryption key for a specific workflow run.
The returned key is a ready-to-use 32-byte AES-256 key. The World
implementation handles all key retrieval and derivation internally
(e.g., HKDF from a deployment key). The core encryption module uses
this key directly for AES-GCM encrypt/decrypt operations.
Two overloads:
- getEncryptionKeyForRun(run): Preferred. Pass a WorkflowRun when
the run entity already exists. The World reads any context it needs
(e.g., deploymentId) directly from the run.
- getEncryptionKeyForRun(runId, context?): Used when the run entity
is not locally available, such as start() before run creation or a
forwarded writable stream carrying its owning deployment context. The
context parameter carries opaque world-specific data (e.g.,
{ deploymentId } for world-vercel) needed to resolve the correct key.
When context is omitted, the World assumes the current deployment.
When not implemented, encryption is disabled: data is stored unencrypted. |
createRunId | (options?: Readonly<Record<string, unknown>> | undefined) => string | Mint a new workflow run ID.
Called by start() to generate the unique ID for a newly-created run.
The returned value is the "bare" ID (without any wrun_ prefix); the
core attaches the prefix.
Implementations are free to embed world-specific metadata in the ID
(e.g., a region identifier) as long as the returned string remains a
valid ULID. When omitted, start() falls back to generating a standard
monotonic ULID. |
uploadDynamicWorkflowCode | (runId: string, params: { workflowName: string; code: Uint8Array<ArrayBufferLike>; }) => Promise<string> | Upload a dynamic run's serialized workflow VM code to the World's blob
storage ahead of run_created, returning the ref key to attach to the
run.
The inline path — sending the bytes on run_created itself — is the
common case and needs nothing from this method: a generated orchestration
function is usually a couple of KB, and keeping it on the creating write
costs no extra round-trip. This exists for the tail: a definition too
large to ride the event wire, which has to be streamed separately and
referenced.
Worlds that store run records whole (local, Postgres) have no size
pressure and leave this unset; start() then always sends inline, and a
definition over its own source limit is rejected client-side rather than
silently truncated.
The upload necessarily precedes the run it belongs to, so implementations
must accept a runId that does not exist yet, and must scope the stored
object to the caller's tenant and that run so it is reclaimed with the
run's other storage. |
getEnvironment | () => string | undefined | The environment this World's writes are attributed to by the backend
(@workflow/world-vercel: 'production' | 'preview' | 'development').
Synchronous and side-effect free: implementations derive this from
configuration or environment variables they already hold, never from a
network call. Return undefined when the environment can't be determined.
The value MUST match the attribution the backend will actually apply to
this client's writes: for world-vercel that means keeping it in lockstep
with the x-vercel-environment header (proxy path) and the OIDC token's
environment claim (in-deployment path). A value that merely looks
plausible is worse than undefined, because callers use it to detect
cross-tenant mismatches and a wrong answer manufactures a false one.
start() stamps this into the queue message's runInput so the consuming
deployment can tell that a message it was handed was created against a
different environment than its own. Not all Worlds have an environment
dimension: local dev and Postgres have exactly one tenant, so they omit
this and the check is skipped. |
describeRun | (run: Readonly<Record<string, unknown>>) => Record<string, string | null> | Promise<Record<string, string | null> | null> | null | World-specific display fields for a run.
Tooling (e.g. the workflow inspect CLI) calls this to enrich a
run's listing row / detail output with fields only the world can
derive: a region decoded from the run ID, placement read off the
run's executionContext, a shard, a billing tier, etc. Consumers
render each returned key as an additional column/property; when the
hook is absent, no extra fields appear at all.
The contract:
- **Cheap and pure.** Called once per displayed run, so avoid I/O.
Prefer deriving fields from the entity you are given.
- **Read only what you recognize.** The argument is the run entity
as the caller has it (a full storage run, or a leaner analytics
row), typed loosely for the same reason as createRunId.
Tolerate missing fields.
- **Must not throw.**
- A null field value means "applicable but undeterminable" and is
preserved as null in structured output (vs. the hook being
absent, where the key does not exist at all). Return null or an
empty object to add nothing for a given run. |
telemetry | Telemetry | Optional telemetry write namespace for non-critical observability signals. |
getDeploymentId | () => Promise<string> | |
invoke | (runId: string, payload: unknown, options?: InvokeOptions | undefined) => Promise<unknown> | Send an invocation to a run's executor. Must be enabled via capabilities.invoke. An invocation carries an out-of-band request that must be processed by the executor. The executor's response is propagated back and errors are rethrown by invoke(). A transport error is an unknown outcome: the request may have been successfully processed by the executor, for example, and the success result may be lost. |
isDeploymentUnavailableError | (error: unknown) => boolean | Returns true only when a queue error definitively means the explicitly targeted deployment cannot receive the message. Unknown and transient errors must return false so the current delivery can be retried safely. |
queue | (queueName: string, message: { __healthCheck: true; correlationId: string; runId?: string | undefined; } | { runId: string; invoke?: true | undefined; requestId?: string | undefined; input?: unknown; ... 13 more ...; hookResumeTiming?: { ...; } | undefined; }, opts?: QueueOptions | undefined) => Promise<...> | Enqueues a message to the specified queue. |
queueBatch | (queueName: string, messages: readonly { message: { __healthCheck: true; correlationId: string; runId?: string | undefined; } | { runId: string; invoke?: true | undefined; requestId?: string | undefined; ... 14 more ...; hookResumeTiming?: { ...; } | undefined; }; opts?: QueueOptions; }[]) => Promise<...> | Enqueues several messages to the SAME logical queue in as few round trips
as the backing transport allows. Optional: callers MUST fall back to
per-message Queue.queue when a World does not implement it.
Exists for wide fan-outs. Publishing an N-branch Promise.all one message
at a time costs N round trips through a bounded connection pool, and that
cost is paid before the fan-out's first step body runs, so it lands
directly on time-to-first-step.
Contract:
- Results are returned in input order, one per input message. Returning
a different number of results than there were messages is a contract
violation the runtime rejects the whole batch on: an omitted result is
indistinguishable from a message that was never published, and reading
it as success would strand that step with no error anywhere.
- Partial failure is normal. A rejected entry reports error; a
retryable entry may succeed if the whole batch is published again.
Implementations MUST NOT throw for a per-entry failure — reserve
rejection for request-level failures where no entry outcome is known.
- Callers are expected to pass opts.idempotencyKey per message, because
the recovery for both a request-level failure and a retryable entry is
to republish the batch: without keys that redelivers the entries that
already succeeded.
- Every message must target one logical queueName. Implementations are
free to split the batch (by transport cap, or by any per-message routing
dimension they derive from the payload, such as region or physical
topic); the split must not be observable in the returned order. |
createQueueHandler | (queueNamePrefix: string, handler: (message: unknown, meta: { attempt: number; deliveryCount?: number; createdAt?: Date; queueName: string; messageId: string & $brand<"MessageId">; requestId?: string; }) => Promise<...>) => (req: Request) => Promise<...> | Creates an HTTP queue handler for processing messages from a specific queue.
A rejected handler must retry the same message with an incremented attempt.
With invoke: true, the return value is response data delivered by World.
Only ordinary wake results interpret { timeoutSeconds } as queue control.
meta.messageId SHOULD be stable across redeliveries of the same message
(one ID per enqueued message, reused on every delivery attempt). The
runtime's inline step ownership uses it as a liveness lease: the lazy
step_started records the handling invocation's messageId, and only a
delivery of that same message may re-execute the step before the
ownership lease expires (crash recovery via queue redelivery). A World
whose queue mints a fresh ID per delivery degrades gracefully: owner
redeliveries fall back to the delayed-backstop path instead of executing
immediately, adding recovery latency but never wedging or duplicating.
meta.deliveryCount, when the World reports it, is 1 on a message's first
delivery and increments on every redelivery, whatever caused it (lease
lapse, a rejected handler, a { timeoutSeconds } result). A World whose
queue cannot say leaves it undefined. meta.createdAt, when known, is
when the message was first enqueued, unchanged across its redeliveries. |
registerHandler | (prefix: string, handler: (req: Request) => Promise<Response>) => void | Optional. Delivers every message whose queue name starts with prefix to
handler inside this process, without HTTP. For Worlds whose queue runs
in the app's own process (world-local, world-postgres). handler takes
the same request a route serving createQueueHandler(prefix, ...) would,
so passing that route handler works. Takes precedence over
Queue.registerRoute for the prefix; a later registration for the
same prefix replaces an earlier one.
Keyed by the exact prefix: registering __wkf_workflow_ also takes the
Workflow SDK's own messages. A queue consumer other than the Workflow SDK
uses a prefix of its own, __{namespace}_wkf_workflow_. |
registerRoute | (prefix: string, path: string) => void | Optional. The route (a path on the app's base URL, such as
/api/agent/queue) that this World's HTTP delivery POSTs the messages of
prefix to. The app serves it with createQueueHandler(prefix, ...).
A prefix without a registered route goes to the Workflow SDK's flow route
(WORKFLOW_FLOW_ROUTE), so only the Workflow SDK relies on that
path, and a queue consumer of its own registers its route here, before
the World's start() when it has one (start() re-enqueues active runs).
An in-process handler for the prefix (Queue.registerHandler)
takes precedence; a later registration for the same prefix replaces an
earlier one.
Worlds that deliver by subscription rather than by path (world-vercel)
do not implement this: subscribe the route to the prefix's topics there. |
streamFlushIntervalMs | number | Number of milliseconds a stream waits for additional chunks to arrive
before flushing to the underlying transport.
Default 0: the first chunk dispatches immediately, and chunks
arriving while a request is in flight coalesce into the next group.
Setting this to > 0 trades first-chunk latency for fewer requests.
The WORKFLOW_STREAM_FLUSH_INTERVAL_MS environment variable, when
set, overrides this option.
Not supported by all worlds. |
streams | { createWriteSession?(runId: string, name: string, options: CreateStreamWriteSessionOptions): StreamWriteSession; ... 6 more ...; getInfo(runId: string, name: string): Promise<...>; } | |
runs | { get(id: string, params: GetWorkflowRunParams & { resolveData: "none"; }): Promise<WorkflowRunWithoutData>; get(id: string, params?: (GetWorkflowRunParams & { ...; }) | undefined): Promise<...>; get(id: string, params?: GetWorkflowRunParams | undefined): Promise<...>; ... 4 more ...; cancelMany?(request: { ...; }):... | |
steps | { get(runId: string, stepId: string, params: GetStepParams & { resolveData: "none"; }): Promise<StepWithoutData>; get(runId: string, stepId: string, params?: (GetStepParams & { ...; }) | undefined): Promise<...>; get(runId: string, stepId: string, params?: GetStepParams | undefined): Promise<...>; list(params: ListW... | |
events | { create<T extends RunCreatedEventRequest>(runId: string | null, data: T, params?: CreateEventParams | undefined): Promise<EventResult<T["eventType"]>>; create<T extends CreateEventRequest>(runId: string, data: T, params?: CreateEventParams | undefined): Promise<...>; createBatch?(runId: string, events: BatchEventRe... | The event log, and the one part of this interface with a requirement the
types cannot express: **the World allocates every event id, and every id
is a slot**: evnt_ followed by the event's dense, 1-based position in
its run's log, zero-padded to 26 characters. Use slotToEventId() to
format one.
Not a capability to opt into. The runtime reads a position out of every id
it loads (requireEventSlot) and fails the run if it cannot, so a World
whose ids are not positions cannot replay anything at all. Two properties
are what the runtime actually relies on:
- **Density.** A run's slots are contiguous from 1, so the number of
events a reader holds *is* the position of the last one. That is what
makes CreateEventParams.eventCount a complete statement of the
writer's snapshot in a single integer, and what lets a reader tell a
complete log from a truncated one by its length.
- **Bump and report.** A create never fails because its requested slot is
taken. The World advances to the next free slot, commits there, and
returns the events occupying the slots it skipped over on the success
response (see EventResult.events ). The writer learns its
snapshot was stale without the write being rejected, which is why no
World needs a precondition guard.
Allocating at the commit is what makes a reader's log a *prefix* of the
run's log rather than a prefix with a hole in it. A World that hands a
position out earlier, and can therefore let an event land behind one a
reader has already passed, breaks the property every replay depends on. |
hooks | { get(hookId: string, params?: GetHookParams | undefined): Promise<{ runId: string; hookId: string; token: string; ownerId: string; projectId: string; environment: string; createdAt: Date; ... 7 more ...; claimedFrom?: { ...; } | undefined; }>; getByToken(token: string, params?: GetHookParams | undefined): Promise<.... | |
experimental_snapshots | { save(runId: string, data: Uint8Array<ArrayBufferLike>, metadata: { eventsCursor: string | null; createdAt: Date; eventCount?: number | undefined; ... 5 more ...; formatVersion?: number | undefined; }): Promise<...>; load(runId: string): Promise<...>; delete(runId: string): Promise<...>; } | VM snapshot storage for the QuickJS engine's VM-memory snapshotting. |
World SDK
The World object provides access to several entity interfaces. See the World SDK reference for complete documentation:
Storage
Query runs, steps, hooks, and the underlying event log.
Streams
Read, write, and manage data streams.
Queue
Low-level queue dispatch (internal SDK infrastructure).
Data hydration
Step and run data is serialized using the devalue format. Use workflow/observability to hydrate it for display:
See workflow/observability for the full hydration and parsing API.
List workflow runs (display names)
List workflow runs and derive human-readable names from the workflowName field:
The workflowName field contains a machine-readable identifier like workflow//./src/workflows/order//processOrder.
Use parseWorkflowName() to extract the shortName (e.g., "processOrder")
and moduleSpecifier for display in your UI.