Code actions let you write TypeScript that runs inside Simple AI when an agent invokes the action — no server to deploy or host. Your code executes in a fresh, isolated Cloudflare Worker with the Simple AI SDK, the OpenAI SDK, Zod, and libphonenumber-js bundled. The Simple AI SDK is authenticated for your organization automatically.
Use a code action when the logic is more than one HTTP call — combining lookups, applying business rules, or reading and writing custom data — but you don’t want to run your own middleware server (for that, see Custom Code).
Writing a Code Action
Create an Action in the dashboard and choose the Code backend. Default-export a function; defineAction from the SDK adds typing and an authenticated client:
Your function receives:
inputs — the Action’s declared inputs, already validated against its input schema
config — your organization’s configuration variables (for API keys and settings)
client — the Simple AI SDK, authenticated for your organization automatically; you never handle an API key
The value you return maps to the Action’s declared outputs through output mappings rooted at result — for example {{ result.plan }}.
Authentication
Use Authentication to set up managed OAuth connections, store credentials securely, and authenticate requests with API keys, bearer tokens, or other methods. Choose an OAuth connection explicitly on each fetch that needs authentication. The guide explains that per-request choice, secure credential storage, and managed token renewal.
Steering the conversation
A code action invoked from a workflow can decide where the conversation goes next. Call workflow.transitionTo with the name of the node to run:
nodeName — the node to run next, exactly as it is named in the Agent. Any node in the workflow is reachable; you do not need an edge from the node that invoked the action.
message — optional. A short sentence the agent speaks as it moves.
reason — optional. Recorded with the transition on the call.
transitionTo returns normally: it records where to go next, and the rest of your handler still runs. A transition comes in addition to the result, so the example above both maps plan to its outputs and moves the conversation. Calling it more than once keeps the last request, and an action that throws transitions nowhere. If the named node does not exist in the workflow, the transition is logged and ignored, and the agent follows its own edges instead.
Capabilities
Code actions run in more places than workflows — per-turn actions, automatic before/after-call actions, and data sync all execute the same code, but there is no live conversation to steer. Each invocation states what it allows, and your handler reads it as metadata.capabilities:
WORKFLOW_TRANSITION is granted when the action runs from a workflow — as an action node or an agent tool call. Calling transitionTo without it fails the action with a MissingCapabilityError, so check metadata.capabilities first if the same action also runs outside workflows.
Calling an LLM
A code action can call an LLM through Simple AI’s LLM router with llm.infer, without an LLM provider key of its own. It takes an OpenAI Responses API request body:
model — a router ladder slug, not a vendor model ID. Ask Simple AI support which slugs to use.
- Everything else is an ordinary Responses API body, such as
input, instructions, text, or temperature. Requests are never streamed.
- The response comes back with its
output typed, as above; other Responses API fields are there untyped.
- If the router refuses the request,
infer throws SimpleAIError with its HTTP status, code, and message.
LLM calls are available in code actions during conversations, automatic before/after-call actions, data sync, and editor test runs. Authentication is provided automatically.
Rules and Limits
- TypeScript or JavaScript in the Cloudflare Workers runtime. Use standard web APIs such as
fetch, Request, and Response; Node.js APIs are not available.
- The importable packages are
@simple-ai-lab/sdk, openai, zod, and libphonenumber-js. They are bundled when you save.
libphonenumber-js parses, validates, and formats phone numbers (for example to E.164 before dialing). The default entry point carries minimal metadata; import libphonenumber-js/max when you need getType() or other extended-metadata lookups.
- The Simple AI SDK is authenticated automatically. To call an LLM, use
llm.infer, or store your own OpenAI API key in an organization configuration variable and pass it to the OpenAI client from config.
- Timeout: configurable per action, 1–60 seconds (default 30). Voice conversations feel best well under 10 seconds — configure filler speech on the agent for longer actions.
- Network requests: up to 100 subrequests per execution.
- Result size: the returned value must serialize to JSON of at most 1 MB.
- Logs:
console.log / console.error output is captured and shown in test runs and call transcripts — it never pollutes the result.
- Each execution runs in a fresh Worker isolate; nothing persists between runs. Persist state in custom data instead.
Testing
The Action editor’s test panel runs your code in a real Worker isolate: fill in sample inputs, press Run test, and inspect the returned value (click fields to map them as outputs), stdout/stderr logs, and execution timings. Syntax errors are rejected when you save; runtime errors surface in the test result with the thrown error’s name and message.
A test run supports llm.infer. It has no conversation attached, so workflow.transitionTo fails with MissingCapabilityError. Guard workflow transitions with a metadata.capabilities check to keep the action testable from the editor, and exercise transitions on a test call. Last modified on October 2, 2026