Steps and connector flows
Use steps to record work. Use child flows to compose resumable website operations.
Return data from a step
const title = await ctx.step("read-title", async () => {
const page = await ctx.browser();
return page.title();
});After an input pause, execution starts again at the beginning. A completed step returns its recorded result without rerunning the callback.
| Put inside a step | Keep outside |
|---|---|
| Browser actions, requests, reading saved data | Deterministic mapping and validation |
| Time and random values used by later work | Calls to ctx.run, ctx.input, ctx.publish |
Return plain JSON: strings, numbers, booleans, null, arrays and objects. Represent dates as strings. Do not assign results to an outer variable from inside the callback.
Call a connector flow
import listBookings from "../connector/flows/list-bookings.js";
// Inside exec(ctx):
const bookings = await ctx.run(listBookings, {});Only createFlow connector operations can be called with ctx.run. Endpoints and syncs are entry points, not callable child flows.
The child shares the run, browser and connection. If it requests MFA, the whole run waits. Studio shows the endpoint or sync above its internal flows and steps.
Use stable step names. Repeated names in a sequential loop get separate recorded occurrences automatically. Await durable operations sequentially; do not nest them inside a step callback.
Interrupted actions
| Situation | Outcome |
|---|---|
| Completed step, then input pause | Completed result is reused |
| Worker lost during an external action | ACTION_UNCERTAIN; inspect the website before retrying |
| Retained browser session lost | SESSION_LOST |
| Local daemon crashes | Active local runs fail with LOCAL_SERVICE_INTERRUPTED |
Durable execution does not make arbitrary website mutations exactly once.