Create a routine
Compiles and publishes a new routine as version 1. The body is the routine definition, as JSON or as YAML (`Content-Type: application/yaml`). Fails with `routine_invalid` (pointers into the definition) when it does not compile, and with `routine_name_taken` when the name exists; `PUT /v1/routines/{routine}` creates or updates by name instead. Webhook triggers verified with the `b3os` scheme get a signing secret that is returned in this response only.
POST
/v1/routines
Compiles and publishes a new routine as version 1. The body is the routine definition, as JSON or as YAML
(`Content-Type: application/yaml`). Fails with `routine_invalid` (pointers into the definition) when it does
not compile, and with `routine_name_taken` when the name exists; `PUT /v1/routines/{routine}` creates or
updates by name instead. Webhook triggers verified with the `b3os` scheme get a signing secret that is
returned in this response only.
Authentication
Bearer Token (b3os_sk_ API key or b3os_pat_ token) API Key (cookie: __Host-b3os_session)
Header Parameters
Idempotency-Key
string
optional
header
A unique value (for example a UUID) that makes this request safe to retry. Scoped to your project for 24
hours: a retry with the same key, method, path and body returns the stored response with
`Idempotent-Replayed: true`, and only to the same API key. Reusing the key for a different request returns
`409 idempotency_key_conflict`.
Request Body required
application/json$schema
string (uri)
Optional pointer to this schema for editors.
name
string
REQUIRED
Routine slug, unique within the project. Used by the CLI (b3os routine deploy); an MCP server that lists routines as tools names the tool routine_<name> with - mapped to _. runs, hooks, validate and import are reserved: they are API paths under /v1/routines.
description
string
What the routine does; an MCP server that lists routines as tools uses it as the tool description.
inputs
object | boolean & object
JSON Schema 2020-12 for the run input, available as inputs. Defaults declared here are applied before the run starts. Absent: the run takes no input ({}).
output
unknown
The run's result, evaluated once after every step has finished and the run succeeded. Absent: the result is {}. Context: inputs, vars, env, trigger, run, steps (all).
env
object
Non-secret strings evaluated once at run start (context: inputs, vars, trigger, run) and exposed as env.<NAME>. Not passed to sandboxes implicitly; put what a step needs in its with.
vars
object
Read-only JSON constants exposed as vars.<name>. Values are literals; strings in them are not evaluated as templates.
triggers
object[]
How runs start besides POST /v1/routines/{id}/runs. Triggers switch atomically when a new version is published. Action triggers are trigger subscriptions (tsub_): publishing a version creates or updates them, and a rollback or delete removes them (README, 'Pack triggers').
Array of:
type
unknown
REQUIRED
Enum:
cron, interval, webhook, actionid
string
REQUIRED
A CEL identifier (so steps.<id>.outputs parses): lowercase letters, digits and _, not a CEL reserved word.
uses
string
action only: the pack trigger to subscribe to.
with
object
cron, interval, webhook: the run input built from the event (context: trigger, vars), validated against inputs; absent: {} plus input defaults. action: the pack trigger's configuration, validated against its configSchema and evaluated once when the version is published (context: vars only); action runs start with {} plus input defaults and read the event from trigger.data.
connections
object
action only: binds the pack trigger's connection keys (its requires.connections) to vault connections. A key left out is resolved when the version is published (the most recently created connection for that pack the publishing key may use) and pinned in the subscription; when there is none the publish fails with routine_invalid.
if
string
Start a run only when true (context: trigger, vars). A webhook delivery that does not match is acknowledged with 202 and starts nothing; an action event that does not match is dropped. Status functions are not available here.
cron
string
Six fields with seconds: second minute hour day-of-month month day-of-week, single spaces. Numbers, names (JAN, MON), *, a-b, a,b, */n and ? are accepted; L, W and # in the day fields.
tz
string
IANA zone for cron, default UTC. A local time skipped by a DST change fires at the first instant after the gap; a local time that occurs twice fires once, at its first occurrence.
every_ms
integer
Interval period. Plans may raise the floor (GET /v1/limits).
anchor
unknown
interval: epoch fires at Unix-epoch multiples of every_ms (clock aligned); publish fires every_ms after the version became active, then every every_ms.
Enum:
epoch, publishcatch_up
unknown
Occurrences missed while the node was down or the routine was paused: skip runs none, latest runs only the newest one, all runs each one in order. Only occurrences inside catch_up_window_s count. Required on every schedule (ARCHITECTURE 8).
Enum:
skip, latest, allcatch_up_window_s
integer
overlap
unknown
When an occurrence fires while a run started by this trigger is still active: skip drops it, buffer_one keeps at most one pending occurrence (a newer one replaces it), allow starts it anyway, cancel_running cancels the active run first. defaults.concurrency still applies on top.
Enum:
skip, buffer_one, allow, cancel_runningverify
object
Mandatory verification for POST /v1/routines/hooks/{hk_id} (ARCHITECTURE 4.3); the hk_ id is created when the version is published. b3os: B3OS-Signature 't=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")>' with a per-hook secret B3OS generates (shown once by the API). github, stripe, slack, standard_webhooks: the provider's scheme as in b3pack verify. hmac_sha256: generic, and always timestamped: the signature covers '<value of timestamp_header>.<body>', and a delivery whose timestamp header is missing or not a Unix time is refused. Timestamped schemes reject deliveries more than tolerance_s (at most 300 s) from the node clock. Secrets never appear in the URL.
scheme
unknown
REQUIRED
Enum:
b3os, github, stripe, slack, standard_webhooks, hmac_sha256secret
string
An api_key vault connection whose api_key field holds the provider's signing secret (vault.yaml field names). Store it as a verification-only connection, without pack and without hosts: B3OS reads it to check signatures, and a connection with no hosts is never injected into any request. Not used with b3os.
header
string
encoding
unknown
Enum:
hex, base64prefix
string
timestamp_header
string
hmac_sha256 only, and required there: header carrying the delivery's Unix timestamp.
tolerance_s
integer
dedupe
string
webhook: string key; a second delivery with the same key within 24 h returns the first delivery's response and starts nothing. Default per scheme: github X-GitHub-Delivery, stripe body.id, slack body.event_id, standard_webhooks webhook-id, b3os the Idempotency-Key header or else the v1 signature, hmac_sha256 none (then dedupe is required).
defaults
object
durability
unknown
ephemeral: memory only, nothing journaled (the agent tool-call fast path; a crash loses the run). standard: group-commit journal, effects durable before dependents run. strict: intent and result commits for every effect step (ARCHITECTURE 4.9, 5.4). A run may request a different tier at start.
Enum:
ephemeral, standard, stricttimeout_ms
integer
Default timeout for uses and map steps that do not set timeout_ms. Wait and sleep steps are not affected.
retry
object
Default retry policy, applied only to steps whose effect class is pure or read. A write step retries only when it sets retry itself; an irreversible step never retries.
max_attempts
integer
REQUIRED
Total attempts including the first.
initial_ms
integer
factor
number
max_ms
integer
jitter
boolean
concurrency
object
Admission for runs of this routine, shaped like GitHub's concurrency. At most limit runs are active per group; on_limit decides what a new run does when the group is full.
limit
integer
REQUIRED
group
string
Group key evaluated at run start (context: inputs, vars, trigger). Default: one group for the routine.
on_limit
unknown
queue: wait (FIFO) for a free slot. skip: the new run ends canceled with reason concurrency_skipped. cancel_running: cancel the oldest active run in the group, then start.
Enum:
queue, skip, cancel_runningsteps
(unknown | unknown | unknown | unknown)[]
REQUIRED
Array of:
One of:
Option 1unknown
Option 2unknown
Option 3unknown
Option 4unknown
Responses
201
The routine and its first version.
application/jsonid
string
REQUIRED
object
string
REQUIRED
Enum:
routinename
string
REQUIRED
description
string | null
REQUIRED
status
string
REQUIRED
`paused` when triggers are stopped; API runs still work unless `paused_reason` is `run_as_revoked`.
Enum:
active, pausedpaused_reason
string | null
REQUIRED
Enum:
manual, run_as_revoked, nullversion
object
REQUIRED
id
string
REQUIRED
number
integer
REQUIRED
created_at
string (date-time)
REQUIRED
triggers
object[]
REQUIRED
Array of:
id
string
REQUIRED
The trigger's `id` in the definition.
type
string
REQUIRED
Enum:
cron, interval, webhook, actionstatus
string
REQUIRED
Enum:
active, pausednext_fire_at
string (date-time) | null
REQUIRED
Next scheduled fire (cron and interval), or `null`.
hook
object | null
REQUIRED
The inbound URL of a webhook trigger, or `null`.
id
string
REQUIRED
url
string (uri)
REQUIRED
Give this URL to the provider.
verify
string
REQUIRED
Enum:
b3os, github, stripe, slack, standard_webhooks, hmac_sha256secret
string
The `b3os` scheme's signing secret. Present only in the response of the publish that created the hook (and of a rotation).
subscription_id
string | null
REQUIRED
The trigger subscription of an action trigger (`GET /v1/actions/subscriptions/{subscription_id}`), or `null`.
run_as
object
REQUIRED
The identity runs execute as (the key that published the active version, with its scopes at publish time).
key
string | null
REQUIRED
The API key ID; `null` when a dashboard user published.
scopes
string[]
REQUIRED
Array of:
string
created_at
string (date-time)
REQUIRED
updated_at
string (date-time)
REQUIRED
metadata
object
REQUIRED
Up to 16 string key-value pairs you attach to an object for your own use. Keys are 1 to 40 characters of
`A-Z a-z 0-9 _ . : -`; values are at most 500 characters. B3OS never interprets them.
400
The request failed validation (`invalid_request`, `invalid_url`).
401
Missing, malformed, expired or revoked credentials (`unauthorized`).
403
Authenticated but not allowed (`forbidden`, `egress_denied`).
409
The name is in use (`routine_name_taken`), or the `Idempotency-Key` was used for a different request (`idempotency_key_conflict`) or its first request is still being processed (`idempotency_request_in_progress`, with `Retry-After`).
413
The request body exceeds this operation's limit.
415
The Content-Type is not accepted by this operation.
422
The definition does not compile (`routine_invalid`).
429
A rate, concurrency or quota limit applies (`rate_limited`, `concurrency_limited`, `quota_exceeded`).
500
Something failed inside B3OS (`internal`). Retry with backoff.
503
The node or the product is not available right now (`node_unavailable`, `product_unavailable`, `capacity_unavailable`).
curl -X POST 'https://beta-api.b3os.org/v1/routines' \ -H 'Authorization: Bearer YOUR_API_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "name": "nightly-digest", "triggers": [ { "type": "cron", "id": "nightly", "cron": "0 0 6 * * *", "tz": "America/Los_Angeles", "catch_up": "latest" } ], "steps": [ { "id": "page", "uses": "vision/scrape@1", "with": { "url": "https://news.example.com", "formats": [ "markdown" ] } }, { "id": "summary", "uses": "infer/chat@1", "with": { "messages": [ { "role": "user", "content": "Summarize: ${{ steps.page.outputs.markdown }}" } ] } } ]}'
const response = await fetch('https://beta-api.b3os.org/v1/routines', { method: 'POST', headers: { "Authorization": "Bearer YOUR_API_TOKEN", "Content-Type": "application/json" }, body: JSON.stringify({ "name": "nightly-digest", "triggers": [ { "type": "cron", "id": "nightly", "cron": "0 0 6 * * *", "tz": "America/Los_Angeles", "catch_up": "latest" } ], "steps": [ { "id": "page", "uses": "vision/scrape@1", "with": { "url": "https://news.example.com", "formats": [ "markdown" ] } }, { "id": "summary", "uses": "infer/chat@1", "with": { "messages": [ { "role": "user", "content": "Summarize: ${{ steps.page.outputs.markdown }}" } ] } } ] })});const data = await response.json();console.log(data);
import requestsheaders = { 'Authorization': 'Bearer YOUR_API_TOKEN'}response = requests.post('https://beta-api.b3os.org/v1/routines', headers=headers, json={ "name": "nightly-digest", "triggers": [ { "type": "cron", "id": "nightly", "cron": "0 0 6 * * *", "tz": "America/Los_Angeles", "catch_up": "latest" } ], "steps": [ { "id": "page", "uses": "vision/scrape@1", "with": { "url": "https://news.example.com", "formats": [ "markdown" ] } }, { "id": "summary", "uses": "infer/chat@1", "with": { "messages": [ { "role": "user", "content": "Summarize: ${{ steps.page.outputs.markdown }}" } ] } } ]})print(response.json())
201
Response
{ "id": "rtn_01m5t6vhkw4a8fa4tb5w4ffz37", "object": "routine", "name": "nightly-digest", "description": null, "status": "active", "paused_reason": null, "version": { "id": "ver_01m5t3vcc6bxj0v3x705zmg4cd", "number": 1, "created_at": "2026-10-25T19:10:00.000Z" }, "triggers": [ { "id": "nightly", "type": "cron", "status": "active", "next_fire_at": "2026-10-26T13:00:00.000Z", "hook": null, "subscription_id": null } ], "run_as": { "key": "key_01m5t5gb92a5zncjx654gevx4p", "scopes": [ "vision:run", "infer:run" ] }, "created_at": "2026-10-25T19:10:00.000Z", "updated_at": "2026-10-25T19:10:00.000Z", "metadata": {}}
API Playground
Try this endpoint
POST
/v1/routines