Validate a definition
Compiles a definition without publishing it and returns every diagnostic with a JSON pointer: schema errors, unknown callables and step references, dependency cycles, CEL type errors and input mismatches against the callables' schemas. Always answers 200; check `valid`. Use it while an agent writes a routine.
POST
/v1/routines/validate
Compiles a definition without publishing it and returns every diagnostic with a JSON pointer: schema errors,
unknown callables and step references, dependency cycles, CEL type errors and input mismatches against the
callables' schemas. Always answers 200; check `valid`. Use it while an agent writes a routine.
Authentication
Bearer Token (b3os_sk_ API key or b3os_pat_ token) API Key (cookie: __Host-b3os_session)
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
200
The validation result.
application/jsonobject
string
REQUIRED
Enum:
routine_validationvalid
boolean
REQUIRED
diagnostics
object[]
REQUIRED
Array of:
severity
string
REQUIRED
Enum:
error, warningcode
string
REQUIRED
An error code from the catalog, such as `routine_invalid` or `connection_required`.
pointer
string
REQUIRED
JSON pointer into the definition.
detail
string
REQUIRED
steps
integer
Number of steps in the compiled plan, map bodies included.
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`).
413
The request body exceeds this operation's limit.
415
The Content-Type is not accepted by this operation.
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/validate' \ -H 'Authorization: Bearer YOUR_API_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "name": "nightly-digest", "steps": [ { "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/validate', { method: 'POST', headers: { "Authorization": "Bearer YOUR_API_TOKEN", "Content-Type": "application/json" }, body: JSON.stringify({ "name": "nightly-digest", "steps": [ { "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/validate', headers=headers, json={ "name": "nightly-digest", "steps": [ { "id": "summary", "uses": "infer/chat@1", "with": { "messages": [ { "role": "user", "content": "Summarize: ${{ steps.page.outputs.markdown }}" } ] } } ]})print(response.json())
200
Response
{ "object": "routine_validation", "valid": false, "diagnostics": [ { "severity": "error", "code": "routine_invalid", "pointer": "/steps/0/with/messages/0/content", "detail": "steps.page is not a step of this routine" } ], "steps": 1}
API Playground
Try this endpoint
POST
/v1/routines/validate