Data retention

A finished run leaves data behind: the inputs and outputs of the workflow and each of its steps, the payloads on its event log, and anything written to its streams. How long that data is kept is decided by the World you are running on, not by the SDK.

experimental_retention on start() lets a run ask for a specific retention period, rather than the World's default.

Deleting a run's data as soon as it ends

const run = await start(processDocumentWorkflow, [documentId], {
  experimental_retention: 0,
})

0 asks the World to delete the run's user data the moment the run completes or fails, rather than keeping it for the World's default window.

Two values are accepted today:

ValueMeaning
0Delete user data as soon as the run reaches a terminal state.
'default'Use the World's default. Identical to omitting the option.

The option is prefixed experimental_ because both its name and the set of values it accepts are expected to change.

What is deleted, and what is not

Deleted: the run's input, output and error; every step's input, output and error; the payloads on the event log; and stream contents.

Kept: the run, step and event records themselves — their ids, timestamps, status, step names, and any attributes you set. They are kept for the World's default period so the run stays visible in the CLI and web UI. A purged run is still listed and still traceable; its payloads simply read back as expired.

Inspecting a purged run shows it as expired rather than failing. The Workflow CLI renders the run's own input, output and error as <data expired>:

workflow inspect runs wrun_...

Step, hook and event payloads read back empty. On the Vercel World they also render as <data expired>; on Worlds that clear the stored value outright they simply show as empty. Either way the data is gone — the difference is only in how the absence is labelled.

You cannot read the return value of a run started with experimental_retention: 0. The deletion races your own read of the result and generally wins, so await run.returnValue throws RunExpiredError instead of resolving.

This is a known limitation. If you need the result, send it somewhere you control, e.g. a step that writes it to your own store, rather than reading it back off the run.

RunExpiredError is not specific to experimental_retention: 0. Any run read after its retention window has passed throws it, and the error carries runId, runStatus and expiredAt when the World still has them — so a caller can tell a successful run whose result is gone from a failed one whose error is gone. If the run's metadata is gone too, the World reports the run as missing and you get WorkflowRunNotFoundError instead.

Retention is implemented by the World

The SDK records your preference; it does not enforce it. start() writes the value onto the run as the reserved $retention attribute, and the World decides what to do when the run ends. Attributes are a spec version 4 feature, so experimental_retention: 0 throws against an older World rather than being recorded and ignored. A World that does implement spec version 4 but not retention keeps the data. If you need certainty that a specific World deletes your data, confirm it against that World's own documentation rather than the presence of this option.