Skip to content
Workflow Agents

Workflow Agents

Create, test, publish and run a fixed JavaScript workflow through the console or API.

On this pageCreate in the consoleCreate and publish through the APITypeScript extensionVersions and updatesProgram and toolsRuns and client function resultsStreaming, cancellation and recoveryAPI referenceGenerate through the APIWebhook notifications

A Workflow Agent executes a saved JavaScript program directly. It accepts JSON input, streams progress and returns a JSON result. It has no outer model deciding what to do next. Your code can still call tools, including an MCP tool that calls another model.

Dynamic Workflow generates a temporary program during a Managed Agent conversation. A Workflow Agent is a separate, versioned resource with an explicit test and publication lifecycle.

Create in the console

Open Workflow Agents and select Create Workflow Agent.

  1. Describe the job and select Generate draft, or write JavaScript directly. Review the code, input schema and tool configuration. Rebyte's shared Workflow Builder Managed Agent generates code; generation is a platform-funded feature.
  2. Optionally preview unsaved code, then select Create draft to save version 1.
  3. Select Test version with example input. Inspect streamed progress and the final result. Testing uses real tools and can perform real actions.
  4. Select Publish version after the test succeeds. External API requests now execute this fixed version.
  5. Use Edit as new version to make changes. New drafts do not affect the published version. Test and publish the new version when ready.

A successful test covers the input you tested; it does not prove correctness for all inputs. Publication is organization-scoped, not public anonymous access. The API enforces the same test requirement as the console.

Create and publish through the API

Use an organization API key with tasks:read and tasks:write. The base is https://api.rebyte.ai/v1/workflow-agents. No Agents beta header is required. This API is separate from /v1/agents; existing SDK Agent methods do not create Workflow Agents. Use the TypeScript extension or HTTP, as in this complete Node.js example:

js
const base = 'https://api.rebyte.ai/v1/workflow-agents';
const key = process.env.REBYTE_API_KEY;
if (!key) throw new Error('Set REBYTE_API_KEY');
async function post(path, body) {
  const response = await fetch(base + path, {
    method: 'POST',
    headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  const value = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(value));
  return value;
}
async function waitForRun(run) {
  while (['preparing', 'in_progress'].includes(run.status)) {
    await new Promise(resolve => setTimeout(resolve, 500));
    const response = await fetch(`${base}/runs/${run.id}`, {
      headers: { Authorization: `Bearer ${key}` },
    });
    if (!response.ok) throw new Error(await response.text());
    run = await response.json();
  }
  // Programs with application functions need the handoff described below.
  if (run.status !== 'completed') throw new Error(JSON.stringify(run));
  return run;
}
const agent = await post('', {
  name: 'Order total',
  code: `async (input, emit) => {
    await emit({ phase: 'calculating' });
    return { total: input.quantity * input.price };
  }`,
  input_schema: {
    type: 'object',
    properties: { quantity: { type: 'number' }, price: { type: 'number' } },
    required: ['quantity', 'price'],
    additionalProperties: false,
  },
});
// agent.latest_version === 1; agent.published_version === null
const test = await waitForRun(await post(`/${agent.id}/test`, {
  version: 1, input: { quantity: 3, price: 7 },
}));
if (test.status !== 'completed' || test.result.total !== 21)
  throw new Error(JSON.stringify(test));
await post(`/${agent.id}/publish`, { version: 1, test_run_id: test.id });
const run = await waitForRun(await post(`/${agent.id}/runs`, {
  input: { quantity: 4, price: 7 },
}));
if (run.status !== 'completed') throw new Error(JSON.stringify(run));
console.log(agent.id, run.result); // { total: 28 }

The example creates a persistent Agent and two run records in your organization. Delete resources you no longer need using the endpoints below.

/runs defaults to published_version. An explicit version must have been published before. /test requires an explicit version and permits drafts. Unpublished execution returns 409 not_published. Publication requires a successful, non-deleted test of that exact Agent/version in the same organization. An unsaved /preview run does not satisfy that requirement.

TypeScript extension

Install the official client and the optional Rebyte extension:

Terminal
pnpm add openai@7.15.0 @rebyteai/agent-extensions@0.3.0

The current npm version is 0.3.0. Workflow function handoffs and the runs.submitToolResult() SDK method require the 0.4.0 source while npm publication is pending. Build the SDK repository to run those examples now. The HTTP API below is already available.

ts
import OpenAI from 'openai';
import { RebyteExtensions } from '@rebyteai/agent-extensions';

const client = new OpenAI({
  apiKey: process.env.REBYTE_API_KEY,
  baseURL: 'https://api.rebyte.ai/v1',
  maxRetries: 0,
});
const rebyte = new RebyteExtensions(client);
const workflows = await rebyte.workflowAgents.list();
for await (const agent of workflows) console.log(agent.id, agent.name);

Use rebyte.workflowAgents.create, .test, .publish and .runs.create for the lifecycle above. Run creation is asynchronous: retrieve the run before publishing or consuming its final result. Use the HTTP protocol below for new client-function handoffs if your installed extension does not expose them. Standard Agent APIs remain on client.beta.agents. The complete extension examples cover typed execution, streaming, version pagination and cleanup.

Versions and updates

Source and execution configuration are immutable within a version. To create a new draft while retaining its private tool configuration:

http
POST /v1/workflow-agents/wfa_.../versions
Content-Type: application/json
Authorization: Bearer <organization-api-key>

{
  "base_version": 1,
  "code": "async input => ({total: input.quantity * input.price, currency: 'USD'})",
  "input_schema": {
    "type": "object",
    "properties": {"quantity": {"type": "number"}, "price": {"type": "number"}},
    "required": ["quantity", "price"],
    "additionalProperties": false
  }
}

Alternatively, submit a full definition to /versions with code, input_schema, tools, environment and vault_ids. Omitted configuration is initialized to empty tools/vaults and environment: {type: "none"}; it does not inherit. Public read responses omit connection secrets and private environment setup.

Test version 2, then publish it using its successful test run ID. Until that explicit publication, callers continue using version 1. To roll back, test and publish an earlier version. Existing runs retain the version selected when they started. Publication updates a pointer; it does not regenerate or execute code.

Program and tools

Use a function expression, normally async (input, emit) => { ... }. Return JSON-compatible data and use await emit(value) for progress. Input must match input_schema; validation does not coerce values or insert defaults. Admission checks source syntax; the isolated runtime determines whether it is callable and whether its logic succeeds. Invalid code is not automatically repaired or retried.

Each invocation runs in an isolated JavaScript environment. JavaScript cannot access Node.js, the filesystem, subprocesses or the network directly. The configured server tools are available as tools.NAME(arguments) through the same Tool Registry used by Managed Agents. Customer credentials stay in the service that owns the tool; they are not embedded in program source.

Supported tool configuration includes saved MCP servers, platform connections and web search. An openai_hosted environment exposes command, stdin, patch and image tools. Application functions are supported through the handoff below. Deferred function discovery and nested run_code are not supported here. Custom functions and calls to other language models can be exposed through MCP. For example, with a configured DeepWiki server:

js
async () => {
  await tools.search_tools({server: 'deepwiki', query: 'read_wiki_structure'});
  const result = await tools.call_tool({
    server: 'deepwiki', name: 'read_wiki_structure',
    arguments: {repoName: 'modelcontextprotocol/python-sdk'},
  });
  if (result.error !== null) throw new Error(JSON.stringify(result.error));
  return result.output;
}

Its tools entry is:

json
{"type":"mcp","server_label":"deepwiki","connection_origin":"service","required":true,"allowed_tools":["read_wiki_structure"],"transport":{"type":"http","server_url":"https://mcp.deepwiki.com/mcp"}}

Runs and client function results

/test, /:id/runs and /preview return as soon as the run is admitted. HTTP 201 means a new run was created; an identical idempotent retry returns 200. Neither response means the program has completed. Poll /runs/:runId, or follow its events, until the run reaches a terminal status.

Run statusApplication behavior
preparingTool/environment preparation is in progress
in_progressThe program is executing
requires_actionExecute the function in required_actions and submit its result
completedRead result
failedRead error; review completed effects before retrying
cancelledExecution has stopped after a cancellation request

Configure an application function in the definition's tools array:

json
{
  "type": "function",
  "name": "get_number",
  "description": "Look up the account's number in the application",
  "parameters": {
    "type": "object",
    "properties": {"account": {"type": "string"}},
    "required": ["account"],
    "additionalProperties": false
  }
}

Its program can call it directly:

js
async (input, emit) => {
  await emit({ phase: 'waiting_for_number' });
  const number = await tools.get_number({ account: input.account });
  return { doubled: number * 2 };
}

A waiting run exposes:

json
{
  "status": "requires_action",
  "required_actions": [{
    "type": "function_call",
    "call_id": "call_...",
    "name": "get_number",
    "arguments": {"account": "account-123"}
  }],
  "expires_at": 1790553600000
}

expires_at is the current waiting deadline in Unix milliseconds, or null when no function result is awaited. created_at and completed_at use Unix seconds. required_actions is empty outside a pending handoff. A pending call can wait up to 24 hours; each later call gets a new deadline. Expiration fails the run.

Submit one result to the Workflow run, using its exact call ID:

http
POST /v1/workflow-agents/runs/wfr_.../tool-results
Authorization: Bearer <organization-api-key>
Content-Type: application/json

{"call_id":"call_...","success":true,"output":21}

output accepts JSON, including null. For failure submit {"call_id":"call_...","success":false,"error":"Account unavailable"}. The program can catch the error; otherwise the run fails. A result submission returns HTTP 200 with the current run, which may still be progressing. Poll or follow events for the next handoff or terminal outcome. An identical accepted result retry is acknowledged; a conflicting, stale or expired result returns 409 invalid_tool_result. The endpoint does not require an Idempotency-Key.

Functions must have unique names matching [A-Za-z_][A-Za-z0-9_]*, outside the reserved __rebyte_ prefix, and cannot shadow configured server tools. Fixed-code functions cannot use defer_loading: true. At most 64 callable tools are allowed.

Streaming, cancellation and recovery

Set stream: true on /test, /runs or /preview for Server-Sent Events. Run snapshots contain required_actions, ordered outputs emitted by the program, and the recorded calls. Events include workflow.run.created, .started, .output, .requires_action, .tool_result, and terminal .completed, .failed, .cancelled. Terminal events carry the final run. The SSE id: is the event sequence.

Run events are persisted. GET /runs/:runId/events?after=<sequence> replays and follows them. Reconnecting does not create another run. Both the initial SSE request and event-only subscriptions can disconnect without stopping execution. The console's run history lets you reopen a run and answer a waiting function.

To stop a run, POST {} to /runs/:runId/cancel. Cancellation is asynchronous; read or follow the run until it is terminal. It does not undo completed tool side effects. Deletion is refused while a run is active.

Use Idempotency-Key on preview, test and execution requests. An identical retry returns the original run, even if the default published version changed. Different input under that key, or a deleted run, returns 409. The key is scoped to your organization across those run-creation endpoints.

Waiting survives service restarts. On continuation, completed tool calls and emit outputs retain their results without being repeated. JavaScript computation may replay to reach the pending call. Await tools sequentially; parallel tool calls are unsupported. Capture randomness or current time used in arguments or branches with await codemode.step(name, fn), using a stable name and a JSON-compatible result. The Dynamic Workflow recovery rules apply to fixed programs too.

Each active code segment, before completion or the next pause, is bounded by 60 seconds. Client waiting is outside this bound. There is no separate fixed total run lifetime. A scheduled run is also bounded by its Schedule's total timeout_seconds. A failed or ambiguous active execution is not automatically started again; review completed effects before deliberately creating another run.

Source is limited to 32,768 JavaScript string code units. Input, client result submissions and the accumulated emit outputs are limited to 512 KiB. Resource limits still apply while code is executing. Code admission does not guarantee that arbitrary programs will run successfully.

API reference

All paths below are relative to /v1/workflow-agents.

MethodPathPurpose
POST/Create Agent and draft version 1
GET/List Agents; limit, after
GET / DELETE/:idRead / delete Agent
POST/:id/versionsAppend an immutable draft
GET/:id/versionsList versions; limit, before
GET/:id/versions/:versionRead a version
POST/:id/testTest {version, input, stream?}
POST/:id/publishPublish {version, test_run_id}
POST/:id/runsExecute {input, version?, stream?}
POST/previewExecute an unsaved definition plus input, stream?
POST/generateGenerate or revise a draft
GET/runsList runs; limit, after, optional agent_id
GET / DELETE/runs/:runIdRead / delete a terminal run and its environment
GET/runs/:runId/eventsReplay/follow events; after sequence
POST/runs/:runId/tool-resultsSubmit one application function result
POST/runs/:runId/cancelRequest cancellation with {}

Deleting an Agent prevents new use; existing run records remain. Explicitly delete terminal runs to clean up their tool environments. Agent and run access is organization-scoped. UI management requires organization administrator access.

Generate through the API

POST /generate with prompt and optional tools, environment, vault_ids. The non-streaming response is {draft: {name, summary, code, input_schema, input}}. For revision include that draft and optionally preview_error. With stream: true, events are generation.started, .delta, .completed; errors use an error event. Generation does not execute customer tools or publish.

Rebyte's official Managed Agent owns generation Sessions and model charges. Saved Workflow Agents, tests and execution belong to your organization. The Agent receives tool descriptions rather than connection credentials. No authoring service API key is exposed to the client.

Webhook notifications

Subscribe to workflow.run.action_required for application function handoffs, and to .completed, .failed, or .cancelled for final state changes. Retrieve the Run using data.id; notifications contain no call arguments or result payload. Acknowledging a webhook does not submit a tool result. See Webhooks for registration, signing and retries.