Durable functions give a deployed function a stable, restartable handle: start it as a named instance and the platform records that instance in Postgres, so you can look up its status, output, and history by id — and dedupe duplicate starts. It is a durability primitive around a single function run, not a workflow DSL. For multi-step branching, fan-out, scheduled work, and human approvals, use graph Pipelines (see the Pipelines page).
A durable instance runs one ordinary deployed function to completion. Its return value becomes the instance output, and the instance ends in COMPLETED (or FAILED if the handler throws). Because the start is written to Postgres before the function runs, the instance survives a restart — the platform re-drives it from the durable queue instead of losing the work.
Start a durable instance
From a Node.js function, the injected global.durable client starts another deployed function as a durable instance with startNew(functionName, instanceId?, input?). The target is a normal handler — not a generator. Pass an instanceId to make the start idempotent: a second startNew with the same id returns the existing instance instead of launching a duplicate. Whatever you pass as input becomes that function's event.
// process-order.js — an ORDINARY deployed function (Node.js). Its return value // becomes the durable instance output. Deploy it like any other function. exports.handler = async (event, context) => { const order = event; // the input you passed to startNew() const charged = await chargeCard(order); await reserveStock(order); return { ok: true, orderId: order.id, chargeId: charged.id }; }; // starter.js — a second Node.js function launches "process-order" as a durable, // idempotent, restartable instance and follows it by id. global.durable is // injected into the Node runtime only. exports.handler = async (event, context) => { // startNew(functionName, instanceId?, input?). Passing an instanceId makes the // start idempotent — a duplicate start with the same id returns the same instance. const { instanceId } = await global.durable.startNew( 'process-order', `order-${event.orderId}`, { id: event.orderId, amount: event.amount }, ); // Optional: block up to N ms for the instance to finish (resolves on COMPLETED, // throws on FAILED/TERMINATED/timeout). Otherwise poll global.durable.getStatus(id). const status = await global.durable.waitForCompletion(instanceId, 30000); return { instanceId, state: status.status, output: status.output }; };
Control client (Node.js only)
global.durable is injected into the Node.js runtime — Python and Go functions do not receive it. It exposes:
startNew(name, instanceId?, input?)— launch a named function as an instance; resolves to{ instanceId, … }.getStatus(instanceId)— read the instance:status,output,createdAt,lastUpdatedAt, and eventhistory(ornullif it does not exist).waitForCompletion(instanceId, timeoutMs?)— poll until the instance reachesCOMPLETEDand resolve with its status; throws onFAILED,TERMINATED, or timeout.terminate(instanceId, reason?)— stop an instance and mark itTERMINATED.
HTTP API
The same operations are available over HTTP for callers outside a Node function. POST /durable/orchestrations/{name}/start returns 202 with { instanceId, statusQueryGetUri, terminatePostUri, … }, and GET /durable/orchestrations/{instanceId}/status returns the instance. These routes authenticate with your login session or a Bearer PAT (scopes jobs:read / jobs:write); global.durable calls them for you with an internal token.
Durable vs jobs vs pipelines
Reach for a durable instance when you want a single background run with a stable id — idempotent start, a status you can poll, and restart-safety. Reach for a background job (see Background jobs) when you want fire-and-forget work with automatic retries, backoff, and a dead-letter queue. Reach for a graph Pipeline when several functions form a workflow with branching, fan-out, scheduled triggers, or a human-approval gate (the Human gate node). All three run the same functions on the same Postgres-backed queue underneath.