Workflows
Chain multiple Botverse tools into a single declarative pipeline. Define your steps in JSON, submit once, and poll for results — the engine handles parallelism, dependencies, retries, and failure recovery.
How it works
A workflow is a JSON document written in BWDL — the Botverse Workflow Definition Language. You submit it with submit_workflow, which validates the definition, charges a billing gate, and immediately returns a workflow_id. Then you poll get_workflow_statusevery 5–10 seconds: each call advances the workflow — it checks in-progress steps, dispatches newly ready ones, and applies failure policies. When a terminal status is returned, the workflow is done.
Quick start
The simplest possible workflow — one step, no dependencies. Once you understand the submit-and-poll pattern, multi-step pipelines work exactly the same way.
// Call submit_workflow with your BWDL definition
const result = await callTool("submit_workflow", {
definition: {
workflow_id: "my-first-workflow-001",
steps: [
{
id: "transcode",
tool: "transcode_from_url",
inputs: {
source_url: "https://example.com/video.mp4",
output_format: "mp4"
}
}
]
}
});
// Returns immediately
// { workflow_id: "my-first-workflow-001", status: "PROCESSING", step_count: 1 }let status;
do {
await sleep(5000);
status = await callTool("get_workflow_status", {
workflow_id: "my-first-workflow-001"
});
} while (!["COMPLETED", "FAILED", "PARTIALLY_FAILED", "CANCELLED"].includes(status.status));
// Terminal — inspect step outputs
const step = status.steps.find(s => s.step_id === "transcode");
console.log(step.status); // "COMPLETED"
console.log(step.output_url); // pre-signed download URL
console.log(step.cost_usd); // e.g. 0.001BWDL schema reference
A BWDL document is a JSON object. The $schema field is optional but enables validation in editors that support JSON Schema.
Top-level fields
Step fields
Available tools
The tool field must be one of these values:
Transcode output_format values: mp4 webm mov_prores mp3 gif. Convert output_format values: docx pdf html txt md rst xlsx.
Expression syntax
Step inputs support JSONPath-style expressions that are resolved at dispatch time. Any input value that starts with $. is treated as an expression; all other values are used as-is.
$.steps.{id}.output_key as the value of a source_url input, the engine automatically substitutes the pre-signed download URL for that step — you don't need to handle URL generation yourself.Parallelism and dependencies
Steps with no depends_on (or an empty array) are dispatched immediately when the workflow starts. Steps that list one or more dependencies wait until every listed step reaches COMPLETED before they dispatch. This makes fan-out and fan-in natural to express.
A step can depend on multiple steps — it will wait until all of them complete (fan-in). Circular dependencies are detected at submit time and rejected with a CIRCULAR_DEPENDENCY error.
Conditional steps
A step with a when field is only dispatched if the condition evaluates to true. If it evaluates to false, the step is marked SKIPPED — it does not block downstream steps that depend on it.
Supported condition syntax:
{
"id": "prores_master",
"tool": "transcode_from_url",
"depends_on": ["ingest"],
"when": "$.params.include_prores == 'true'",
"inputs": {
"source_url": "$.steps.ingest.output_key",
"output_format": "mov_prores"
}
}Failure modes
Each step can declare how the workflow behaves if that step fails. The default is HALT.
{
"id": "upload_to_cdn",
"tool": "transcode_from_url",
"failure_mode": "COMPENSATE",
"on_failure_compensate": "cleanup",
"inputs": { "source_url": "$.params.source_url", "output_format": "mp4" }
},
{
"id": "cleanup",
"tool": "convert_content",
"inputs": {
"content": "Cleanup triggered for $.params.job_ref",
"input_format": "txt",
"output_format": "txt"
}
}Retry policy
"retry": {
"max_attempts": 3, // 1–10. Counts the original attempt — so 3 = 2 retries.
"initial_interval_seconds": 5,// Wait before first retry. Default: 1.
"backoff_coefficient": 2.0, // Multiply interval on each retry. Default: 1 (no backoff).
"retryable_errors": [ // Optional — only retry these error substrings.
"FETCH_FAILED",
"TIMEOUT"
]
}Status values
Workflow statuses
Step statuses
MCP tool reference
submit_workflowValidate and submit a BWDL workflow definition. Returns immediately — the workflow runs server-side. Submitting the same workflow_id twice is idempotent: the existing workflow is returned without re-running.
| Name | Type | Req | Description |
|---|---|---|---|
| definition | object | yes | BWDL workflow definition. Must include workflow_id (string) and steps (array). Each step requires id, tool, and inputs. |
| Field | Type | Description |
|---|---|---|
| workflow_id | string | The workflow_id from your definition. Pass this to get_workflow_status. |
| status | string | Initial status — always PROCESSING on a new submission. |
| step_count | number | Number of steps in the workflow. |
| already_exists | boolean | true if this workflow_id was already submitted. |
get_workflow_statusAdvance and return the current state of a workflow. Each call checks in-progress steps, dispatches newly ready steps, and applies failure policies. Poll every 5–10 seconds until a terminal status is returned.
| Name | Type | Req | Description |
|---|---|---|---|
| workflow_id | string | yes | Workflow ID returned by submit_workflow. |
| Field | Type | Description |
|---|---|---|
| workflow_id | string | Echoed back. |
| status | string | Current workflow status. |
| total_cost_usd | number | Cumulative cost of all completed steps. |
| error_message | string | Set when the workflow reaches FAILED. |
| completed_at | string | ISO timestamp when the workflow reached a terminal state. |
| steps | array | Array of step state objects — see fields below. |
Each object in steps contains:
cancel_workflowCancel an in-progress workflow. All QUEUED and DISPATCHED steps are immediately marked CANCELLED. Has no effect on already-terminal workflows — calling it on a COMPLETED or FAILED workflow returns an error.
| Name | Type | Req | Description |
|---|---|---|---|
| workflow_id | string | yes | Workflow ID to cancel. |
| Field | Type | Description |
|---|---|---|
| workflow_id | string | Echoed back. |
| cancelled | boolean | Always true on success. |
| status | string | Always CANCELLED on success. |
Error codes
Validation errors from submit_workflow include a machine-readable codein the JSON-RPC error object at error.data.code.
Complete example — multi-rendition pipeline
This pipeline takes a source URL from params, ingests it as an MP4, then fans out to three parallel renditions: H.264 MP4 for web, VP9 WebM for modern browsers, and an MP3 audio track. The three output steps all depend on the ingest step and run concurrently.
{
"$schema": "https://botverse.cloud/schemas/workflow/v1.json",
"workflow_id": "multi-rendition-20240610-001",
"version": "1.0",
"description": "Transcode one source URL into MP4, WebM, and MP3",
"params": {
"source_url": "https://example.com/video.mp4"
},
"steps": [
{
"id": "ingest",
"tool": "transcode_from_url",
"description": "Fetch and store the source file as baseline MP4",
"inputs": {
"source_url": "$.params.source_url",
"output_format": "mp4"
}
},
{
"id": "mp4",
"tool": "transcode_from_url",
"description": "H.264 MP4 for web delivery",
"depends_on": ["ingest"],
"inputs": {
"source_url": "$.steps.ingest.output_key",
"output_format": "mp4"
},
"retry": { "max_attempts": 2 }
},
{
"id": "webm",
"tool": "transcode_from_url",
"description": "VP9 WebM for modern browsers",
"depends_on": ["ingest"],
"failure_mode": "SKIP_DEPENDENTS",
"inputs": {
"source_url": "$.steps.ingest.output_key",
"output_format": "webm"
}
},
{
"id": "audio",
"tool": "transcode_from_url",
"description": "MP3 audio track",
"depends_on": ["ingest"],
"failure_mode": "CONTINUE",
"inputs": {
"source_url": "$.steps.ingest.output_key",
"output_format": "mp3"
}
}
]
}Submitting and polling this workflow
// Submit
const { workflow_id } = await callTool("submit_workflow", { definition });
// Poll
let result;
do {
await new Promise(r => setTimeout(r, 5000));
result = await callTool("get_workflow_status", { workflow_id });
} while (!["COMPLETED","FAILED","PARTIALLY_FAILED","CANCELLED"].includes(result.status));
// Collect outputs
for (const step of result.steps) {
console.log(step.step_id, step.status, step.output_url);
}
// mp4 COMPLETED https://storage.botverse.cloud/...?X-Amz-...
// webm COMPLETED https://storage.botverse.cloud/...?X-Amz-...
// audio COMPLETED https://storage.botverse.cloud/...?X-Amz-...output_url are valid for 1 hour. Download and store the files to long-term storage before the URL expires.