Skip to main content

Execute Workflow

Use WorkflowClient to trigger, stream, and inspect workflow runs. You need a Workspace API key (ws_...) and the UUID of the workflow to trigger. Get both from Dashboard → Workspace Settings → API Keys.


Setup​

import { WorkflowClient } from 'caller-sdk';

const workflow = new WorkflowClient({
apiKey: process.env.WR_API_KEY!, // ws_...
workflowId: process.env.WR_WORKFLOW_ID!, // UUID of your workflow
});

Trigger a run​

const { runId, jobId } = await workflow.trigger();
console.log('Run started:', runId);

Returns immediately with a runId. The workflow starts executing asynchronously.


Wait for completion​

The simplest way to get the final result — opens an SSE stream and resolves once the run finishes:

const run = await workflow.waitForRun(runId);

if (run.status === 'COMPLETED') {
console.log(`Done! Credits used: ${run.totalUsage}`);
for (const stage of run.runStages) {
console.log(` ${stage.module}: ${stage.status}`, stage.output);
}
} else if (run.status === 'FAILED') {
const failed = run.runStages.filter(s => s.status === 'FAILED');
console.error('Failed stages:', failed.map(s => s.module));
}

Timeout​

waitForRun has a configurable timeout (default 5 minutes):

const run = await workflow.waitForRun(runId, 120_000); // 2 minutes

Stream live progress​

Use stream when you want per-stage updates as the workflow executes:

const sub = workflow.stream(runId, {
onUpdate(event) {
const pending = event.output.pendingStageCount ?? 0;
const total = event.output.totalUsage ?? 0;
console.log(`${event.status} | pending: ${pending} | credits: ${total}`);

// Currently active stages
for (const stage of event.output.nonTerminalStages ?? []) {
console.log(` → ${stage.module} (attempt ${stage.executionAttempt})`);
}
},
onError(err) {
console.error('Stream error:', err.message);
},
});

// Stream closes automatically on COMPLETED / FAILED / CANCELED
// Call sub.close() to unsubscribe early

Get run details​

Fetch the full run state at any time, including all stage outputs:

const run = await workflow.execution.get(runId);

console.log(run.status); // 'COMPLETED'
console.log(run.pendingStageCount); // 0
console.log(run.failedStageCount); // 0
console.log(run.totalUsage); // credits used

for (const stage of run.runStages) {
console.log(stage.module, stage.status, stage.output);
}

WorkflowRunDetail shape​

interface WorkflowRunDetail {
id: string;
status: 'CREATED' | 'EXECUTING' | 'COMPLETED' | 'FAILED' | 'CANCELED';
pendingStageCount: number;
failedStageCount: number;
cancelRequestedAt: string | null;
cancelReason: string | null;
totalUsage: number;
createdAt: string;
updatedAt: string;

runStages: Array<{
id: string;
module: string;
status: 'CREATED' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELED';
output: unknown | null;
error: unknown | null;
creditUsage: number;
executionAttempt: number; // 1 = first attempt
updatedAt: string;
}>;
}

Update a workflow component​

Modify a component's config, display name, or canvas position programmatically. Useful for dynamic workflows driven by external parameters:

await workflow.component.update('component-uuid', {
config: { apiUrl: 'https://new-api.example.com/endpoint' },
name: 'My API Call',
});

You can update config, name, and position ([x, y]) independently:

// Move the component on the canvas
await workflow.component.update('component-uuid', {
position: [200, 400],
});

Full example: trigger, stream, and inspect​

import { WorkflowClient } from 'caller-sdk';

const workflow = new WorkflowClient({
apiKey: process.env.WR_API_KEY!,
workflowId: process.env.WR_WORKFLOW_ID!,
});

async function runWorkflow(): Promise<void> {
// 1. Trigger
const { runId } = await workflow.trigger();
console.log('Started run:', runId);

// 2. Stream live updates
await new Promise<void>((resolve, reject) => {
const sub = workflow.stream(runId, {
onUpdate(event) {
const pending = event.output.pendingStageCount ?? 0;
console.log(` ${event.status} — ${pending} stages pending`);

if (event.status === 'COMPLETED') {
console.log(` Total credits: ${event.output.totalUsage}`);
resolve();
}
if (event.status === 'FAILED') {
reject(new Error(`Run failed — ${event.output.failedStageCount} stage(s) failed`));
}
},
onError: reject,
});

void sub; // sub.close() called automatically on terminal status
});

// 3. Inspect stage results
const run = await workflow.execution.get(runId);
for (const stage of run.runStages) {
console.log(` ${stage.module}: ${stage.status}`, stage.output);
}
}

runWorkflow().catch(console.error);

Stage statuses​

StatusMeaning
CREATEDQueued — waiting for upstream dependencies
RUNNINGActively executing on a microservice
COMPLETEDFinished successfully
FAILEDFailed — see error field
CANCELEDRun was canceled before this stage could execute

Error handling​

import { WorkflowClient, CallerSDKError } from 'caller-sdk';

const workflow = new WorkflowClient({
apiKey: process.env.WR_API_KEY!,
workflowId: process.env.WR_WORKFLOW_ID!,
});

try {
const { runId } = await workflow.trigger();
const run = await workflow.waitForRun(runId);
} catch (err) {
if (err instanceof CallerSDKError) {
console.error(err.message); // e.g. "Invalid API key" or "Rate limit exceeded"
console.error(err.details); // API error body
}
if (err instanceof Error && err.message.includes('timed out')) {
console.error('Workflow took longer than the timeout');
}
}