Pikbase Docs
Open console (opens the console)
Esc

Type to search.

Build

Workflows

Model a process, start an instance, advance its tasks, and audit the run.

Contract sourcePikbase 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 data payload small; it is stored with the run.
  • A single activity failing marks the instance failed. Read result and responseStr before retrying.
  • Never start or run a workflow from model-generated code without explicit user confirmation.

Activities call serverless functions — see serverless functions and runWfFunction.