Skip to main content
Workflows are deterministic automation pipelines. Unlike agents (which decide what to do), workflows follow a fixed graph of blocks and edges. This guide creates a simple summarizer workflow and deploys it as a webhook.
Prerequisites:
  • Authentication configured (see Authentication guide)
1

Create a workflow

Create an empty workflow container.Endpoint: POST /api/workflows
2

Define the graph

Save blocks (processing steps) and edges (connections between them) as a complete graph. The block registry recognizes these canonical types: starter, agent, code, condition, general_api, platform_api, response, split, webhook, orchestration (function and api_call are accepted as back-compat aliases for code and general_api). The example below wires a starter that injects workflow variables, runs an agent block to produce a summary, then returns the agent’s output through a response block.Endpoint: PUT /api/workflows/{id}/graph
Unknown block types are rejected with 400 Unknown block type. See the Workflows concept page for what each block does.
3

Execute the workflow

Run the workflow with input data. Returns execution results.Endpoint: POST /api/workflows/{id}/execute
4

Deploy with webhook

Two activation modes:
  • Deploy (POST /api/workflows/{id}/deploy) sets state = "deployed". The webhook accepts unlimited calls until you undeploy.
  • Arm (POST /api/workflows/{id}/arm) leaves state = "internal" but opens a 10-minute window during which the webhook accepts exactly one call. After it fires (or expires), you must arm again.
Use deploy for production integrations; use arm for one-shot tests.The webhook_id and webhook_secret are properties of the webhook block itself, stored in block.config. The studio editor mints them client-side when you drag in a webhook block. To retrieve them programmatically, fetch the workflow and pull them from the block’s config.
If you create the webhook block via API instead of the UI (PUT /api/workflows/{id}/graph), you must mint both webhook_id and webhook_secret yourself and include them in the block’s config. The server does not generate them. webhook_id MUST be a valid UUID; the trigger endpoint validates it and returns 400 otherwise. webhook_secret can be any non-empty string (the studio editor uses crypto.randomUUID() for both). A webhook block with no secret is silently un-triggerable (the trigger endpoint returns 401).
The deploy endpoint returns {"ok": true, "state": "deployed"} and arm returns {"ok": true, "armed_until": "<iso-timestamp>"}. Neither response contains the webhook credentials.
5

Trigger externally

Call the webhook endpoint from any external system. No platform API key is needed; auth is per-webhook via the secret, sent either as a Bearer header (preferred) or a ?token= query param. The request body becomes the workflow’s input variables directly.
Use one auth mechanism or the other, not both. The server checks the header with auth_header.lower().startswith("bearer ") (note the trailing space). If your Authorization header is exactly Bearer (trailing space, no token), which is what Bearer ${secret || ""} produces when secret is falsy, the server reads an empty token and 401s without consulting ?token=. The ?token= fallback only fires when the header doesn’t start with Bearer .
Endpoint: POST /api/webhooks/{webhook_id}
If the workflow is deployed, the webhook accepts unlimited calls. If only armed, the webhook accepts exactly one call within the 10-minute window; re-arm to fire it again.

What’s Next

Workflows (Copilot)

Build workflows with natural language.

Workflows

Understand block types and graph execution.

Workflows API Reference

Full endpoint documentation.