Workflow Agents
Create, test, publish and run a fixed JavaScript workflow through the console or API.
On this page
Create in the consoleCreate and publish through the APITypeScript extensionVersions and updatesProgram and toolsRuns and client function resultsStreaming, cancellation and recoveryAPI referenceGenerate through the APIWebhook notificationsA 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.
- 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.
- Optionally preview unsaved code, then select Create draft to save version 1.
- Select Test version with example input. Inspect streamed progress and the final result. Testing uses real tools and can perform real actions.
- Select Publish version after the test succeeds. External API requests now execute this fixed version.
- 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:
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:
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.
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:
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:
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:
{"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 status | Application behavior |
|---|---|
preparing | Tool/environment preparation is in progress |
in_progress | The program is executing |
requires_action | Execute the function in required_actions and submit its result |
completed | Read result |
failed | Read error; review completed effects before retrying |
cancelled | Execution has stopped after a cancellation request |
Configure an application function in the definition's tools array:
{
"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:
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:
{
"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:
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.
| Method | Path | Purpose |
|---|---|---|
| POST | / | Create Agent and draft version 1 |
| GET | / | List Agents; limit, after |
| GET / DELETE | /:id | Read / delete Agent |
| POST | /:id/versions | Append an immutable draft |
| GET | /:id/versions | List versions; limit, before |
| GET | /:id/versions/:version | Read a version |
| POST | /:id/test | Test {version, input, stream?} |
| POST | /:id/publish | Publish {version, test_run_id} |
| POST | /:id/runs | Execute {input, version?, stream?} |
| POST | /preview | Execute an unsaved definition plus input, stream? |
| POST | /generate | Generate or revise a draft |
| GET | /runs | List runs; limit, after, optional agent_id |
| GET / DELETE | /runs/:runId | Read / delete a terminal run and its environment |
| GET | /runs/:runId/events | Replay/follow events; after sequence |
| POST | /runs/:runId/tool-results | Submit one application function result |
| POST | /runs/:runId/cancel | Request 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.