workflow
Module workflow
ballerina/workflow Ballerina library
Ballerina Workflow Library
The ballerina/workflow library provides durable, fault-tolerant workflow orchestration for Ballerina applications. It lets you define long-running business processes — spanning minutes, hours, or days — that automatically recover from crashes and process restarts without losing progress.
Overview
Workflows and activities are ordinary Ballerina functions:
@workflow:Workflow— A durable function that orchestrates a business process. The runtime checkpoints every step and replays recorded history to recover from failures.@workflow:Activity— A function that performs a single non-deterministic operation (API call, database query, email send). Once an activity completes, its result is recorded and never re-executed during replay.
import ballerina/workflow; type OrderRequest record {| string orderId; string item; |}; type OrderResult record {| string orderId; string status; |}; @workflow:Activity function checkInventory(string item) returns boolean|error { // Call external inventory API return true; } @workflow:Workflow function processOrder(workflow:Context ctx, OrderRequest request) returns OrderResult|error { boolean inStock = check ctx->callActivity(checkInventory, {"item": request.item}); if !inStock { return {orderId: request.orderId, status: "OUT_OF_STOCK"}; } return {orderId: request.orderId, status: "COMPLETED"}; }
Authoring guidelines
- Workflow functions must contain only orchestration logic (control flow and waiting for data). All business logic and non-deterministic operations - database calls, external API calls, I/O - belong in activity functions.
- Activities must be invoked through
ctx->callActivity(...)from within a workflow function. Calling an activity function directly is a compile-time error. - When calling an activity, pass its arguments as a record whose keys exactly match the activity function's parameter names - for example,
ctx->callActivity(checkInventory, {"item": request.item})forfunction checkInventory(string item). - When using the Ballerina Integrator tooling, place all
@workflow:Workflowand@workflow:Activityfunctions in the project'sfunctions.balfile. - Store the workflow ID returned by
workflow:run()so that laterworkflow:sendData()calls can be routed to the correct running instance.
Starting a Workflow
Use workflow:run() to start a workflow instance from any entry point — HTTP service, scheduled job, message consumer, or main:
string workflowId = check workflow:run(processOrder, {orderId: "ORD-001", item: "laptop"});
Receiving External Data
A workflow can pause and wait for external input — approvals, payment confirmations, user decisions — using future-based event records. Send data to a running workflow with workflow:sendData():
// In the workflow — wait for a human decision ApprovalDecision decision = check wait events.approval; // From outside — deliver the decision check workflow:sendData(processOrder, workflowId, "approval", {approverId: "mgr-1", approved: true});
Multi-Future Waits with ctx->await
Use ctx->await to wait for multiple futures at once, with optional quorum and timeout:
| Pattern | Example |
|---|---|
| Wait for all | ctx->await([f1, f2]) |
| Wait for any (first wins) | ctx->await([f1, f2], 1) |
| Quorum (N of M) | ctx->await([f1, f2, f3], 2) |
| With deadline | ctx->await([f1, f2], timeout = {hours: 48}) |
Error Handling
Activity errors are returned as plain Ballerina values. The workflow decides what happens next:
string|error result = ctx->callActivity(chargeCard, {"amount": input.amount}); if result is error { // retry with a different card, fall back, or compensate }
Enable automatic retries for transient failures:
string result = check ctx->callActivity(chargeCard, {"amount": input.amount}, retryPolicy = {maxRetries: 3});
Configuration
Add a Config.toml to your project. For local development with no server:
[ballerina.workflow] mode = "IN_MEMORY"
For production, connect to a Temporal server:
[ballerina.workflow] mode = "SELF_HOSTED" url = "temporal.mycompany.com:7233" namespace = "default" taskQueue = "my-task-queue"
See Configure the Module for every mode and field.
Documentation
| Guide | Description |
|---|---|
| Get Started | Write and run your first workflow |
| Key Concepts | Workflows, activities, external data, and timers |
| Write Workflow Functions | Signatures, determinism rules, and durable sleep |
| Write Activity Functions | Activity patterns and retry options |
| Handle Data | Waiting for external input and sending data |
| Handle Errors | Propagation, retry, fallback, and compensation |
| Configure the Module | Connection settings, TLS, and namespaces |
Examples
| Example | Description |
|---|---|
| Get Started | First workflow |
| Order Processing | HTTP-triggered workflow with result polling |
| Human in the Loop | Pause for a human approval |
| Wait for All | Dual authorization — both teams must approve |
| Alternative Wait | First responder wins (approval ladder) |
| Forward Recovery | Pause for corrected data and retry |
| Error Propagation | Fail the workflow on a critical error |
| Error Fallback | Fall back to a secondary activity |
| Error Compensation | Saga: undo committed steps on failure |
Functions
completeHumanTask
function completeHumanTask(string taskWorkflowId, anydata result, [string, string...]? callerRoles, string? userId) returns error?Completes a pending human task by sending the result back to the waiting workflow.
The taskWorkflowId is the child workflow ID of the task, available via the
inbox/task-listing API. It is an opaque UUID — the task's kind and name travel in the
execution's memo and type, not in the ID.
check workflow:completeHumanTask(taskWorkflowId, {approved: true, comment: "LGTM"});
If callerRoles is provided the function fetches the userRoles stored on the task
and returns an error when none of the caller's roles appear in that list.
When omitted the role check is skipped; enforcement is then the caller's responsibility.
Parameters
- taskWorkflowId string - Temporal workflow ID of the human task child workflow
- result anydata - The value to return to the workflow (must be compatible with the declared
T)
- callerRoles [string, string...]? (default ()) - Roles held by the caller; validated against the task's configured
userRoles
- userId string? (default ()) - The user ID of the person completing the task (used for auditing)
Return Type
- error? - An error if the task cannot be found, is already completed, or the caller is unauthorized
getPendingAgentEvents
function getPendingAgentEvents(string agentId) returns PendingAgentEvent[]|errorLists the data events a running durable agent has accepted but not yet
answered. Use after a crash or restart to rediscover in-flight event turns
for a session and fetch their answers via DurableAgent.getDataResult /
waitForDataResult — nothing is lost while the agent works, however long
the turn takes.
workflow:PendingAgentEvent[] pending = check workflow:getPendingAgentEvents(agentId); foreach var pendingEvent in pending { string answer = check agent.waitForDataResult(agentId, pendingEvent.token); }
Parameters
- agentId string - Target agent (workflow) ID
Return Type
- PendingAgentEvent[]|error - The in-flight event turns (empty when the agent is idle), or an error
getWorkflowResult
Waits for a workflow to complete and returns its result.
anydata raw = check workflow:getWorkflowResult(workflowId);
Parameters
- workflowId string - The workflow ID
- timeoutSeconds int (default 30) - Maximum wait time in seconds
Return Type
- anydata|error - Result of the workflow as anydata, or an error
run
Starts a new workflow instance and returns its unique ID.
string workflowId = check workflow:run(orderProcess, input = {"orderId": "ORD-123"});
Parameters
- processFunction
function() ()- The workflow function (must have@Workflow)
- input anydata (default ()) - Optional input data for the workflow. Must match the workflow
function's declared input parameter type (any
anydatasubtype)
sendData
function sendData( function() () workflow, string workflowId, string dataName, anydata data) returns error?Sends data to a running workflow's events record.
check workflow:sendData(orderProcess, workflowId, "approval", {approved: true});
Parameters
- workflow
function() ()- The workflow function (must have@Workflow)
- workflowId string - Target workflow ID (from
run)
- dataName string - Field name in the workflow's events record
- data anydata - The data payload
Return Type
- error? - An error if sending fails
Classes
workflow: DurableAgent
A durable AI agent declared as an object. Must be assigned to a module-level
final variable (compiler-enforced) — the compiler plugin reads the constructor
config to generate the Temporal registration at module init, and the module-level
variable name becomes the agent's stable identity.
The object itself is a declaration anchor: capability registration is generated at compile time from the constructor config, and the driver methods are lowered by the compiler plugin to the context-appropriate runtime primitive.
final workflow:DurableAgent orderAgent = check new ({ systemPrompt: {role: "Order assistant", instructions: "Help the user."}, model: wso2Model, activities: [checkInventory, reserveStock], events: {chat: {request: string, response: string, cardinality: workflow:MULTI_EVENT}} });
Constructor
Declares the agent. Capabilities are fixed here and registered with the workflow runtime at module init by the compiler plugin.
init (*DurableAgentConfig config)- config *DurableAgentConfig - The agent's complete configuration
bindAgentName
function bindAgentName(string agentName)Binds the agent's stable identity — its module-level variable name — to this object. Called by the compiler-plugin-generated module-init code; not part of the public API surface. The first binding wins; later calls are ignored.
Parameters
- agentName string - The agent's module-level variable name
run
Starts the agent durably and returns the new instance ID — always the ID,
never the result (a durable agent may suspend for days on a human task, so
no caller thread is blocked). Outside a workflow this is a top-level start;
inside a @workflow:Workflow the agent runs as a Temporal child workflow.
Parameters
- query string - The user turn appended to the agent's system prompt
- input json (default ()) - Optional structured JSON payload for the run; must match the
agent's declared
inputType
sendData
Sends an event to a running instance on a declared channel and returns a correlation token for reading that turn's response.
The instance must be one of this agent's: the compiler plugin checks the
channel and its payload against this declaration, and the runtime checks them
against the target instance's. The two agree exactly when the instance came
from this agent's run.
Parameters
- instanceId string - An instance ID this agent's
runreturned
- eventName string - A channel declared in the agent's
events
- data anydata - The payload; validated against the channel's declared
requesttype
getResult
Returns the final result of an instance if it has finished, without waiting.
While the instance is still working (e.g. suspended on a human task) a
workflow:AgentBusyError is returned — check back later, or use waitForResult.
Parameters
- instanceId string - The agent instance ID returned by
run
- T typedesc<anydata> (default <>) - Expected result type (inferred from context)
Return Type
- T|error - The result as
T, aworkflow:AgentBusyErrorwhile in progress, or an error
getDataResult
Returns the response for a specific sendData turn if it is ready, without
waiting. While the turn is unanswered a workflow:AgentBusyError is returned.
Parameters
- instanceId string - The agent instance ID returned by
run
- token string - The correlation token returned by
sendData
- T typedesc<anydata> (default <>) - Expected response type (inferred from context)
Return Type
- T|error - The turn's response as
T, aworkflow:AgentBusyErrorwhile unanswered, or an error
waitForResult
Waits until the instance finishes and returns its result. Inside a workflow this durably suspends the caller (no thread held); from a service it blocks but is resumable — if the caller crashes, calling again after restart resumes the wait, because the result lives in history.
Parameters
- instanceId string - The agent instance ID returned by
run
- T typedesc<anydata> (default <>) - Expected result type (inferred from context)
Return Type
- T|error - The result as
T, or an error
waitForDataResult
Waits for a specific sendData turn's response (same durability guarantees
as waitForResult).
Parameters
- instanceId string - The agent instance ID returned by
run
- token string - The correlation token returned by
sendData
- T typedesc<anydata> (default <>) - Expected response type (inferred from context)
Return Type
- T|error - The turn's response as
T, or an error
Clients
workflow: Context
Workflow execution context providing activity execution, durable sleep, deterministic time, and multi-future await APIs.
Constructor
Creates a workflow execution context wrapping the native context handle.
This constructor is called by the workflow runtime; do not instantiate Context directly.
init (handle nativeContext)- nativeContext handle - Native context handle from the workflow engine
callActivity
function callActivity( function() () activityFunction, map<anydata|object {}> args, typedesc<anydata> T, string? stepId, *CallActivityOptions options) returns T|errorExecutes an activity function. A completed activity is never re-executed on
workflow replay — its recorded result is reused, even across process crashes
and restarts. A failed attempt may run again, however: AutoRetry re-executes
the activity automatically and a ReviewTaskDefinition lets a human rerun it, so make the
activity's side effects idempotent (or deduplicate them) when retries are enabled.
PaymentResult result = check ctx->callActivity(processPayment, args = {"orderId": orderId});
The result type T is inferred from what the result is assigned to, so the call must
always be bound — including when the activity returns nothing but error?. Bind such a
call to () _; a bare statement or a plain _ gives the compiler nothing to infer from:
() _ = check ctx->callActivity(reserveStock, args = {"orderId": orderId});
Parameters
- activityFunction
function() ()- The activity function (must have@Activity)
- args map<anydata|object {}> (default {}) - Arguments to pass to the activity. Values are normally
anydata. Module-levelfinalclient objectvariables may also be passed for activity parameters whose declared type is a client object; the compiler plugin validates the call site and substitutes a"connection:<name>"marker for transport across the workflow execution boundary.
- T typedesc<anydata> (default <>) - Expected return type (inferred from context)
- stepId string? (default ()) - Identity of this step within the workflow, reported by every execution of it and
matching a node of the descriptor's graph — so a run can be traced back to the
exact call that ran, the one in the
ifarm rather than theelse. Name it (stepId = "charge-card") for a step you want to follow: a chosen id survives edits that shift the generated<activity>#<ordinal>. Must be a constant string; an id another step already has is suffixed, with a warning.
- options *CallActivityOptions - How the invocation behaves — today
retryPolicy(NoAutomaticRetrydefault,AutoRetrybackoff, or aReviewTaskDefinition: on failure a review task lets a person rerun or fail it) — as an included record, so each travels as a named argument and a future behaviour option is a new record field rather than a new parameter
Return Type
- T|error - The activity result as
T, or an error
await
function await(future<anydata>[] futures, Unsigned32 minCount, Duration? timeout, typedesc<anydata|error|(anydata|error)[]> T) returns TWaits for at least minCount data futures to complete. Results are a positional tuple
aligned to input order. Use nullable types (T?) for partial waits.
The result can be captured in several ways:
// Wait for all (tuple binding pattern) [Approval, Payment] [approval, payment] = check ctx->await([events.approval, events.payment]); // Capture the whole result, including a possible timeout, without `check` [Approval, Payment]|error result = ctx->await([events.approval, events.payment]); if result is error { /* handle timeout */ } // Handle each position independently (a slot is a value or an error) [Approval|error, Payment|error] [a, p] = check ctx->await([events.approval, events.payment]); // Wait for any (1 of 2) — use nilable members for partial waits [Approval?, Payment?] result = check ctx->await([events.approval, events.payment], minCount = 1);
Parameters
- futures future<anydata>[] - Data futures from the workflow's events record
- minCount Unsigned32 (default <int:Unsigned32>futures.length()) - Minimum completions required (default: all)
- timeout Duration? (default ()) - Maximum wait duration; returns an error on timeout
Return Type
- T - Positional tuple of values (
nilfor an incomplete position), or an error
awaitHumanTask
function awaitHumanTask(string taskName, map<json> taskInput, typedesc<anydata> T, string? stepId, *HumanTaskDefinition definition) returns T|HumanTaskErrorCreates a human task and blocks until a human completes it or the optional timeout elapses.
Internally, the task is modelled as a durable Temporal child workflow whose type is taskName,
so the task survives worker restarts. The task name is registered when the worker starts,
from the workflow descriptor the compiler plugin generates at build time.
do { ApprovalDecision d = check ctx->awaitHumanTask("approveExpense", {"amount": 1200, "currency": "USD"}, userRoles = "FINANCE_APPROVER", title = "Approve order", timeout = {hours: 24} ); return d; } on fail workflow:HumanTaskError e { if e is workflow:HumanTaskTimeoutError { () _ = check ctx->callActivity(notifyEscalation, args = {"taskName": e.detail().taskName}); } return e; }
Parameters
- taskName string - Identifies the task type; used as the Temporal workflow type and child workflow ID
- taskInput map<json> - Read-only object shown beside the form. Pass
{}when there is nothing to show; it is checked against the definition'staskInputType
- T typedesc<anydata> (default <>) - Expected result type; drives form schema generation and runtime validation
- stepId string? (default ()) - Identity of this step within the workflow, as for
callActivity: name it to follow this task across edits, or omit it for a generated<taskName>#<ordinal>
- definition *HumanTaskDefinition - The task's
HumanTaskDefinition, as an included record: each field travels as a named argument (userRoles = "MANAGER")
Return Type
- T|HumanTaskError - The typed value submitted by the human, or a
HumanTaskError: aHumanTaskTimeoutErrorif the deadline passed, aHumanTaskRejectedErrorif someone rejected the task (carrying their reason and details), or aHumanTaskFailedErrorif the task could not produce a result
runChildWorkflow
function runChildWorkflow( function() () childWorkflow, anydata input, string? stepId) returns string|errorStarts a child workflow and returns its instance ID without waiting for the result. The child is a true Temporal child workflow: its lifecycle is tied to this workflow, so when this workflow closes, in-flight children are cancelled with it.
Use getChildWorkflowResult (non-blocking) or waitForChildWorkflow (durable wait)
to read the child's result later — this enables fan-out/fan-in orchestration:
string kycId = check ctx->runChildWorkflow(kycWorkflow, input = customer); // fan out string scoreId = check ctx->runChildWorkflow(scoreWorkflow, input = customer); Kyc kyc = check ctx->waitForChildWorkflow(kycId); // gather Score score = check ctx->waitForChildWorkflow(scoreId);
Parameters
- childWorkflow
function() ()- The child workflow function (must have@Workflow)
- input anydata (default ()) - Optional input for the child workflow. Must match the child workflow
function's declared input parameter type (any
anydatasubtype)
- stepId string? (default ()) - Identity of this step within the workflow, as for
callActivity
getChildWorkflowResult
Returns the result of a child workflow started with runChildWorkflow if it has
already completed, without waiting. While the child is still running (e.g. suspended
on a human task) a workflow:WorkflowBusyError is returned — check back later, or
switch to the blocking waitForChildWorkflow form.
Kyc|error result = ctx->getChildWorkflowResult(kycId); if result is workflow:WorkflowBusyError { /* still running — do other work */ }
Parameters
- childWorkflowId string - The child workflow instance ID returned by
runChildWorkflow
- T typedesc<anydata> (default <>) - Expected result type (inferred from context)
Return Type
- T|error - The child's result as
T, aworkflow:WorkflowBusyErrorwhile the child is still running, or an error if the child failed
waitForChildWorkflow
Waits durably until a child workflow started with runChildWorkflow completes and
returns its result. The wait is a durable suspend — no thread is held, and the wait
survives worker crashes and restarts (on replay the result is served from history).
Parameters
- childWorkflowId string - The child workflow instance ID returned by
runChildWorkflow
- T typedesc<anydata> (default <>) - Expected result type (inferred from context)
Return Type
- T|error - The child's result as
T, or an error if the child failed
callWorkflow
function callWorkflow( function() () childWorkflow, anydata input, typedesc<anydata> T, string? stepId) returns T|errorStarts a child workflow and durably waits for its result — runChildWorkflow
followed by waitForChildWorkflow fused into one call. "Blocking" here is a durable
suspend, not a held thread, so this is safe for long-running children.
Receipt receipt = check ctx->callWorkflow(billingWorkflow, input = order);
Parameters
- childWorkflow
function() ()- The child workflow function (must have@Workflow)
- input anydata (default ()) - Optional input for the child workflow. Must match the child workflow
function's declared input parameter type (any
anydatasubtype)
- T typedesc<anydata> (default <>) - Expected result type (inferred from context)
- stepId string? (default ()) -
Return Type
- T|error - The child's result as
T, or an error if the child failed
sendDataToChildWorkflow
function sendDataToChildWorkflow(string childWorkflowId, string dataName, anydata data) returns error?Sends data to a running workflow instance's events record from inside a workflow.
This is the in-workflow counterpart of workflow:sendData and is typically used to
signal a child workflow started with runChildWorkflow, but accepts any workflow
instance ID.
check ctx->sendDataToChildWorkflow(childId, "approval", {approved: true});
Parameters
- childWorkflowId string - Target workflow instance ID (usually from
runChildWorkflow)
- dataName string - Field name in the target workflow's events record
- data anydata - The data payload
Return Type
- error? - An error if sending fails
sleep
Durable sleep that survives process crashes and restarts. Do not use runtime:sleep() in workflows.
check ctx.sleep({seconds: 30});
Parameters
- duration Duration - The duration to sleep
- stepId string? (default ()) - Identity of this step within the workflow, as for
callActivity: name it to follow this sleep across edits, or omit it for a generatedsleep#<ordinal>. Recorded as the timer's summary, so an instance diagram can tell two sleeps apart
Return Type
- error? - An error if the sleep fails, otherwise nil
currentTime
function currentTime() returns UtcReturns the deterministic workflow time. Use instead of time:utcNow() inside workflows.
time:Utc now = ctx.currentTime();
Return Type
- Utc - The current workflow time as
time:Utc
isReplaying
function isReplaying() returns booleanChecks whether the workflow is recovering from a failure (re-executing recorded history).
Return Type
- boolean -
trueif recovering,falseon first execution
getWorkflowId
Get the unique workflow ID.
getWorkflowType
Get the workflow type name.
Constants
workflow: NoAutomaticRetry
No automatic retry by the engine. Errors from the activity are returned
directly to the caller. This is the default behaviour when no retryPolicy
is specified. Note that an AI agent may still decide to call the activity
again from its own reasoning — this policy only disables engine-driven
retries.
Enums
workflow: EventCardinality
How a declared event channel consumes its requests.
Members
workflow: Mode
Deployment mode for the workflow runtime.
Members
temporal server start-dev)Annotations
workflow: Activity
Marks a function as a workflow activity.
@workflow:Activity function sendEmail(EmailRequest req) returns EmailResponse|error { }
workflow: Workflow
Marks a function as a workflow.
@workflow:Workflow function orderProcess(Order input) returns OrderResult|error { }
Configurables
workflow: mode
Deployment mode for the workflow runtime.
LOCAL— Connects to a locally running server (e.g.,temporal server start-dev). This is the default mode.CLOUD— Managed cloud deployment. Requiresurl,namespace, and authentication (authApiKeyor mTLS certificate/key pair).SELF_HOSTED— Self-hosted server. Requiresurl; authentication is optional.IN_MEMORY— Lightweight in-memory engine with no external server. Workflows are not persisted and will be lost on restart. All other connection/scheduler fields are ignored in this mode.
workflow: url
Server URL for the workflow runtime.
For LOCAL mode, defaults to "localhost:7233".
For CLOUD mode, use the cloud endpoint (e.g., "
workflow: namespace
Workflow namespace.
For LOCAL and SELF_HOSTED modes, defaults to "default".
For CLOUD mode, use your cloud namespace (e.g., "
workflow: authApiKey
API key for bearer-token authentication. Required for CLOUD mode (unless mTLS is configured). Optional for SELF_HOSTED mode. Ignored in LOCAL and IN_MEMORY modes.
workflow: authMtlsCert
Path to the mTLS client certificate file (PEM format).
Used for CLOUD or SELF_HOSTED modes with mutual TLS authentication.
Must be provided together with authMtlsKey.
Ignored in LOCAL and IN_MEMORY modes.
workflow: authMtlsKey
Path to the mTLS client private key file (PEM format).
Used together with authMtlsCert for mutual TLS authentication.
Ignored in LOCAL and IN_MEMORY modes.
workflow: authCaCert
Path to the CA certificate file (PEM format) used to verify the server's TLS certificate. Set this when the Temporal server uses a certificate from a private or self-signed CA that is not in the JVM's default trust store. Applicable to CLOUD mode (API key over TLS) and SELF_HOSTED mode (TLS or mTLS). Ignored in LOCAL and IN_MEMORY modes.
workflow: taskQueue
Task queue name for workflow and activity polling. Each workflow program should use a unique task queue to avoid conflicts. Ignored in IN_MEMORY mode.
workflow: maxConcurrentWorkflows
Maximum number of concurrent workflow task executions. Controls how many workflow tasks the workflow scheduler processes in parallel. Must be a positive integer. Ignored in IN_MEMORY mode.
workflow: maxConcurrentActivities
Maximum number of concurrent activity executions. Controls how many activities the workflow scheduler processes in parallel. Must be a positive integer. Ignored in IN_MEMORY mode.
workflow: activityRetryInitialInterval
Initial delay (in seconds) before the first activity retry attempt.
Part of the default retry policy applied to all activities unless
overridden per-call via callActivity's retryPolicy (e.g., an AutoRetry
record setting maxRetries or retryDelay).
Must be a positive integer.
workflow: activityRetryBackoffCoefficient
Backoff multiplier applied to the activity retry interval after each attempt. For example, with an initial interval of 1s and coefficient of 2.0, retries occur at 1s, 2s, 4s, 8s, etc. Must be >= 1.0.
workflow: activityRetryMaximumInterval
Maximum delay (in seconds) between activity retries. Caps the exponential backoff to prevent excessively long waits. 0 means unset/no upper limit and is allowed. When explicitly set to a non-zero value it must be a positive integer (> 0).
workflow: activityRetryMaximumAttempts
Maximum number of activity retry attempts.
- 1 means no retries (execute once only, the default).
- 0 means unlimited retries.
- Any positive value sets the retry cap.
Records
workflow: ActivityDecl
An activity capability of a durable agent, with optional gating and retry config.
For the no-config case pass the bare @workflow:Activity function instead.
Fields
- activity
function() ()- The@workflow:Activityfunction
- name? string - Tool name advertised to the model; defaults to the function name
- description? string - Tool description advertised to the model; defaults to the function's doc comment
- bindings? map<anydata|object {}> - Fixed arguments partially applied to the activity (e.g. a
connection), hidden from the model: only the remaining data parameters appear in the tool's schema. Client objects are bound by referencing their module-levelfinalvariable
- requiresApproval boolean(default false) - When
true, aPRE_RUNreview activity gates every call
- retryPolicy AutoRetry|ReviewTaskDefinition|NoAutomaticRetry(default NoAutomaticRetry) - Retry behaviour on failure, as for
ctx->callActivity
workflow: AutoRetry
Automatic retry configuration. When the activity fails, it is automatically retried according to the configured backoff policy.
Fields
- maxRetries int(default 3) - Maximum retry attempts (default: 3)
- retryDelay decimal(default 1.0) - Initial delay in seconds before the first retry (default: 1.0)
- retryBackoff decimal(default 2.0) - Multiplier applied to delay after each retry (default: 2.0)
- maxRetryDelay? decimal - Cap on the delay between retries, in seconds
workflow: CallActivityOptions
How a Context.callActivity invocation behaves, passed as an included record
parameter. Deliberately an OPEN record so a future behaviour
option — an approval gate, a heartbeat policy, a per-call timeout — is a new field
here rather than a new parameter, and tooling derives its forms from this record.
The step identity (stepId) is NOT here: it is workflow mechanics, not invocation
behaviour, and stays a function parameter on every context operation.
Fields
- retryPolicy AutoRetry|ReviewTaskDefinition|NoAutomaticRetry(default NoAutomaticRetry) - Failure behaviour:
NoAutomaticRetry(fail the workflow),AutoRetry(durable backoff retries), or aReviewTaskDefinition(raise a review on failure so a person decides to rerun, rerun with edited input, or fail)
workflow: DurableAgentConfig
The complete, self-declarative configuration of a durable agent. Capability kinds are separate fields so each renders as its own edge type in the diagram.
Fields
- systemPrompt SystemPrompt - The agent's identity: role + instructions (the per-run query
is appended as the user turn by
run)
- model ModelProvider - The model provider used for the agent's LLM calls
- inputType typedesc<json>?(default json) - The type of the structured JSON payload the agent's
run(and the management API start) accepts alongside the query.json(the default) accepts any JSON payload unvalidated; a narrower type — typically a record — declares the payload's shape and is validated against it at the call site and at run time;()declares a no-payload agent whose only input is the query text
- resultType typedesc<anydata>?(default ()) - When declared, the agent produces a typed final result: as the
reasoning loop concludes, one more durable model call converts the
conversation outcome into this type, and
waitForResult/getResultreturn it.()keeps the final text response
- activities (ActivityDecl|
function() ())[](default []) -@workflow:Activityfunctions, bare or asActivityDeclwhen gating/roles/bindings are needed
- tools (ToolDecl|ToolConfig|BaseToolKit|
function() ())[](default []) - AI tools:@ai:AgentToolfunctions,ai:ToolConfigs, toolkits, orToolDeclwhen gating is needed
- events map<EventConfig>(default {}) - Event channels keyed by channel name (
{chat: {request: string, MISSING[]response: string}}) MISSING[]
- humanTasks map<HumanTaskDefinition>(default {}) - Human task capabilities keyed by task name (
{signoff: {userRoles: MISSING[]"manager"}}) MISSING[]Capability names share one namespace across activities, tools, events, human tasks, and peers: a name claimed twice is rejected when the agent registers, so the program fails at startup
- peers PeerDecl[](default []) - Peer durable agents advertised as delegable tools
- maxIter int(default 16) - Hard cap on reasoning iterations per turn
- eventTimeout Duration?(default ()) - Maximum wait per event-channel wait (each chat turn, each event).
Omit to wait indefinitely — a conversation stays open as long as it
takes, and
maxEventWaitsremains the runaway backstop. On a timeout the model is told so it can wrap up gracefully
workflow: Duration
A time duration, structurally identical to time:Duration. Declared in this module so
timeout fields render as first-class workflow forms without a cross-module type reference;
time:Duration values remain assignable.
Fields
- years int(default 0) - The duration in years
- months int(default 0) - The duration in months
- weeks int(default 0) - The duration in weeks
- days int(default 0) - The duration in days
- hours int(default 0) - The duration in hours
- minutes int(default 0) - The duration in minutes
- seconds decimal(default 0.0) - The duration in seconds
workflow: EventConfig
One event channel of a durable agent, declared in the mapping form of events
where the mapping key is the channel name — constant by construction, so the
name needs no separate validation. request/response capture both sides'
types and the channel's duplexity: a response type declares a duplex
(request-response) channel whose turn answers are read with
getDataResult/waitForDataResult; a nil response declares a one-way
channel — data flows in, no result is read back.
Fields
- request typedesc<anydata> - Type of the payload sent to the agent on this channel;
sendDatavalidates each payload against it
- response typedesc<anydata>?(default ()) - Type of the agent's reply for this channel;
()for one-way channels
- cardinality EventCardinality(default MULTI_EVENT) - Business cardinality of the channel: re-armed per turn
(
MULTI_EVENT, the default) or consumed once (SINGLE_EVENT)
workflow: HumanTaskDefinition
A human task: who may answer it and how it reads, plus the shapes it takes and returns.
The input supplied to the task is checked against taskInputType before the task is
created, whether a workflow passes it to awaitHumanTask or an agent supplies it.
Fields
- Fields Included from *ReviewTaskDefinition
- taskInputType typedesc<map<json>>(default JsonObject) - Shape of the input shown to the decider
- resultType typedesc<anydata>(default anydata) - Shape of the answer. A workflow states this as
awaitHumanTask'sTinstead; an agent declares it here
workflow: HumanTaskRejectedDetail
Detail fields carried by a HumanTaskRejectedError.
Fields
- taskName string - The
taskNamevalue passed toawaitHumanTask
- taskWorkflowId string - Temporal child workflow ID of the rejected task instance
- reason string - The reason submitted with the rejection
- details map<json>?(default ()) - Structured data submitted with the rejection, or
()if none was given
- rejectedBy string?(default ()) - The user who rejected the task, when the rejection recorded one
workflow: HumanTaskTimeoutDetail
Detail fields carried by a HumanTaskTimeoutError.
Fields
- taskName string - The
taskNamevalue passed toawaitHumanTask
- taskWorkflowId string - Temporal child workflow ID of the timed-out task instance
- timedOutAfter string - Configured deadline as an ISO-8601 duration (e.g.
"PT24H")
- timedOutAt string - ISO-8601 timestamp at which the timeout was recorded
workflow: PeerDecl
A peer durable agent advertised to this agent's model as a delegable tool. The framework runs the peer as a Temporal child workflow.
Fields
- agent DurableAgent - The peer
workflow:DurableAgent
- name string - Tool name advertised to the model (unique across all capabilities)
- description? string - What the peer does, for the model
- 'wait boolean(default true) - When
true(default) the delegation blocks durably for the peer's result; whenfalsethe peer runs async and replies oncallbackChannel
- callbackChannel? string - Declared event channel that receives the async peer's reply;
required when
wait = false
- requiresApproval boolean(default false) - When
true, aPRE_RUNreview activity gates the delegation
workflow: PendingAgentEvent
A data-event turn a durable agent has accepted but not yet answered. Returned
by getPendingAgentEvents so callers can rediscover in-flight event turns
after a crash and fetch their answers via DurableAgent.getDataResult /
waitForDataResult.
Fields
- token string - The turn's correlation token (as returned by
DurableAgent.sendData)
- eventName string - The event channel the turn was sent on
workflow: ReviewTaskDefinition
Who may answer a human decision, and how it reads. Shared by a workflow's human task, a durable agent's task capability, and the review a gated activity raises.
This is a review's whole definition. A human task adds the shapes it is checked against —
see HumanTaskDefinition.
Fields
- title string?(default ()) - Short summary shown in the inbox. Defaults to the task name
- description string?(default ()) - Additional context shown with the form or decision
- timeout Duration?(default ()) - Maximum time to wait. Omit to wait indefinitely
workflow: ToolDecl
An AI tool capability of a durable agent, with optional gating config. For the
no-config case pass the ai:ToolConfig/ai:BaseToolKit/@ai:AgentTool function
directly.
Fields
- tool BaseToolKit|ToolConfig|FunctionTool - The tool: an
@ai:AgentToolfunction, anai:ToolConfig, or a toolkit
- requiresApproval boolean(default false) - When
true, aPRE_RUNreview activity gates every call
Errors
workflow: AgentBusyError
Returned by the non-blocking getResult/getDataResult reads when the agent
instance (or the specific turn) is still in progress — e.g. suspended on a human
task. Check back later, or use the blocking waitForResult/waitForDataResult
forms, which durably wait and are resumable across crashes.
workflow: HumanTaskError
Every failure awaitHumanTask can report: nobody acted in time
(HumanTaskTimeoutError), someone rejected the task (HumanTaskRejectedError), or the
task could not produce a result at all (HumanTaskFailedError).
workflow: HumanTaskFailedError
Returned by awaitHumanTask when the task neither completed nor closed with a reason
it can report — the task workflow failed, was terminated by an administrator, or the
submitted value did not match the expected result type.
workflow: HumanTaskRejectedError
Returned by awaitHumanTask when the task is rejected instead of completed — the
fail management operation, which records a reason rather than a result. The reason
and any structured details submitted with the rejection are on the error detail, so a
workflow can compensate on what the rejecting user said:
Approval|workflow:HumanTaskError approval = ctx->awaitHumanTask("approve", userRoles = "FINANCE"); if approval is workflow:HumanTaskRejectedError { () _ = check ctx->callActivity(notifyRejected, args = {"reason": approval.detail().reason}); }
workflow: HumanTaskTimeoutError
Returned by awaitHumanTask when no human acts within the configured deadline.
Catch the whole family with on fail workflow:HumanTaskError e and narrow with
if e is workflow:HumanTaskTimeoutError to run compensation logic for a timeout.
workflow: WorkflowBusyError
Returned by the non-blocking ctx->getChildWorkflowResult read when the child
workflow is still running (e.g. suspended on a human task). Check back later, or
use the blocking ctx->waitForChildWorkflow form, which durably suspends until
the child completes.
Map types
workflow: JsonObject
JsonObject
Any JSON object.
Import
import ballerina/workflow;Metadata
Released date: 7 days ago
Version: 0.9.1
License: Apache-2.0
Compatibility
Platform: java21
Ballerina version: 2201.13.4
GraalVM compatible: No
Pull count
Total: 10919
Current verison: 45
Weekly downloads
Keywords
workflow
Contributors