Skip to main content

workflow

Attribute Macro workflow 

Source
#[workflow]
Expand description

Marks a function as a durable workflow.

Workflows are multi-step processes that survive restarts and handle failures with compensation. Each workflow has a stable logical name, an explicit user-facing version, and a derived signature that acts as the hard runtime safety gate for resumption.

§Versioning and step-name stability

Step names (the string literals passed to ctx.step()) and wait keys (the string literals passed to ctx.wait_for_event()) are part of the workflow’s persisted contract. The macro hashes them together with the version string, timeout, and input/output type names into a signature that is stored with every new run.

Renaming a step or wait key under the same version is a breaking change. Any in-flight run that tries to resume after such a rename will be blocked with WorkflowStatus::BlockedSignatureMismatch because the stored signature no longer matches the binary’s signature. Use cargo expand to inspect the forge:contract doc comment on the generated struct — it lists every key contributing to the signature.

When you need to rename a step (or add/remove steps, change event contracts, or alter the timeout), create a new version instead:

  1. Annotate the old function with deprecated — the runtime keeps it alive for draining.
  2. Write a new function with a new version string containing your changes.
  3. Remove the old function once all its in-flight runs have completed.

The runtime derives a signature from step keys, wait keys, timeout, and type shapes. If you change the persisted contract under the same version, registration will fail.

§Authentication

By default, workflows require an authenticated user to start. Override with:

  • public - Can be started without authentication
  • require_role("admin") - Requires specific role to start

§Attributes

  • name = "logical_name" - Stable workflow name (defaults to function name)
  • version = "2026-05" - User-facing version id (dates, semver, or labels)
  • active - This is the active version; new runs start here (default if neither set)
  • deprecated - Kept for draining old runs; no new runs will start on this version
  • timeout = "24h" - Maximum execution time. Also becomes the default outbound HTTP timeout for ctx.http() when explicitly set

§Example

// Old version kept alive for draining incomplete runs
#[forge::workflow(name = "user_onboarding", version = "2026-03", deprecated)]
pub async fn user_onboarding_v1(ctx: &WorkflowContext, input: Input) -> Result<Output> {
    let user = ctx.step("create_user", || async { /* ... */ }).await?;
    ctx.step("send_welcome", || async { /* ... */ }).await;
    Ok(Output { user })
}

// New active version with an additional step
#[forge::workflow(name = "user_onboarding", version = "2026-05", active)]
pub async fn user_onboarding_v2(ctx: &WorkflowContext, input: Input) -> Result<Output> {
    let user = ctx.step("create_user", || async { /* ... */ }).await?;
    ctx.step("send_welcome", || async { /* ... */ }).await;
    ctx.step("sync_crm", || async { /* ... */ }).await;
    Ok(Output { user })
}