REST API
Everything in Boardwalk is an HTTP call. The dashboard, the CLI, and the MCP server all sit on the same REST surface, so anything they do, your own server can do too: fire a run when an event happens in your product, read its status and events, deploy from CI, or manage your org. The hosted API lives at https://api.boardwalk.sh; self-hosted is your own host. Every path is under /v1, requests and responses are JSON, and auth is a bearer token.
Authentication
For server-to-server calls, use an API key. Create one in the dashboard under Settings → API keys (key minting needs a logged-in session, so it is not something a key can do to itself). The full key begins with bwk_ and is shown once at creation; store it as a secret. Send it as a bearer token:
curl https://api.boardwalk.sh/v1/orgs/acme/workflows \
-H "Authorization: Bearer bwk_your_key_here"A key belongs to one org, so org-scoped routes still take your org slug in the path. The CLI and CI both read the same value from the BOARDWALK_API_KEY environment variable. Two other credentials reach the same routes: a Clerk session JWT (the dashboard) and the CLI's OAuth token (boardwalk login). Credential-minting actions below are marked session only; an API key can never perform them at any scope.
Key scopes
A key is least-privilege. Leave its scopes empty for full access at the key's role, or list scopes to restrict it to exactly the actions you need. For a key that only kicks off runs from your app, grant run:trigger and nothing else. Scopes are <resource>:<action> strings:
| Resource | Scopes |
|---|---|
| Workflows | workflow:read, workflow:create, workflow:update, workflow:delete, workflow:trigger, workflow_version:read, workflow_version:create |
| Runs | run:read, run:trigger, run:cancel, run:delete |
| Inference | inference:invoke (call the managed inference gateway directly) |
| Read-only | audit_log:read, billing:read |
Credential-minting actions (writing secrets, minting API keys, wiring inference providers, inviting members) are never reachable by an API key at any scope; they require a logged-in session in the dashboard. See roles for the role each action needs.
Conventions
The whole surface is consistent: JSON in and out, ISO-free millisecond-epoch timestamps, cursor pagination, and one error shape.
Pagination & polling
List endpoints take ?limit (1 to 100, default 50) and an opaque ?cursor, and return a nextCursor that is null on the last page. Pass it back to page forward:
GET /v1/orgs/acme/runs?limit=50
// → { "runs": [ ... ], "nextCursor": "eyJ0IjoxNz..." }
GET /v1/orgs/acme/runs?limit=50&cursor=eyJ0IjoxNz...
// → { "runs": [ ... ], "nextCursor": null } // last pageList and detail reads support a conditional GET: send the ETag you last saw back as If-None-Match and an unchanged resource answers 304 Not Modified with no body. That makes status polling cheap; for a live view, prefer the event stream over a poll loop (see Runs & observability).
Errors
Every non-2xx response is the same envelope: a stable code, a human message, and an optional detail object.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "triggers must contain at least one entry",
"detail": { "path": "triggers" }
}
}400 VALIDATION_FAILED: the request body or query failed schema validation.401 UNAUTHENTICATED/403 FORBIDDEN: no usable credential, or the credential lacks the role or scope.404 NOT_FOUND: the resource is missing orbelongs to another org. Cross-tenant access is a 404, never a 403, so a key can't probe for ids it can't see.405: the path exists but not for that method; the response carries anAllowheader.429: rate limited (a per-user and per-org bucket); back off and retry.
Trigger a run
This is the call you want when your own server or web app needs to start a workflow on demand. POST to the workflow's runs collection; the optional inputbody becomes the run's trigger payload, available to the program as input. An optional environment (a name) selects the environment the run executes in, with its secrets and variables; omit it for the organization base.
curl -X POST \
https://api.boardwalk.sh/v1/orgs/acme/workflows/wf_123/runs \
-H "Authorization: Bearer bwk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "input": { "pr_url": "https://github.com/acme/app/pull/42" } }'Or from your application code:
const res = await fetch(
"https://api.boardwalk.sh/v1/orgs/acme/workflows/wf_123/runs",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BOARDWALK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ input: { pr_url: prUrl } }),
},
);
const { run } = await res.json(); // 201 Created
console.log(run.id, run.status); // the new run's id + "queued"The trigger returns immediately with the new run's id and status; the run executes asynchronously. To react to a workflow event instead of polling, point a workflow webhook at it, or subscribe with a watch.
Read a run
Poll the run by id for its terminal status, and read its event log for the full timeline (phases, agent turns, output):
GET /v1/runs/{runId} # status, timing, token + cost totals
GET /v1/runs/{runId}/input # the trigger payload this run was called with
GET /v1/runs/{runId}/events # the run's event log (phases, agent, output)
POST /v1/runs/{runId}/cancel # stop a queued or in-flight run
POST /v1/runs/{runId}/retry # re-run with the same inputA run's status moves through queued, running, then one of completed, failed, or cancelled. The value passed to output() in the program lands in the run's events under the output channel. For the event model, channels, and the live stream, see Runs & observability.
Endpoint reference
The full public surface, grouped by resource. All paths are under /v1 and take a bearer token; org-scoped routes take your org slug in the path.
Workflows
| Method & path | What it does |
|---|---|
GET /orgs/:slug/workflows | List the org's workflows (paginated). |
POST /orgs/:slug/workflows | Create a workflow from a built program artifact (the CLI's deploy). |
POST /orgs/:slug/workflows/artifact-upload-url | Mint a presigned upload URL for a program artifact before create/update. |
GET /workflows/:id | Fetch one workflow, its manifest, and its current version. |
PATCH /workflows/:id | Publish a new version (or rename the slug). |
DELETE /workflows/:id | Delete a workflow (soft; runs and audit are retained). |
POST /workflows/:id/disable · /enable | Pause every trigger, reversibly, then resume. |
Runs
| Method & path | What it does |
|---|---|
POST /orgs/:slug/workflows/:id/runs | Trigger a run; optional input and environment. Returns 201. |
GET /orgs/:slug/workflows/:id/runs | List a workflow's runs (filter ?status=, paginated). |
GET /orgs/:slug/runs | List every run across the org. |
GET /runs/:id | Status, timing, token and cost totals, error. |
GET /runs/:id/input | The trigger payload this run was called with. |
GET /runs/:id/events | A snapshot of the run's stored event log. |
POST /runs/:id/cancel · /retry | Stop a queued or in-flight run; re-run with the same input. |
Schedules
| Method & path | What it does |
|---|---|
POST /workflows/:id/schedules | Create a durable schedule: exactly one of cron, rate, or at, with optional timezone and input. |
GET /workflows/:id/schedules | List a workflow's durable schedules. |
DELETE /workflows/:id/schedules/:scheduleId | Cancel a schedule so it stops firing. |
GET /orgs/:slug/schedules | The org's cron triggers plus predicted next fire times (the agenda view). |
Schedules created here are the same primitive a program provisions with workflows.schedule(). A cron trigger declared in meta needs nothing here; these endpoints are for schedules created at runtime or from your own tooling.
Webhooks
| Method & path | What it does |
|---|---|
GET /orgs/:slug/workflows/:id/webhook | The inbound URL and auth mode (the secret is never returned on read). |
POST /orgs/:slug/workflows/:id/webhook/rotate | Rotate the signing secret; the new URL is shown once (admin). |
Watches
| Method & path | What it does |
|---|---|
GET|PUT|DELETE /workflows/:id/watch | Your subscription to a workflow's terminal outcomes. |
GET|PUT|DELETE /runs/:id/watch | Your subscription to one run's terminal outcome. |
GET|PATCH /me/notifications | Your notification preferences and every watch you hold. |
DELETE /me/watches/:id | Drop a single watch. |
Artifacts
| Method & path | What it does |
|---|---|
GET /orgs/:slug/runs/:id/artifacts | List the artifacts a run produced. |
GET /artifacts/:id | One artifact's metadata (name, content type, size, expiry). |
GET /artifacts/:id/download | 302 redirect to a short-lived signed URL. |
GET /artifacts/:id/download-url | The signed URL as JSON (when you can't follow a redirect with a header). |
See Artifacts for what a workflow writes and how the signed URLs work.
Secrets
| Method & path | What it does |
|---|---|
GET /orgs/:slug/secrets | List secret names and metadata. Values are never returned. |
POST /orgs/:slug/secrets (session only) | Stage a secret value into the vault. |
GET /secrets/:id | One secret's metadata. |
DELETE /secrets/:id · POST /secrets/:id/rotate (session only) | Delete or rotate a secret. |
Environments
| Method & path | What it does |
|---|---|
POST /orgs/:slug/environments · GET | Create a named environment; list the org's environments. |
GET|PATCH|DELETE /environments/:id | Read, update, or delete one (delete cascades its secrets and variables). |
POST /orgs/:slug/env-variables · GET | Set a non-secret variable on an environment or the org base; list them. |
GET|PATCH|DELETE /env-variables/:id | Read, update, or delete one variable. |
An environment scopes secrets and variables; a run picks one by name at trigger time, and an env-level value overrides the org base. See Secrets & environments.
Inference
| Method & path | What it does |
|---|---|
GET /orgs/:slug/inference-providers | List BYO providers (name, source, base URL). Keys are never returned. |
POST /orgs/:slug/inference-providers (session only) | Register a provider and stage its key. |
DELETE /orgs/:slug/inference-providers/:name (session only) | Remove a provider. |
PUT /orgs/:slug/inference-providers/:name/bedrock-role | Wire a BYO Bedrock cross-account role (the second step after create). |
GET /inference/rates | The public managed-model price table (no auth). |
API keys
| Method & path | What it does |
|---|---|
GET /orgs/:slug/api-keys | List keys (prefix, last-4, scopes, spend cap; never the value). |
POST /orgs/:slug/api-keys (session only) | Mint a key; the bwk_ token is shown once. |
POST /orgs/:slug/inference-keys | Mint an inference-only key with a default spend cap (what boardwalk dev uses). |
PATCH /api-keys/:id (session only) | Set or clear a monthly spend cap. |
DELETE /api-keys/:id | Revoke a key. |
Billing & usage
| Method & path | What it does |
|---|---|
GET /orgs/:slug/usage | Runs, compute, tokens, outcomes, and credit over a window. |
GET /orgs/:slug/workflows/:id/usage | The same, scoped to one workflow. |
GET /orgs/:slug/billing/balance · /seats · /transactions | Credit balance, seat counts, and the ledger. |
POST /orgs/:slug/billing/checkout · /portal | Open a Stripe Checkout session or the customer portal (admin). |
GET|PATCH /orgs/:slug/billing/email | Read or set the billing email. |
Org, members, audit
| Method & path | What it does |
|---|---|
POST /orgs · GET /orgs/check-slug | Create an org (you become owner); check slug availability. |
GET|PATCH|DELETE /orgs/:slug | Read, rename, or delete the org (delete is owner-only). |
PATCH /orgs/:slug/security | Require 2FA, allow-list email domains, set run retention (admin). |
GET|PATCH|DELETE /orgs/:slug/members/:userId | List, re-role, or remove members. |
POST|GET /orgs/:slug/invitations · DELETE .../:id | Invite, list, or revoke (invite is session only). |
POST /invitations/accept | Accept an invitation by token. |
GET /orgs/:slug/audit | The audit log (admins see all; others see their own entries). |
POST /orgs/:slug/exports · GET .../exports/:id | Request an org data export and read its status (admin). |
You
| Method & path | What it does |
|---|---|
GET /me | Your profile and org memberships. |
PATCH /me · DELETE /me (session only) | Update your display name; delete your account. |
POST /me/export | Download your account data as JSON. |
Inbound webhooks
The trigger endpoint above is for code you control. When a third-party system you don't control needs to start a run (a GitHub push, a Stripe event), give it a workflow webhook instead: Boardwalk provisions a per-workflow URL and secret, and verifies the incoming request (a shared token in the URL or an HMAC signature) before firing. Fetch the URL and auth mode with GET /v1/orgs/:slug/workflows/:id/webhook, or rotate its secret with the /rotate route.
The runner API
A running workflow reaches the control plane over a separate /runner/v1/* surface (secrets, artifacts, child runs, telemetry). It is authorized by a short-lived per-run token the platform mints for each run, not by your API key, and is an internal contract between the runner and the broker. It is not part of the public API and not for general use; your programs reach all of it through the SDK instead.