> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gavai.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflows

> Named, multi-step write sequences — the unit of work an agent fires or a page invokes when one capability is not enough.

export const PageMeta = ({audience, readingTime, prereqs, level}) => {
  const items = [audience && ({
    key: "audience",
    label: "AUDIENCE",
    value: audience
  }), readingTime && ({
    key: "time",
    label: "READ",
    value: readingTime
  }), level && ({
    key: "level",
    label: "LEVEL",
    value: level
  }), prereqs && prereqs.length > 0 && ({
    key: "prereqs",
    label: "PREREQS",
    value: prereqs.join(" · ")
  })].filter(Boolean);
  if (items.length === 0) return null;
  return <div className="gv-page-meta" role="group" aria-label="Page metadata">
      {items.map(item => <div className="gv-page-meta__item" key={item.key}>
          <span className="gv-page-meta__label">{item.label}</span>
          <span className="gv-page-meta__value">{item.value}</span>
        </div>)}
    </div>;
};

export const Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

export const BrandCard = ({eyebrow, title, href, icon, badge, cta, featured = false, horizontal = false, children}) => {
  const classes = ["gv-card", featured && "gv-card--featured", horizontal && "gv-card--horizontal"].filter(Boolean).join(" ");
  const inner = <>
      {icon && <span className="gv-card__icon" aria-hidden="true">
          <Icon icon={icon} />
        </span>}
      <div className="gv-card__main">
        {(eyebrow || badge) && <div className="gv-card__meta">
            {eyebrow && <Eyebrow label={eyebrow} tone="muted" />}
            {badge && <span className="gv-card__badge">{badge}</span>}
          </div>}
        <h3 className="gv-card__title">{title}</h3>
        {children && <div className="gv-card__body">{children}</div>}
        {cta && <span className="gv-card__cta">
            {cta}
            <svg className="gv-card__cta-arrow" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <line x1="7" y1="17" x2="17" y2="7" />
              <polyline points="7 7 17 7 17 17" />
            </svg>
          </span>}
      </div>
    </>;
  return href ? <a className={classes} href={href}>
      {inner}
    </a> : <div className={classes}>{inner}</div>;
};

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="CONCEPT" />

<PageMeta audience="Developers" readingTime="6 min" prereqs={["Capabilities"]} level="Concepts" />

A capability is one typed action. Sometimes one is enough — open a checkout, send an email, store a file. Sometimes the work is *two or three actions that must succeed together*: charge the card, mark the order paid, send the receipt. **Workflows** are how the platform packages those multi-step sequences. By the end of this page you will know what a workflow is, how it differs from chaining capabilities yourself, and when to reach for one.

## A first workflow

A workflow is a named recipe — an ordered list of steps, each one a capability invocation or a control-flow node, addressed by a single id when something fires it. The shape:

```json workflow.json theme={null}
{
  "id": "checkout_complete",
  "actions": [
    { "kind": "capability", "name": "collectPayment", "args": { "plan_id": "$.input.plan_id" } },
    { "kind": "capability", "name": "runQuery",       "args": { "table": "orders", "patch": { "status": "paid" } } },
    { "kind": "capability", "name": "sendEmail",      "args": { "template": "receipt", "to": "$.input.email" } }
  ]
}
```

The `$.input.*` references read from the arguments passed when the workflow is fired. Step two's `runQuery` can also reference outputs from step one — the runtime threads results between steps so each one can build on the previous one's output without you wiring it up by hand.

## The mental model: a directed action graph

Think of a workflow as a **directed graph of actions** with the runtime as the executor. You declare what should happen; the runtime decides when each step runs, what it inherits from earlier steps, and what to do if a step fails. The graph is usually a straight line, but branching is supported — a step can declare a `when` predicate that gates whether it runs based on an earlier step's output.

```mermaid theme={null}
flowchart LR
    T[Trigger] --> A1[collectPayment]
    A1 -->|success| A2[runQuery: mark paid]
    A2 --> A3[sendEmail: receipt]
    A1 -->|failure| A4[sendEmail: retry-link]
```

Two properties matter:

* **Steps share a result envelope.** Each step writes to the run's result map under its action name. Later steps reference earlier outputs by path — no glue code, no manual passing of return values.
* **The runtime owns retry and audit.** A failed step retries on the platform's schedule, not yours. Every step appends to the run's audit chain.

## Workflow vs capability vs agent

These three are the platform's three units of work and it is easy to confuse them.

<DefList>
  <Def term="Capability">
    A single typed action. Atomic — succeeds or fails as one operation. Invoked from a block prop, an API call, or as a step inside a workflow or agent.
  </Def>

  <Def term="Workflow">
    A named sequence of capabilities (and control-flow nodes) that the runtime executes as one logical unit. Has its own id, its own run history, and its own audit trail. Triggered by a page, an agent step, or a direct API call.
  </Def>

  <Def term="Agent">
    A workspace automation that runs on a schedule or trigger and dispatches reads and writes — including triggering workflows — inside its own session.
  </Def>
</DefList>

The simplest mental rule: a *capability* is "do this one thing"; a *workflow* is "do these things together"; an *agent* is "do this work without a request behind it."

## What triggers a workflow

Workflows are fired, not "called." Three things fire them.

<ArrowList>
  <ArrowItem>**A block's binding.** A Cta or Form block can name a workflow on `on_click` or `on_submit` the same way it names a single capability. The runtime fires the workflow with the block's submission as input.</ArrowItem>
  <ArrowItem>**An agent step.** Inside an agent recipe, a step can fire a named workflow instead of a single capability. The agent's session waits for the workflow run to complete before advancing to the next step.</ArrowItem>
  <ArrowItem>**The API.** A direct `POST` to the workflow's run endpoint fires it with the body as input. Useful for orchestrating from outside the platform.</ArrowItem>
</ArrowList>

## Runs

Every fire is a *run*. A run has a stable id, a status that walks the same lifecycle as agent runs (`pending`, `running`, `completed`, `failed`), and an inputs/outputs envelope you can fetch after the fact. Failed runs are not silently dropped — the run record persists with the failing step, its error, and any retries the runtime attempted.

Runs that originate from an agent are linked back to the agent run. The audit chain records the parent, so an operator inspecting an agent's run can drill into each workflow it triggered.

## Idempotency at the workflow level

A workflow that fires twice with the same input — a network retry on the trigger side, a double-submit from a page — must not charge the card twice. The runtime tracks workflow runs by `(workflow_id, input_hash)` for a 24-hour window: the second fire returns the result of the first run, marked replayed, and does not re-execute any steps. The mechanism mirrors the [API idempotency model](/api-reference/idempotency) but is keyed on the workflow's input rather than an `Idempotency-Key` header.

For runs where the input legitimately *should* re-execute — same input but a deliberate retry — pass an explicit `dedupe_key` field on the trigger to scope the dedupe window to that key.

## When to use a workflow (and when not to)

Use a workflow when:

<ArrowList>
  <ArrowItem>**Two or more actions must succeed together**, and a failure on step two needs to surface the same way a failure on step one does. A capability chain you wire by hand has to reinvent that retry/audit story.</ArrowItem>
  <ArrowItem>**The recipe is reused.** If three pages all need to "charge + mark paid + email receipt," the workflow is the single place that lives. A copy on each page drifts.</ArrowItem>
  <ArrowItem>**An agent needs to dispatch a coordinated write.** The agent's recipe stays short; the multi-step detail lives in the workflow.</ArrowItem>
</ArrowList>

Don't use a workflow when:

<ArrowList>
  <ArrowItem>**One capability is enough.** A button that opens checkout is a `collectPayment` invocation, not a workflow. Wrapping it adds an extra audit hop and an extra id to track.</ArrowItem>
  <ArrowItem>**The steps run on a different system.** Workflows execute inside the runtime; they cannot reach across workspaces or coordinate with external services in the way a queue or job system can. Use your own orchestration for cross-system work.</ArrowItem>
</ArrowList>

## Related

<Columns cols={2}>
  <BrandCard eyebrow="CONCEPT" title="Capabilities" href="/concepts/capabilities" icon="zap">
    The atomic actions workflow steps are built from.
  </BrandCard>

  <BrandCard eyebrow="CONCEPT" title="Agents" href="/concepts/agents" icon="bot">
    The automations that fire workflows on a schedule or event.
  </BrandCard>

  <BrandCard eyebrow="CONCEPT" title="Approval gates" href="/concepts/approvals" icon="user-check">
    How a workflow step can pause for human sign-off before dispatching.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="Idempotency" href="/api-reference/idempotency" icon="rotate">
    The cross-API idempotency model that workflow dedupe mirrors.
  </BrandCard>
</Columns>
