Build
Workflows
Model a process, start an instance, advance its tasks, and audit the run.
Pikbase docs A workflow coordinates a business process that spans more than one request: an approval, an onboarding, a nightly reconciliation. It is modelled as data in the tenant, so it is queryable like anything else.
The records
| Definition | Role |
|---|---|
GsbWorkflow |
The design: name, title, activities, transitions, triggers |
GsbActivity |
One node — a function call, a human task, a pause |
GsbTransition |
One edge, with an optional condition |
GsbWorkflowInstance |
One run, with status, result, and lastUpdateDate |
GsbWorkflowLog |
Per-step audit rows for an instance |
GsbWorkflowTrigger |
What starts a run: an entity operation or a cron expression |
Routing and composition
A workflow can mix standard, conditional, and named parallel transitions. Conditional routes continue only when the activity result matches the configured value. Parallel routes fork independent activities; an Await Parallel activity joins the named branches and resumes only after every inbound branch has completed.
Use a Multi Inner Workflow activity to compose processes instead of duplicating them. It can start one child workflow for every element supplied through a runtime instance parameter, run those child instances sequentially or in parallel, and continue the parent after the selected child workflow runs complete. The child workflow and entity definition are configurable, while the runtime parameter controls which instances are created for a particular run.
Human decisions
User activities can assign a task to a role, position, the instance starter, or a manager approval step. The workflow remains durable while it waits. A person advances the task with an action such as approve, reject, reassign, or cancel, and the result selects the next conditional route.
Functions inside activities
Each activity has before, main, and after function pipelines. A pipeline can call reusable functions, run server-side script, read or write entities, call GSB or external APIs, commit pending changes, generate and attach a PDF, send email or SMS, and create in-app notifications. Operations run in order, so a flow can calculate data, render a document, deliver it, and continue routing without moving orchestration into the frontend.
Start a run
Use startWorkflow whenever the process waits on a person, a timer, or an external event. It returns as soon as the run is accepted.
const started = await entityService.startWorkflow(
{ workflow_id: "workflow-uuid", data: { orderId: "order-123" } },
token,
tenantCode,
);
if (!started.success) {
throw new Error(started.message);
}
const instanceId = started.instance?.id;
Use runWorkflow only when the caller genuinely needs the outcome inline. It holds the connection open for the whole run.
Both operations are registered as external-side-effect: disabled by default, and refused through the CLI or an MCP client without explicit approval.
gsb call startWorkflow --yes --raw --input '{
"request": {
"workflow": { "name": "Order Approval" },
"data": { "orderId": "order-123" }
}
}'
Advance a waiting task
An activity that needs a decision leaves the instance waiting. Three calls supply that decision, and they differ in who calls them and where they stop.
The assignee submits their own task with submitWorkflowTask. The decision is a transition choice: selection_id is the id of the GsbTransition leaving the current activity, so a task screen reads the outgoing transitions and offers them as buttons rather than hardcoding "approve" and "reject".
await entityService.submitWorkflowTask(
{
task: {
id: "task-123",
selection_id: "transition-uuid",
note: "Within policy.",
},
},
token,
tenantCode,
);
An integration or server function completes a task programmatically with iterateTask, which then carries on with the rest of the workflow.
await entityService.iterateTask(
{
taskId: "task-123",
action: "approve",
input: { comments: "Within policy." },
},
token,
tenantCode,
);
An administrator debugging a design steps it with iterateOnce, which advances exactly one activity and halts. Every step must carry instance.workflow_id.
const step = await entityService.iterateOnce(
{ instance: { id: "instance-123", workflow_id: "workflow-uuid" } },
token,
tenantCode,
);
For an administrator the response instance also reports activity_id, result, currentTask, lastProcessor_id, locker_id and message — where the run is, what it chose, and why it stopped.
Monitor instances
There is no dedicated instance operation. Workflow instances are ordinary entities, so read them with query.
const instances = new QueryParams<GsbWorkflowInstance>("GsbWorkflowInstance")
.filter("workflow_id", "workflow-uuid")
.sortBy("lastUpdateDate", QuerySortType.Descending)
.select(["id", "status", "result", "startDate", "lastUpdateDate"])
.skip(0)
.take(25)
.returnCount();
const page = await entityService.query(instances, token, tenantCode);
Filter on entity_id to find every run touching one record. Read GsbWorkflowLog by instance_id for the engine's step-by-step trace, including activity, function, operation, result, details, and processing time. Read GsbWfInstanceHistory for the human history: who acted, when they completed the task, their note and result, and any attachments.
Operating rules
- Starting the same workflow twice creates two instances. Guard against duplicate starts in the caller.
- Keep the
datapayload small; it is stored with the run. - A single activity failing marks the instance failed. Read
resultandresponseStrbefore retrying. - Never start or run a workflow from model-generated code without explicit user confirmation.
Activities call serverless functions — see serverless functions and runWfFunction.