Appearance
HTTP endpoints
Every flow you build is automatically a live HTTP endpoint. This page covers the URL it lives at, how the method is chosen, how inputs are validated, how to protect it, and how to see inside a run.
The URL
A flow is reachable at:
/api/run/<project>/<file-slug>[/param1/param2/…]<project>is your project's ID.<file-slug>is the flow file's name, lowercased with every run of non-alphanumeric characters turned into a single hyphen. A file namedCreate Orderanswers atcreate-order.- If the flow lives inside a named folder, that folder contributes its own slug segment first:
orders/create-order. Folders without a name are transparent — they add no segment. - Any extra segments after the slug are positional path parameters (see below).
If the project, the file, or a method-matching Start node can't be found, the endpoint returns 404 — the same answer whether the flow doesn't exist or simply doesn't accept that method, so you never leak which flows exist.
The method comes from the Start node
A flow's Start node has a method — GET, POST, PUT, PATCH, or DELETE (default GET). An incoming request only runs the flow if its method matches a Start node's method.
A single flow file can hold more than one Start node, each with a different method, so one URL can behave differently for a GET versus a POST. The request is routed to the Start node whose method matches.
Input validation
The Start node declares what inputs it accepts, sorted into four buckets. Each is validated independently, and a caller that violates any of them gets a 400 with a list of exactly what failed.
| Bucket | Read from | Notes |
|---|---|---|
| Query | ?key=value in the URL | |
| Body | The JSON request body | Only read for methods that allow a body (not GET/HEAD), and only when the body schema has fields. Requires Content-Type: application/json. |
| Headers | Request headers | Looked up case-insensitively. |
| Path | Positional trailing URL segments | Filled in the order the path params are declared. |
Sending more trailing path segments than the flow declares is a 404, not a 400 — an undeclared path isn't a real endpoint.
Once every bucket validates, the values are merged into a single input object and handed to the flow's Start node. From there, downstream nodes read them by name.
NOTE
Body parsing is strict-ish but forgiving: a non-JSON content type is treated as an empty body, and malformed JSON is reported as body must be valid JSON. If the schema declares no body fields, the body is never read at all.
Authentication
The Start node can require callers to authenticate. Auth is checked before any input is parsed, so an unauthenticated request never reaches your flow.
| Type | Caller sends | Configured with |
|---|---|---|
| None | Nothing | The endpoint is public. |
| Bearer | Authorization: Bearer <token> | An expected token. |
| Basic | Authorization: Basic <base64 user:pass> | Expected username + password. |
| API key | A header (default X-API-Key) | Expected key + optional header name. |
All comparisons are constant-time. A failed check returns 401, and for Bearer/Basic a WWW-Authenticate header is included.
WARNING
Auth is fail-closed. If you configure an auth type but the expected secret is empty — for example an unresolved ${secrets.TOKEN} because the secret was never set — every request is rejected. This is deliberate: an empty expected value can never accidentally authorize a caller.
Put the actual credentials in the secrets vault and reference them as ${secrets.NAME} in the auth config. The value is resolved per-workspace at run time and never stored in the flow.
Seeing inside a run: ?_trace
Add ?_trace=1 to any request and the response includes a _trace array — one entry per node that ran, with its timing, whether it succeeded, and which output port it took. The same trace is also returned in an X-Flow-Trace header (URL-encoded).
bash
curl 'https://your-app/api/run/proj/orders/create-order?_trace=1' \
-H 'Authorization: Bearer '"$TOKEN"This is the fastest way to debug a live endpoint without opening the editor. The same trace powers run history and the in-editor testing panel.
The response
The flow's End node shapes what the caller receives: its configured status code (any valid 100–599, default 200) and a response body composed from the flow's data. If the flow throws, the endpoint returns 500 with the failing node's ID. A flow with no End node returns a default { ok: true, inputs } echo.
Every endpoint is CORS-enabled (OPTIONS preflight returns 204) and rate-limited per client IP to 60 requests per minute; exceeding that returns 429 with a Retry-After header.