CLI
The CLI ships as @actuate/cli and installs the actuate command. Projects created with npm create actuate list it as a dev dependency, so run it with npx actuate or through the package.json scripts (npm run dev, npm run check, npm run deploy). It needs Node.js 22 or later (its wrangler dependency does).
actuate init [dir] [--template <name>] [--ref <git-ref>] Create a new actuator projectactuate dev [file] [--port 8787] Run locally on workerdactuate check [file] Lint for replay-safetyactuate deploy [file] [--no-config] [--dry-run] Deploy to Actuateactuate run '<json>' [--local] Start a run with this inputactuate logs [runId] [--local] List recent runs, or show one runactuate login [--token <t>] [--api <url>] Save an API tokenactuate providers List providers and their variables[file] defaults to actuator.ts. actuate --help prints the list.
actuate init
Section titled “actuate init”actuate init [dir] [--template <name>] [--ref <git-ref>]Creates a project in dir (default: the current directory), which must be empty apart from .git. Without --template (-t) it writes the built-in hello starter; with one, it downloads actuator.ts, and README.md, .env.example and actuate.config.json when present, from the folder of that name in actuate-run/templates. --ref picks a branch or tag of that repository (default main).
The project contains actuator.ts, actuate.config.json ({}), package.json, tsconfig.json and a .gitignore that excludes node_modules/, .actuate/, .dev.vars and .env.
npm create actuate [dir] -- [--template <name>] [--ref <git-ref>] does the same without installing the CLI first. Its default directory is my-actuator, and --ref picks a branch or tag of the templates repository (default main).
actuate dev
Section titled “actuate dev”actuate dev [file] [--port 8787]Runs the actuator locally on workerd (through wrangler) with the same step engine, a real SQLite-backed Durable Object and real alarms.
- Runs
actuate checkfirst and prints any problems, without stopping. - Bundles the file and rebuilds on every save.
- Serves the runtime HTTP API at
http://localhost:<port>. - Configuration:
actuate.config.json, passed asACTUATE_CONFIG. - Environment: variables from
.envand then.dev.varsin the project directory.actuate devsetsACTUATE_DEV=1, so the admin routes and/webhookwork without secrets locally: onlocalhostonly, and never for a request a web page makes (those get403, so a site you have open can’t start runs with your real providers). SetACTUATE_TOKENorACTUATE_WEBHOOK_SECRETthere to require them anyway, or to use those routes through a tunnel. - Providers: pick real ones in
.dev.vars(seeactuate providers). Anything you don’t pick stays local: messages (and approval links) print in the terminal, and the payment provider is a fake whose bill URL ishttp://localhost:<port>/events/payment.<referenceId>; open it to “pay”. Its refunds always succeed and are printed. Declaredhttpuses yourhttp.allowlist for real.ctx.llmneedsACTUATE_LLMset; otherwise calls fail with “no llm provider is configured”. - Payment webhooks: real payment providers call back to
POST /providers/<kind>/webhook. Expose your local port with a tunnel (e.g.cloudflared tunnel --url localhost:8787) and setACTUATE_PUBLIC_URLto the tunnel URL so callback links point there. Provider webhooks and approval links work through the tunnel; the admin API stays local unless you setACTUATE_TOKEN. - Cron schedules are registered. Fire one with
curl 'localhost:8787/__scheduled?cron=<expression>'. - State persists in
.actuate/statebetween restarts. Delete the folder to start fresh.
Configuration and environment files are read at startup; restart actuate dev after changing them.
actuate providers
Section titled “actuate providers”actuate providersLists every provider and the variables it reads. Choose one per capability in .dev.vars, then add its credentials:
# .dev.vars (never commit this file)ACTUATE_MESSAGING=telegramTELEGRAM_BOT_TOKEN=123456:ABC...# Only to receive messages: any random string, also passed to setWebhookTELEGRAM_WEBHOOK_SECRET_TOKEN=...
ACTUATE_PAYMENT=billplzBILLPLZ_API_KEY=...BILLPLZ_COLLECTION_ID=...BILLPLZ_X_SIGNATURE_KEY=...
ACTUATE_LLM=anthropicANTHROPIC_API_KEY=...| Capability | Choose with | Providers |
|---|---|---|
messaging |
ACTUATE_MESSAGING |
telegram, whatsapp, slack, discord, resend |
payment |
ACTUATE_PAYMENT |
stripe, toyyibpay, billplz, chip |
llm |
ACTUATE_LLM |
anthropic, openai, google, openai-compatible |
Credential variable names are <KIND>_<FIELD>, e.g. TELEGRAM_BOT_TOKEN, BILLPLZ_X_SIGNATURE_KEY. On the hosted platform you set providers in the dashboard instead (Settings → Providers).
actuate check
Section titled “actuate check”actuate check [file]A replay-safety linter. run is replayed from the top after every pause, so side effects and non-determinism must sit inside ctx.step.do. check parses the file and inspects the run function of the object passed to defineActuator, flagging these outside a step:
| Rule | Severity | Flags |
|---|---|---|
no-fetch-outside-step |
error | fetch() and globalThis.fetch() |
no-provider-outside-step |
error | Calls on ctx.messaging, ctx.payment, ctx.llm or ctx.http |
no-nondeterminism-outside-step |
error | Date.now(), new Date() with no arguments, Math.random(), crypto.randomUUID(), performance.now() |
db-write-outside-step |
warning | ctx.db.query, ctx.db.first or ctx.db.sql`...` whose SQL literal starts with INSERT, UPDATE, DELETE or REPLACE |
Everything inside the function passed as the last argument of ctx.step.do counts as inside a step.
actuator.ts:14:5 error ctx.messaging.send() must be called inside ctx.step.do. (no-provider-outside-step)actuator.ts:20:5 warn This write runs again on every replay. Put it inside ctx.step.do. (db-write-outside-step)
1 error(s), 1 warning(s)check exits with status 1 when there are errors, so you can run it in a pre-commit hook. It is a syntactic check of one file: it does not follow calls into helper functions, and it only recognises SQL written as a string literal. The runtime still enforces the provider rule at run time.
actuate deploy
Section titled “actuate deploy”actuate deploy [file] [--no-config] [--dry-run]Deploys the actuator to Actuate.
- Runs
actuate checkand stops if there are errors. - Bundles the file into a single ES module, with the SDK and Zod included, and hashes it. Whitespace and syntax are minified but names are kept, so stack traces stay readable, and Zod’s non-English locales are left out.
- Reads the actuator’s metadata: name, description, capabilities,
http.allow, cron schedules, and the input, config and output schemas as JSON Schema. - Uploads the bundle with
actuate.config.jsonas the configuration, unless you pass--no-config. - Prints the version number and the actuator’s webhook, MCP, OpenAPI and dashboard URLs, plus any warnings from the platform.
--dry-run stops after step 3 and prints the metadata without uploading. Requires actuate login or ACTUATE_API_TOKEN.
actuate run
Section titled “actuate run”actuate run '<json>' [--local] [--port 8787] [--name <actuator>]Starts a run with the given JSON input (default {}) and prints the run record. Quote the JSON in single quotes. Input that fails the schema prints each problem, e.g. name: Too small: expected string to have >=1 characters. With --local it targets actuate dev on localhost:<port>; otherwise the deployed actuator, whose name is read from name: in actuator.ts unless you pass --name.
actuate logs
Section titled “actuate logs”actuate logs [runId] [--local] [--port 8787] [--name <actuator>]Without a run ID, lists the 20 most recent runs: ID, status, start time and trigger. With one, prints the run’s status, each step in the journal with its status and error, the log lines, and the run’s error if it failed. A log line’s data is printed as JSON on the line below it:
run_01a1998e44009c41f2e07b3d5a18 WAITING step WAITING_APPROVAL manager-ok 2026-11-02T00:00:00.000Z info Approval requested for "manager-ok" {"step":"manager-ok","expiresAt":1793664000000}Approval links are never in the logs; see Human approvals.
--local and --name work as for actuate run.
actuate login
Section titled “actuate login”actuate login [--token <token>] [--api <url>]Saves an API token for deploy, run and logs. Without --token it prompts for one; create it in the dashboard under Settings → API tokens, choosing Full. You sign in to the dashboard with Google or another social login; the CLI and agents use API tokens instead.
There are two kinds of token:
| Token | For | Can |
|---|---|---|
| Agent (run-scoped; the default for new tokens) | Agents and MCP clients | Start and read runs on run.actuate.run. Nothing else: no approving, posting events, deploying, configuration or management |
| Full | The CLI and management | Everything your account can do, including deploy and fetching approval links |
actuate login needs a full token. Give agents Agent tokens. --api overrides the API URL (default https://api.actuate.run).
Credentials are written to $XDG_CONFIG_HOME/actuate/credentials.json, or ~/.config/actuate/credentials.json, with mode 0600.
Environment variables
Section titled “Environment variables”| Variable | Effect |
|---|---|
ACTUATE_API_TOKEN |
A full API token. Takes precedence over the saved credentials; useful in CI |
ACTUATE_API_URL |
API URL. Overrides the saved or default URL (https://api.actuate.run) |
XDG_CONFIG_HOME |
Where the credentials file is stored |