diff --git a/en/docs/workflows/develop/durable-agentic-workflow.md b/en/docs/workflows/develop/durable-agentic-workflow.md new file mode 100644 index 0000000000..f7de40938e --- /dev/null +++ b/en/docs/workflows/develop/durable-agentic-workflow.md @@ -0,0 +1,116 @@ +--- +sidebar_position: 4 +title: "Durable Agentic Workflows" +description: Build AI agents on the durable workflow runtime in WSO2 Integrator — with durable activities, event channels, human tasks, and agent-to-agent collaboration. +keywords: [wso2 integrator, durable agent, agentic workflow, ai agent, human in the loop, events, durable] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Durable Agentic Workflows + +A durable agentic workflow flips the authoring model: instead of wiring steps together, you describe the goal in natural language and give the agent **capabilities** — activities, events, human tasks, and peer agents. An AI model plans the path at runtime, and because the agent *is* a durable workflow, every reasoning step, tool call, and wait survives crashes and restarts. + +## The agent is the declaration + +Creating a Durable Agentic Workflow (Add Artifact → **Durable Agentic Workflow** → name → **Create Agent**) generates one module-level declaration that carries everything: + +```ballerina +final workflow:DurableAgent supportAgent = check new ({ + systemPrompt: { + role: "Expense claim assistant", + instructions: string `Validate each claim, request missing bills, and escalate + unusual claims to a manager before paying.` + }, + model: claimModel, + activities: [ + validateClaim, + requestBill, + {activity: payClaim, requiresApproval: true, userRoles: "manager", + retryPolicy: "manager"}, + {activity: notifyEmployee, retryPolicy: {maxRetries: 3, retryDelay: 2}} + ], + events: [ + {name: "billSubmitted", request: BillSubmission, response: string} + ], + humanTasks: [ + {name: "approveExpense", roles: "manager", + title: "Approve expense claim", + description: "Review the claim and bills, then approve or reject."} + ], + maxIter: 16 +}); +``` + +The agent model renders this declaration as a single agent node with its capabilities around it. Use the anchored **+** buttons to add capabilities — human tasks (top left), events (bottom left), activities (middle right), agent tools (bottom right) — and click any capability to edit its entry. + + + +:::info One place to edit +Where the agent is *used* — a `supportAgent.run(...)` call in a service — the agent box is a read-only reference with a **Go to Agent** button. Configuration always lives on the agent's own model. +::: + +## Capabilities + +### Activities + +`@workflow:Activity` functions the agent may invoke. Each call runs durably with the same guarantees and the same **retry policies** as any workflow activity — including **Requires Approval** gates and **Human Review** retries (see [Review activities and error handling](review-activity-and-error-handling.md)). The agent proposes; your policies decide what needs a human. + +### Events + +Typed channels for data arriving mid-conversation. Declare the request (and optional response) type; the agent waits durably on the channel when its instructions call for it: + +```ballerina +events: [ + {name: "billSubmitted", request: BillSubmission, response: string} +] +``` + +### Human tasks + +Escalation points the agent can raise on its own judgement — "this claim looks unusual" — decided from the [Control Plane](../icp/managing-workflows.md) inbox by the named roles, exactly like workflow human tasks. + +### Agent tools and peers + +Reuse `@ai:AgentTool` functions and toolkits, or add **peer agents** — other durable agents the agent can delegate to, synchronously or through a callback channel — to build multi-agent systems. + +## Driving the agent + +```ballerina +// Start an instance; the input becomes part of the first user turn. +string instanceId = check supportAgent.run(claim.toJsonString()); + +// Deliver an event turn and read that turn's answer. +string token = check supportAgent.sendEvent(instanceId, "billSubmitted", submission); +string reply = check supportAgent.waitForEventResult(instanceId, token); + +// Read the final outcome (AgentBusyError while a human decision is pending). +string|error result = supportAgent.getResult(instanceId); +``` + +All reads are durable: results live in the workflow history, so a crashed caller can re-issue `waitForResult` and get the same answer. + +## Why durable agents are different + +| Standalone agent frameworks | Durable agentic workflows | +| --- | --- | +| Crash loses the conversation and in-flight tool calls | Every turn and tool call is recorded; restarts resume mid-plan | +| Human approval needs custom plumbing | Gates, reviews, and tasks are one form field away | +| Waiting for external input holds a process | Waits are suspended with zero resources, for days if needed | +| Retry logic in every tool | Declarative per-activity retry policies | + +## Traditional or agentic? + +Reach for an agentic workflow when the logic is branchy and judgement-heavy ("request whatever is missing, escalate the odd ones"); keep a [transaction workflow](transaction-workflow.md) when the steps are fixed and auditable. The two share activities, tasks, and the runtime — a claim system can use both side by side. + +## Next steps + +- [Build a Claim Handling Agent](../getting-started/build-a-claim-workflow-agent.md) — the end-to-end getting started. +- [Integration Control Plane](../icp/managing-workflows.md) — approving the agent's gated steps and reading its progress. diff --git a/en/docs/workflows/develop/human-task-workflow.md b/en/docs/workflows/develop/human-task-workflow.md new file mode 100644 index 0000000000..b1bb6fcc31 --- /dev/null +++ b/en/docs/workflows/develop/human-task-workflow.md @@ -0,0 +1,126 @@ +--- +sidebar_position: 2 +title: "Human Task Workflows" +description: Pause WSO2 Integrator durable workflows for role-based human decisions and external data — for hours, days, or months — with zero resources held while waiting. +keywords: [wso2 integrator, durable workflow, human task, approval, human in the loop, data event, task inbox, icp] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Human Task Workflows + +Real processes wait on people: a manager approves an expense, a reviewer checks documents, an employee submits missing information. A durable workflow can stop at any point, hand a **task** to a role, and resume the moment someone decides — whether that takes a minute or a month. While it waits, it holds no threads, no memory, no connections. + +## Await a human task + +Add an **Await Human Task** step from the palette's **Workflow → Steps** group. Give the task a name, the **roles** that may decide it, and the **payload** the decider should see: + +```ballerina +RequestDecision request = check ctx->awaitHumanTask("checkExpenseRequest", "manager", + payload = {"claimId": claim.claimId, "employee": claim.employee, + "amount": claim.amount, "purpose": claim.purpose}, + title = string `Check expense request ${claim.claimId}`, + description = "Review the new claim: request the supporting bills, or reject it.", + timeout = {days: 3}); +``` + +The workflow suspends here. The task appears in the [Integration Control Plane](../icp/managing-workflows.md) task inbox for every user holding the `manager` role, showing your title, description, and payload. + + + +## Typed decisions build the task form + +The value the human submits is typed — declare a record and the Control Plane renders a matching form. Use an **enum** for the action and it becomes a dropdown: + +```ballerina +public enum RequestAction { + REQUEST_BILL, + REJECT +} + +public type RequestDecision record {| + RequestAction action; + string comment = ""; +|}; +``` + +When the manager submits, the workflow resumes with the decision as a plain value: + +```ballerina +if request.action == REJECT { + // notify and finish +} else { + // ask the employee for the supporting bills +} +``` + +:::tip Design for the form +Whatever you put in the decision record is exactly what the decider fills in. Keep it small: an action enum, a comment, maybe a corrected amount. +::: + +## Wait for external data + +Sometimes the workflow needs *data*, not a decision — the employee submits the bills, a partner system posts a confirmation. Declare a **data event** as a `future` parameter and wait on it: + +```ballerina +@workflow:Workflow +function expenseApprovalWorkflow(workflow:Context ctx, ExpenseClaim claim, + record {|future billSubmitted;|} dataEvents) returns ExpenseResult|error { + // ... after the manager requests the bills: + BillSubmission submission = check wait dataEvents.billSubmitted; + // validate the bills and continue +} +``` + +Anyone with the workflow ID can deliver the data — typically a service resource: + +```ballerina +resource function post [string workflowId]/bills(BillSubmission submission) returns json|error { + check workflow:sendData(expenseApprovalWorkflow, workflowId, "billSubmitted", submission); + return {workflowId, status: "BILLS_SUBMITTED"}; +} +``` + +While the workflow waits, the Control Plane's execution graph marks the halt point — a **waiting** data node named after the event — so anyone can see exactly what the process is blocked on. + + + +## A complete two-review flow + +Combining tasks and data events gives the classic claim pattern — two reviews with an evidence hand-off between them: + +1. **`checkExpenseRequest`** — the manager triages the new claim: request bills, or reject. +2. The employee submits bills via `workflow:sendData` → the workflow validates them. +3. **`reviewBills`** — the manager sees the validation result and approves or rejects the payout. + +Every decision happens in the Control Plane inbox; the service exposes no task-completion endpoints. + +## Timeouts + +`timeout` bounds the wait. When it expires the task fails with a timeout error your workflow can handle — escalate, remind, or fail gracefully: + +```ballerina +RequestDecision|error decision = ctx->awaitHumanTask("checkExpenseRequest", "manager", + payload = ..., timeout = {days: 3}); +if decision is error { + // escalate to a different role, or end the claim +} +``` + +## Next steps + +- [Review activities and error handling](review-activity-and-error-handling.md) — approvals attached to *activities* rather than free-standing tasks. +- [Integration Control Plane](../icp/managing-workflows.md) — where tasks are decided, and how roles map to users. diff --git a/en/docs/workflows/develop/review-activity-and-error-handling.md b/en/docs/workflows/develop/review-activity-and-error-handling.md new file mode 100644 index 0000000000..4edbcb7f0d --- /dev/null +++ b/en/docs/workflows/develop/review-activity-and-error-handling.md @@ -0,0 +1,109 @@ +--- +sidebar_position: 3 +title: "Review Activities & Error Handling" +description: Gate risky workflow steps behind human approval and turn failures into human-reviewed retries in WSO2 Integrator durable workflows. +keywords: [wso2 integrator, durable workflow, review activity, retry, error handling, approval gate, human review] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Review Activities & Error Handling + +Failures and risky steps are where durable workflows earn their keep. Instead of scattering try/catch blocks and retry loops through your code, you attach a **retry policy** to each activity call — and for the steps that matter most, you put a **human review** in front of the step or behind its failure. + +## The three retry policies + +Every activity call takes a **Retry Policy**, chosen in the call's form: + +| Policy | What happens on failure | +| --- | --- | +| **No Automatic Retry** (default) | The error returns to your workflow logic — handle it with normal Ballerina `check`/`if` handling. | +| **Auto Retry** | The engine re-executes the activity with configurable attempts, delay, and backoff. | +| **Human Review** | A **review task** is created for the roles you name. The reviewer can retry as-is, retry with corrected input, or fail the step. | + + + +### Auto Retry — for transient failures + +```ballerina +string _ = check ctx->callActivity(notifyEmployee, + {"claimId": claim.claimId, "message": "Your claim was approved."}, + retryPolicy = {maxRetries: 3, retryDelay: 2, retryBackoff: 2.0}); +``` + +A flaky notification service recovers transparently; the workflow never sees the intermediate failures. + +### Human Review — when a person should fix it + +The roles **are** the policy — name who may decide the retry: + +```ballerina +string paymentRef = check ctx->callActivity(makePayment, + {"claimId": claim.claimId, "amount": claim.amount, "currency": claim.currency}, + retryPolicy = "manager"); // one role, or ["manager", "finance"] +``` + +When `makePayment` fails (say, the gateway rejects the currency), a **review task** appears in the [Control Plane](../icp/managing-workflows.md) inbox for managers, showing the failing input and the error. The reviewer chooses: + +- **Proceed** — rerun with the same input. +- **Proceed with input** — rerun with corrected input (the form is generated from the activity's parameters, pre-filled with the failing values — fix the currency and retry). +- **Reject** — surface the original error to the workflow, which handles it like any other error. + + + +## Approval gates — review *before* the step runs + +Some steps should never run without sign-off, even when nothing has failed. On a durable agent's activity, enable **Requires Approval** with **Reviewer Roles**: + +```ballerina +activities: [ + validateClaim, + {activity: payClaim, requiresApproval: true, userRoles: "manager"} +] +``` + +Before every `payClaim` call the agent pauses and a review task shows the *proposed* input. The manager can proceed, edit the input, or reject the call — the same three decisions as a failure review, just before the step instead of after it. + +## Error handling in the workflow logic + +Retry policies handle the step; your workflow logic handles the outcome. Because activities return ordinary Ballerina values and errors, error handling looks like normal code: + +```ballerina +string|error paymentRef = ctx->callActivity(makePayment, + {"claimId": claim.claimId, "amount": claim.amount}, retryPolicy = "manager"); +if paymentRef is error { + // the reviewer chose Reject — compensate and finish cleanly + string _ = check ctx->callActivity(notifyEmployee, + {"claimId": claim.claimId, "message": "Payment could not be completed."}); + return {claimId: claim.claimId, status: "PAYMENT_FAILED"}; +} +``` + +Because completed activities are never re-executed, compensation logic only ever deals with the step that actually failed — everything before it is already safely recorded. + +## Choosing a policy + +| Situation | Policy | +| --- | --- | +| Flaky downstream, safe to repeat | Auto Retry | +| Bad input a person could correct | Human Review | +| Risky/irreversible step (payments, deletions) | Approval gate (`requiresApproval`) | +| Business-level failure with a fallback path | No Automatic Retry + workflow logic | + +## Next steps + +- [Human task workflows](human-task-workflow.md) — free-standing decisions and external data. +- [Durable agentic workflows](durable-agentic-workflow.md) — the same policies applied to an AI agent's activities. diff --git a/en/docs/workflows/develop/transaction-workflow.md b/en/docs/workflows/develop/transaction-workflow.md new file mode 100644 index 0000000000..17e80d2981 --- /dev/null +++ b/en/docs/workflows/develop/transaction-workflow.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 1 +title: "Transaction Workflows" +description: Wire activities into reliable, crash-safe transaction workflows in WSO2 Integrator with exactly-once recording, automatic retries, and durable timers. +keywords: [wso2 integrator, durable workflow, transaction, activity, retry, timer, crash recovery] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Transaction Workflows + +A transaction workflow is a multi-step business process where every step must happen reliably — validate the order, charge the card, reserve the stock, send the confirmation. A durable workflow makes that sequence crash-safe: each completed step is recorded, a restart replays the record instead of redoing the work, and a failed step can retry without touching the steps that already succeeded. + +## Create a workflow + +1. In the design view, select **+ Add Artifact**. +2. Under **Durable Workflow**, select **Durable Workflow**. +3. Give it a name (for example `orderWorkflow`) and optionally a **Workflow Input Data type**. +4. Select **Create** — the workflow diagram opens. + + + +The generated function is a normal Ballerina function with a workflow context — you design its logic in the same flow diagram used for any integration, with `if`/`else` branches, loops, and variables: + +```ballerina +@workflow:Workflow +function orderWorkflow(workflow:Context ctx, OrderInput input) returns OrderResult|error { +} +``` + +## Activities: the reliable units of work + +Anything that touches the outside world — an API call, a database write, an email — belongs in an **activity**. The workflow calls activities through the context, and the runtime records each result: + +- A **completed activity is never re-executed** on replay; its recorded result is reused, even across process crashes. +- A **failed activity** can retry (see below) without repeating earlier steps. + +Add a step with **Call Activity** from the palette's **Workflow → Steps** group, then pick or create the activity function: + +```ballerina +@workflow:Activity +function chargeCard(string orderId, decimal amount) returns string|error { + // call the payment gateway +} +``` + +```ballerina +@workflow:Workflow +function orderWorkflow(workflow:Context ctx, OrderInput input) returns OrderResult|error { + boolean valid = check ctx->callActivity(validateOrder, {"input": input}); + if !valid { + return {orderId: input.orderId, status: "INVALID"}; + } else { + string paymentRef = check ctx->callActivity(chargeCard, + {"orderId": input.orderId, "amount": input.amount}); + string _ = check ctx->callActivity(sendConfirmation, + {"orderId": input.orderId, "paymentRef": paymentRef}); + return {orderId: input.orderId, status: "COMPLETED", paymentRef}; + } +} +``` + + + +:::tip Idempotent side effects +A *completed* activity never runs twice. A *failed* attempt may run again when retries are enabled — make the activity's side effects idempotent (for example, pass an idempotency key to the payment gateway) when you turn retries on. +::: + +## Automatic retries + +Transient failures — a flaky network, a rate limit — should not fail the whole transaction. Set the **Retry Policy** on the activity call to **Auto Retry**: + +```ballerina +string paymentRef = check ctx->callActivity(chargeCard, + {"orderId": input.orderId, "amount": input.amount}, + retryPolicy = {maxRetries: 3, retryDelay: 2, retryBackoff: 2.0}); +``` + +The engine retries with backoff; the workflow code stays clean of retry loops. For failures that need a human decision, see [Review activities and error handling](review-activity-and-error-handling.md). + +## Durable timers + +Need to wait before a step — a cooling-off period, a settlement delay? Use the **Sleep** step. A durable sleep survives restarts and consumes nothing while waiting: + +```ballerina +check ctx->sleep({hours: 24}); +``` + +:::warning +Never use `runtime:sleep()` inside a workflow — it blocks a thread and is lost on restart. Always use the workflow context's durable sleep. +::: + +## Start a workflow and read its result + +Workflows start from services, automations, or other triggers with `workflow:run`, which returns the instance ID: + +```ballerina +string workflowId = check workflow:run(orderWorkflow, input); +// later: +anydata result = check workflow:getWorkflowResult(workflowId, 30); +``` + +Every instance — running, suspended, or completed — is visible in the [Integration Control Plane](../icp/managing-workflows.md), including an execution graph that shows exactly which step it is on. + +## Next steps + +- [Human task workflows](human-task-workflow.md) — pause the transaction for a person's decision. +- [Review activities and error handling](review-activity-and-error-handling.md) — approval gates and human-reviewed retries. diff --git a/en/docs/workflows/getting-started/build-a-claim-workflow-agent.md b/en/docs/workflows/getting-started/build-a-claim-workflow-agent.md new file mode 100644 index 0000000000..c792f63d28 --- /dev/null +++ b/en/docs/workflows/getting-started/build-a-claim-workflow-agent.md @@ -0,0 +1,195 @@ +--- +sidebar_position: 1 +title: "Build a Claim Handling Agent" +description: Build your first durable agentic workflow in WSO2 Integrator — an AI agent that validates expense claims and pays them only after a manager approves. +keywords: [wso2 integrator, durable workflow, agentic workflow, durable agent, claim workflow, human in the loop, approval] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Build a Claim Handling Agent + +**Time:** 15 minutes | **What you'll build:** A durable AI agent that receives expense claims, validates them, and pays them out — but only after a manager approves the payment from the Integration Control Plane. If the process crashes while waiting, it resumes exactly where it left off. + +:::info Prerequisites + +- [WSO2 Integrator installed](../../get-started/setup/local-setup.md) +- Signed in to WSO2 Integrator Copilot (provides the default AI model — no API key needed) + +::: + +## Step 1: Create the integration + +1. Open WSO2 Integrator. +2. Select **Create** in the **Create New Integration** card. +3. Set **Integration Name** to `ClaimHandler`. +4. Select **Create Integration**. + + + +## Step 2: Add a Durable Agentic Workflow + +1. In the design view, select **+ Add Artifact**. +2. Under **Durable Workflow**, select **Durable Agentic Workflow**. +3. Set **Name** to `claimAgent`. +4. Select **Create Agent**. + + + +The agent model opens: a single agent node with anchored **+** buttons for adding capabilities — human tasks (top left), events (bottom left), activities (middle right), and agent tools (bottom right). + +## Step 3: Describe the agent + +Click the agent node and give it its role and instructions: + +- **Role:** `Expense claim assistant` +- **Instructions:** + + ```text + Process expense claims end to end. Validate each claim with validateClaim first and + reject invalid claims with a clear reason. When a claim is valid, pay it with payClaim + using the claimed amount. Finish with a one-line summary of the outcome. + ``` + + + +## Step 4: Give the agent activities + +Activities are the units of work the agent can call. Each one runs durably — completed work is never lost or repeated, even across restarts. + +First add the claim validator: + +1. Select the **+** button on the **middle right** of the agent node and choose **Add Activity**. +2. Create a new activity named `validateClaim` with a `claim` input and the validation logic: + +```ballerina +@workflow:Activity +function validateClaim(ExpenseClaim claim) returns boolean|error { + return claim.amount > 0d && claim.purpose.trim().length() > 0; +} +``` + +Then add the payment activity — this is the risky step, so gate it behind a manager: + +1. Add another activity named `payClaim`. +2. In the activity form, enable **Requires Approval** and set **Reviewer Roles** to `manager`. + +```ballerina +@workflow:Activity +function payClaim(string claimId, decimal amount) returns string|error { + return string `PAY-${claimId}`; +} +``` + + + +Behind the scenes the designer maintains a single declaration — the agent *is* the workflow: + +```ballerina +final workflow:DurableAgent claimAgent = check new ({ + systemPrompt: { + role: "Expense claim assistant", + instructions: string `Process expense claims end to end. ...` + }, + model: claimModel, + activities: [ + validateClaim, + {activity: payClaim, requiresApproval: true, userRoles: "manager"} + ] +}); +``` + +## Step 5: Expose the agent over HTTP + +Add an HTTP service so employees can submit claims. Each `run` starts a durable agent instance; the returned `instanceId` is the claim's reference. + +```ballerina +service /claims on new http:Listener(9090) { + + resource function post .(ExpenseClaim claim) returns json|error { + string instanceId = check claimAgent.run(claim.toJsonString()); + return {claimId: claim.claimId, instanceId, status: "PROCESSING"}; + } + + resource function get [string instanceId]() returns json|error { + string|error result = claimAgent.getResult(instanceId); + if result is workflow:AgentBusyError { + return {instanceId, status: "PENDING_APPROVAL"}; + } + if result is error { + return result; + } + return {instanceId, status: "COMPLETED", summary: result}; + } +} +``` + +## Step 6: Run it + +1. Select **Run** in the designer to start the integration. +2. Submit a claim: + +```bash +curl -X POST localhost:9090/claims -H 'Content-Type: application/json' \ + -d '{"claimId":"EXP-1","employee":"nimal","amount":180.50,"purpose":"Team lunch"}' +``` + +The agent validates the claim, decides to pay it, and **pauses** — the gated `payClaim` created an approval review for the `manager` role. The workflow now waits durably; you can even restart the integration and nothing is lost. + +## Step 7: Approve the payment + +1. Open the **Integration Control Plane** and sign in as a user with the `manager` role. +2. Open the **Task Inbox** — the `payClaim` approval shows the claim ID and amount the agent proposed. +3. Select **Proceed**. + + + +The agent resumes, completes the payment, and records its summary: + +```bash +curl localhost:9090/claims/ +# {"instanceId":"...","status":"COMPLETED","summary":"Claim EXP-1 validated and paid (PAY-EXP-1)."} +``` + +## What you built + +- A **durable AI agent** whose reasoning, activity calls, and waits all survive restarts. +- A **gated activity** — the agent can propose a payment, but only a manager can release it. +- A **zero-cost wait** — the claim can sit in the inbox for days without holding any resources. + +## Next steps + +- [Human task workflows](../develop/human-task-workflow.md) — ask people structured questions, not just approvals. +- [Review activities and error handling](../develop/review-activity-and-error-handling.md) — let a human fix a failed step's input and retry it. +- [Durable agentic workflows](../develop/durable-agentic-workflow.md) — events, multi-turn conversations, and agent-to-agent collaboration. diff --git a/en/docs/workflows/icp/managing-workflows.md b/en/docs/workflows/icp/managing-workflows.md new file mode 100644 index 0000000000..bf5f135892 --- /dev/null +++ b/en/docs/workflows/icp/managing-workflows.md @@ -0,0 +1,89 @@ +--- +sidebar_position: 1 +title: "Manage Workflows with the Control Plane" +description: Monitor durable workflow instances, decide human tasks and reviews, and control running workflows from the WSO2 Integration Control Plane. +keywords: [wso2 integrator, integration control plane, icp, task inbox, human task, review activity, workflow monitoring] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Manage Workflows with the Integration Control Plane + +The **Integration Control Plane (ICP)** is where running workflows meet their humans: operations teams watch instances and intervene, and business users decide the tasks and reviews workflows are waiting on — all through role-based access. + +## Connect your integration + +Add the Control Plane runtime bridge to the integration and configure it in `Config.toml`: + +```toml +[wso2.icp.runtime.bridge] +environment = "dev" +project = "expense" +integration = "expense-approval" +runtime = "default" +secret = "" + +[ballerina.workflow.management] +enableManagementApi = true +``` + +The integration registers itself with the Control Plane on startup; its workflows, tasks, and reviews appear under the configured project. + + + +## Users and roles + +Task and review visibility is role-based: a task created for the `manager` role is only visible to — and decidable by — users holding that role. Set up Control Plane users with the roles your workflows name (`manager`, `finance`, `support-lead`, …) and the workflow-management permissions (view workflows, view/manage human tasks). + +## Monitor workflow instances + +The workflow list shows every instance with its status — **Running**, **Suspended**, **Completed**, **Failed** — and each instance opens into: + +- **Execution graph** — the steps the instance has taken, and crucially **where it is halted right now**: a pending human task, a review awaiting a decision, or a **waiting data event** shown as a receiving node named after the event. +- **History** — the full recorded event history for auditing and debugging. + + + +## The task inbox + +The inbox lists everything waiting on the signed-in user's roles: + +- **Human tasks** — rendered as forms generated from the task's typed decision record (enums become dropdowns). Submit to resume the workflow with the decision. +- **Approval reviews** — a gated activity's *proposed* input, before it runs: **Proceed**, **Proceed with input** (edit the values), or **Reject**. +- **Retry reviews** — a failed activity's input and error: retry as-is, retry with corrected input, or fail the step. + + + +## Control running instances + +From an instance's page, operators can: + +| Action | Effect | +| --- | --- | +| **Suspend** | Pause the instance; it holds its exact position. | +| **Resume** | Continue a suspended instance from where it paused. | +| **Terminate** | Stop immediately, without cleanup. | +| **Cancel** | Request a graceful stop the workflow can react to. | + +## Programmatic access + +Everything the Control Plane shows is served by the integration's [Management API](../reference/management-api.md) — build custom portals or automations on the same endpoints. diff --git a/en/docs/workflows/overview.md b/en/docs/workflows/overview.md new file mode 100644 index 0000000000..a7211ca877 --- /dev/null +++ b/en/docs/workflows/overview.md @@ -0,0 +1,51 @@ +--- +title: Durable Workflows Overview +description: Build long-running, crash-safe business processes with WSO2 Integrator using durable workflows, human tasks, events, and durable AI agents. +keywords: [wso2 integrator, durable workflow, workflow, human task, agentic workflow, durable agent, temporal, long running, crash recovery] +sidebar_label: Overview +slug: /workflows/overview +--- + +# Durable Workflows + +Most integrations start simple and end up long-lived: an order needs a manager's approval, a claim waits days for supporting documents, a payment needs a retry after a gateway hiccup. A normal program loses everything when the process restarts — a **durable workflow does not**. + +WSO2 Integrator lets you design workflows that: + +- **Survive crashes and restarts** — every completed step is recorded, and the workflow resumes exactly where it left off. A finished step is never re-executed. +- **Wait for as long as it takes** — pause for hours, days, or months for a human decision or an external event, consuming no threads or memory while suspended. +- **Recover from failures** — retry failed steps automatically, or hand the failure to a human who can fix the input and retry. +- **Keep humans in the loop** — assign role-based tasks that people decide from the [Integration Control Plane](icp/managing-workflows.md) task inbox. + +## Two ways to build, one durable runtime + +| Durable Workflow | Durable Agentic Workflow | +| --- | --- | +| You wire the steps together in a visual flow | You describe the goal; an AI model decides the steps | +| Explicit, predictable path | Adapts to each request at runtime | +| Best for known, fixed business logic | Best for branchy, hard-to-enumerate logic | + +Both run on the same durable runtime, so an AI agent gets crash-safety, human tasks, timers, and retries for free. + +## Getting started + +- **[Build a Claim Handling Agent](getting-started/build-a-claim-workflow-agent.md):** Your first durable agentic workflow — an agent that validates expense claims and pays them out only after a manager approves. + +## Workflow features + +- **[Transaction workflows](develop/transaction-workflow.md):** Wire activities into a reliable, crash-safe flow with automatic retries. +- **[Human task workflows](develop/human-task-workflow.md):** Pause for role-based human decisions and external data, for as long as it takes. +- **[Review activities and error handling](develop/review-activity-and-error-handling.md):** Approval gates before risky steps and human-reviewed retries after failures. +- **[Durable agentic workflows](develop/durable-agentic-workflow.md):** AI agents with durable activities, events, human tasks, and agent-to-agent collaboration. + +## Manage running workflows + +- **[Integration Control Plane](icp/managing-workflows.md):** See every workflow instance, where it is halted, decide human tasks and reviews, and suspend, resume, or terminate runs. + +## Tutorials + +- **[Tutorials](tutorials/overview.md):** Complete, step-by-step examples for each workflow feature. + +## Reference + +- **[Management API](reference/management-api.md):** The REST API behind the Control Plane — list instances, read execution graphs, and complete tasks programmatically. diff --git a/en/docs/workflows/reference/management-api.md b/en/docs/workflows/reference/management-api.md new file mode 100644 index 0000000000..473daa0e9d --- /dev/null +++ b/en/docs/workflows/reference/management-api.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 1 +title: "Management API" +description: REST API reference for managing WSO2 Integrator durable workflows — instances, execution graphs, human tasks, and review activities. +keywords: [wso2 integrator, durable workflow, management api, rest, human task api, review activity api] +--- + +# Management API + +Every integration with durable workflows can expose a **Management API** — the same REST surface the [Integration Control Plane](../icp/managing-workflows.md) uses. Enable it to build custom portals, automations, or operational tooling. + +## Enable and configure + +```toml +[ballerina.workflow.management] +enableManagementApi = true +port = 8234 # default +enableApiKey = true # optional API-key protection +apiKeyValue = "" +apiKeyHeader = "x-api-key" +``` + +Base URL: `http://:8234/workflow` + +### Caller identity headers + +| Header | Purpose | +| --- | --- | +| `x-user-id` | Recorded in audit fields (`completedBy`, `decidedBy`). | +| `x-user-roles` | Comma-separated roles; tasks and reviews are filtered and authorized against them. | + +## Workflow instances + +| Method & path | Description | +| --- | --- | +| `GET /workflows` | List instances. Filters: `status` (`RUNNING`, `SUSPENDED`, `COMPLETED`, `FAILED`, …), `workflowType`, `workflowId` prefix, time bounds, pagination (`limit`, `pageToken`). | +| `GET /workflows/{workflowId}` | Instance detail: type, status, result, and activity invocations. | +| `GET /workflows/{workflowId}/history` | Full recorded event history. | +| `GET /workflows/{workflowId}/execution-graph` | Nodes and edges of the execution so far. Node types: `ACTIVITY`, `TIMER`, `DATA`, `CHILD_WORKFLOW`, `HUMAN_TASK`, `REVIEW_ACTIVITY`. A `DATA` node with status `WAITING` marks a data event the workflow is currently blocked on. | +| `GET /workflows/{workflowId}/activity-tree` | The same execution as a tree of typed nodes with inputs, outputs, and attempts. | +| `POST /workflows/{workflowId}/suspend` | Pause the instance. | +| `POST /workflows/{workflowId}/resume` | Resume a suspended instance. | +| `POST /workflows/{workflowId}/terminate` | Stop immediately (no cleanup). | +| `POST /workflows/{workflowId}/cancel` | Request graceful cancellation. | +| `POST /workflows` | Start a workflow by type: `{"workflowType": "...", "input": {…}}`. | +| `GET /definitions` | The workflow types this integration registers, for launcher UIs. | + +Run-scoped variants exist for detail, control, history, activity-tree, and execution-graph: +append `/{runId}` (for example `GET /workflows/{workflowId}/{runId}/execution-graph`). + +### Example: find where an instance is halted + +```bash +curl -s http://localhost:8234/workflow/workflows//execution-graph \ + -H 'x-user-roles: manager' | jq '.nodes[] | select(.status=="WAITING" or .status=="RUNNING")' +``` + +## Human tasks + +| Method & path | Description | +| --- | --- | +| `GET /human-tasks` | List tasks; filters: `status` (`PENDING`, `COMPLETED`, …), `parentWorkflowId`, `taskName`, time bounds, pagination. Visibility is filtered by `x-user-roles`. | +| `GET /human-tasks/pending-count` | Pending-task count for the caller's roles (inbox badges). | +| `GET /human-tasks/{taskId}` | Task detail: title, description, payload, roles, and the decision form's JSON schema. | +| `POST /human-tasks/{taskId}/complete` | Complete with `{"result": {…}}` matching the task's decision type. | +| `POST /human-tasks/{taskId}/fail` | Fail the task with a reason. | + +```bash +curl -s -X POST http://localhost:8234/workflow/human-tasks//complete \ + -H 'Content-Type: application/json' -H 'x-user-id: alice' -H 'x-user-roles: manager' \ + -d '{"result": {"action": "REQUEST_BILL", "comment": "Please attach the receipts"}}' +``` + +## Review activities + +Approval gates (before a gated step runs) and retry reviews (after a step fails) share one surface: + +| Method & path | Description | +| --- | --- | +| `GET /review-activities` | List reviews; same filters and role-based visibility as human tasks. | +| `GET /review-activities/{taskId}` | Review detail: the activity, its (proposed or failing) input, the error for failure reviews, and the input form's JSON schema. | +| `POST /review-activities/{taskId}/proceed` | Run/rerun with the original input. | +| `POST /review-activities/{taskId}/proceed-with-input` | Run/rerun with corrected input: `{"input": {…}}`. | +| `POST /review-activities/{taskId}/reject` | Skip the gated call, or surface the failure to the workflow. | + +```bash +curl -s -X POST http://localhost:8234/workflow/review-activities//proceed-with-input \ + -H 'Content-Type: application/json' -H 'x-user-id: alice' -H 'x-user-roles: manager' \ + -d '{"input": {"claimId": "EXP-1", "amount": 180.50, "currency": "EUR"}}' +``` + +:::info Role-based visibility +A task or review that declares roles is only listed for — and decidable by — callers whose `x-user-roles` include one of them. Reviews created without roles can optionally be restricted with the `reviewActivityAccessRole` configuration. +::: + diff --git a/en/docs/workflows/tutorials/overview.md b/en/docs/workflows/tutorials/overview.md new file mode 100644 index 0000000000..9a1751306f --- /dev/null +++ b/en/docs/workflows/tutorials/overview.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 1 +title: "Workflow Tutorials" +description: Complete, step-by-step durable workflow tutorials for WSO2 Integrator. +keywords: [wso2 integrator, durable workflow, tutorials] +sidebar_label: Overview +--- + +# Workflow Tutorials + +Complete, real examples — each tutorial builds a working integration step by step with screenshots and full code. + +- **[Build a Claim Handling Agent](../getting-started/build-a-claim-workflow-agent.md)** — the getting-started tutorial: a durable agent with a gated payment approved from the Control Plane. + +More tutorials are on the way, covering each feature in depth: + +- *Expense approval with two human reviews* — a traditional control-flow workflow with a data-event hand-off between reviews. +- *Payment retries with human review* — Auto Retry for transient failures and reviewer-corrected retries for bad input. +- *A multi-agent travel desk* — durable agents collaborating through synchronous and callback peers. diff --git a/en/sidebars.ts b/en/sidebars.ts index b2e22c80fa..2b81397a86 100644 --- a/en/sidebars.ts +++ b/en/sidebars.ts @@ -2020,6 +2020,56 @@ const sidebars: SidebarsConfig = { ], }, + // ───────────────────────────────────────────── + // DURABLE WORKFLOWS + // "How do I build long-running, crash-safe processes?" + // ───────────────────────────────────────────── + { + type: 'category', + label: 'Durable Workflows', + collapsed: true, + link: { type: 'doc', id: 'workflows/overview' }, + items: [ + // Getting Started + { + type: 'category', + label: 'Getting Started', + items: [ + 'workflows/getting-started/build-a-claim-workflow-agent', + ], + }, + // Workflow Features + { + type: 'category', + label: 'Workflow Features', + items: [ + 'workflows/develop/transaction-workflow', + 'workflows/develop/human-task-workflow', + 'workflows/develop/review-activity-and-error-handling', + 'workflows/develop/durable-agentic-workflow', + ], + }, + // Integration Control Plane + { + type: 'category', + label: 'Integration Control Plane', + items: [ + 'workflows/icp/managing-workflows', + ], + }, + // Tutorials + 'workflows/tutorials/overview', + // API Reference + { + type: 'category', + label: 'API Reference', + items: [ + 'workflows/reference/management-api', + ], + }, + ], + }, + // ───────────────────────────────────────────── // TUTORIALS // "Show me a complete, real example"