Skip to main content

Call a workflow from an app, an agent, MCP or a script

A published workflow is callable by its pinned reference wf:<tenant>/<slug>@<n> (the published revision; @latest is for interactive callers only, never in a manifest). The input is { logical_date?, targets? }, and the call is always a production run as the caller.

From a data app​

Declare it in the manifest (a person reviews the version that adds or re-pins a workflow import when it is published):

"functions": {
"weekly_digest": { "ref": "wf:<tenant>/weekly_digest@2" }
}

Start it from a button, show progress, allow Cancel:

import { bda } from "./studio/bda.js";

const started = await bda.fn.call("weekly_digest", {}, { wait: false });
// input (optional): { logical_date: "YYYY-MM-DD", targets: ["<artifact>"] }
cancelButton.onclick = () => bda.fn.cancel(started.invocation_id);
const watch = bda.fn.watch(started.invocation_id, batch => showProgress(batch.items));
// batch.items: plain-word lines, e.g. "fetch succeeded · 812 rows · 1.2 s"
const final = await watch.done;
const out = bda.fn.outputOf(final) as {
status: string; partial?: boolean; failed?: string[];
artifacts: Record<string, { version_id: string; rows: number | null; data?: unknown }>;
receipts: { node: string; state: string; approval_id?: string | null }[];
};
if (out.partial) showNote(`Some steps did not finish: ${(out.failed ?? []).join(", ")}`);

What comes back:

{
"run_id": "run_...",
"status": "succeeded",
"logical_date": "2026-09-21",
"artifacts": {
"summary": { "version_id": "av_...", "rows": 8, "published": true,
"data": { "columns": [{ "name": "reason", "type": "string" }], "rows": [["card declined"]] } }
},
"receipts": [
{ "node": "send", "destination": "email:...", "state": "awaiting_approval", "approval_id": "apr_...", "idempotency_key": "..." }
]
}
  • status: "partial" adds partial: true and failed: [steps].
  • artifacts[name].data is inlined for the run's targets while the output stays under 1 MiB; the rest is read by version id.
  • A receipt's state follows the send: awaiting_approval, then sent (or rejected, expired, failed). Do not tell the viewer "sent" before it is.

Rules for app code: call only the declared local name through bda.fn; start it from a button's click (the sandbox has no forms); render output as text, never HTML; reuse does not apply to workflow imports.

From an agent​

Add wf:<tenant>/<slug>@<n> to the agent's functions and switch on Chat and agents on the workflow (a person does that in Studio). The agent then has a tool wf_<slug> that starts a run and streams its progress. A data app's chat lists the app's workflow imports the same way. An agent or an app can start a workflow, never approve its sends.

From MCP​

With MCP exposure switched on (a person does that), the workflow is a tool named wf_<slug> for a coding agent connected to Studio; it starts a run and streams progress. To build or edit workflows the agent uses the workflow_* tools.

From a script​

# $STUDIO = https://app.bicycle.ai ; $TOKEN = a Bicycle API token
curl -s -X POST "$STUDIO/api/studio/v1/functions/wf:<tenant>/<slug>@<n>:submit" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"input": {"logical_date": "2026-09-21"}}'
# 202 {"invocation_id": "inv_...", "kind": "workflow", "status": "queued", ...}
curl -s "$STUDIO/api/studio/v1/invocations/inv_...?wait_s=25" -H "Authorization: Bearer $TOKEN"
curl -s "$STUDIO/api/studio/v1/invocations/inv_.../events?after=0&wait_s=25" -H "Authorization: Bearer $TOKEN"
curl -s -X POST "$STUDIO/api/studio/v1/invocations/inv_...:cancel" -H "Authorization: Bearer $TOKEN"

:invoke (synchronous) answers kind_requires_async for a workflow; use :submit. function_not_found (404) means an unknown or unpublished reference, or a surface the workflow is not exposed to. workflow_disabled (409) means it is disabled.

What the person sees​

Every call from an app is a run as the viewer, on the Runs page with the app's id, and on the workflow's Runs tab, with the same trace as a run started from Studio. A send it opened waits in Approvals like any other.

Worked example: Train a model weekly, predict daily (an app that starts a workflow and shows its summary).