Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Workflow

Workflows (the run that outlives the request)

A `workflow` is a run that survives the process it started in — it parks, waits days for a person or a timer, and resumes in a later deployment. The first-day mistake is writing one like a long function: every `await` is a place the app may be redeployed underneath you, so each awaited step carries a label you choose, and any call that leaves the platform is already a durable step you neither mark nor can forget.

stable1 example compiled by CIworkflowovervieworientationguide

Summary#

A workflow is a run that outlives the request that started it. It parks — on a person, on a timer, on a child run — and resumes later, possibly days later, possibly in a deployment that did not exist when it began. Everything below follows from that one fact.

enum InvoiceStatus { Submitted, Approved, Escalated }

entity Invoice {
  [Required, MaxLength(60)] string Reference;
  decimal Amount;
  InvoiceStatus Status = InvoiceStatus.Submitted;
}

workflow Approval {
  Tracks = Invoice.Status;
  Initial = Submitted;

  event Approve();

  state Submitted {
    Expire = TimeSpan.FromDays(2);    // nobody acted → the clock acts

    subscribe Approve();              // a person acts → a transition
    on Approve { goto Approved; }
    on Expire  { goto Escalated; }
  }
  terminal success Approved { }
  terminal cancel  Escalated { }
}

Description#

Three properties separate a workflow from a long function, and each is the source of a different first-day mistake.

It parks, so a step needs a name. Awaiting a child run suspends this one. While it is suspended the app can be recompiled and redeployed, and the resuming version has to recognise which step the run is sitting at — so every awaited step carries a label you write, a literal string, unique within the workflow. See Step labels (naming a child run so it survives a new version). A fire-and-forget start never parks and needs none.

It replays, so leaving the platform is special. On resume the engine re-executes the body up to where the run had reached. A call that already went out must not go out twice — a charge, an email, a webhook. You do not mark those: the compiler makes every outbound call a durable step and replays its recorded answer instead of repeating it (Automatic durability (steps you do not have to write)). What you must not do is reach for a clock or a random number as if they were ordinary — see Wall-time clocks (Accrues = false).

It is a state machine, so what may happen next is data. Workflow.Transitions(item) answers the moves this instance can make right now, each arm's guard already evaluated (Transitions — where this item may go next) — a board offers the lanes a card can actually reach instead of accepting a drop and having the engine refuse it afterwards. Workflow.Raise(item, move) takes one of them by handing the row back.

Who holds the work is also data. Workflow.Inbox<T> (what is waiting for me) is what this principal may act on; Workflow.Work<T> (everything outstanding) and its SLA numbers is every live slot of every run, whoever holds it, with the deadline budget attached. They are the same rows read from two viewpoints, and picking the wrong one is how an operator screen quietly becomes a personal one.

The pages#

Run osy docs workflow for the full listing. The groups:

See also#

Related

Workflow.Run (start a workflow)

Start the workflow bound to an entity's type, on that entity. Bare — `Workflow.Run(order)` — is fire-and-forget: start…

Transitions — where this item may go next

One row per move this instance can make right now, with each arm's guard evaluated against it. A board offers only the…

Automatic durability (steps you do not have to write)

Any call that leaves the platform — an outbound client call, an external service — is made a durable step by the…

Step labels (naming a child run so it survives a new version)

Every child workflow you AWAIT carries a label — a literal string you choose, naming that step. Awaiting parks the run…

Workflow.Work&lt;T&gt; (everything outstanding) and its SLA numbers

Every live slot of every run tracking T, whoever holds it — the unfiltered sibling of Workflow.Inbox. Rows carry…

Workflow.Inbox&lt;T&gt; (what is waiting for me)

The current principal's queue: every slot they can act on, across every run of every workflow that tracks T. Rows carry…

subscribe

Declares that a workflow state waits on an event, and configures the wait — who may hold it, who may hand it on…

fan-out (foreach subscribe)

One `subscribe` declaration that expands into MANY parallel wait slots — one per element of a collection. A fan-out…

Assigned / Finished (milestones)

A milestone puts an SLA on a slot's progress — Assigned (someone must PICK IT UP within Within) and Finished (it must…

For(entity).Audit

Reads a running instance's lifecycle timeline — every transition, claim, deposit, reminder and refusal as an…