---
title: "Invoke a flow via API"
description: "Create an API key, copy the ready-made curl command from the Use via API dialog, and call your compiled flow over HTTP — one-shot or as an SSE stream."
category: tutorial
surfaces: [canvas, console, http-api, cli]
related: [getting-started/first-flow, guides/api-keys, guides/use-interactive-api-docs, reference/http-api, concepts/execution-model]
last_reviewed: 2026-06-10
---

# Invoke a flow via API

Every flow you build in the canvas can be invoked over HTTP. This page takes
the flow from [Build your first flow](./first-flow.md) and calls it from
outside the editor: create an API key, copy a ready-made `curl` command from
the **Use via API** dialog, run it, and read the result — about ten minutes.

## Prerequisites

- A saved flow in the editor — finish
  [Build your first flow](./first-flow.md) first.
- An account you can sign in to the editor with. The API key you create must
  belong to the same account that owns the flow.
- `curl` on your machine.

## Create an API key

API requests authenticate with a bearer API key. To create one:

1. In the app, go to **Console → API Keys** (`/console/api-keys`).
2. Click **Create key**, fill in **Name** (for example `my-laptop`), and
   click **Create key** in the dialog.
3. The **Save your API key** dialog shows the raw key — a 64-character hex
   string — **exactly once**. Copy it now and click **Done**; the key is
   never shown or recoverable again (the platform stores only its SHA-256
   hash).

Export it so the commands below can use it:

```bash
export AXIOM_API_KEY="<the 64-character key you copied>"
```

After the dialog closes, the key list shows only the name and a masked form
(first 8 characters … last 4). To revoke a key, click **Revoke
&lt;name&gt;** in its row and confirm with **Revoke key**; revocation takes
effect on the key's very next use. Full management details:
[Create and manage API keys](../guides/api-keys.md).

The key authorizes requests as your tenant: it can invoke flows your account
owns and nothing belonging to other tenants — see
[Sandboxing and tenancy](../concepts/sandboxing-and-tenancy.md).

## Copy the curl command from the Use via API dialog

The editor generates the invoke command for you, two ways:

- Open your flow, click **Run** at the bottom of the canvas, then click
  **Use via API** in the run dialog's footer. Any input values you filled
  into the run form are pre-filled into the command.
- Or open the flow inspector's **API** section and click
  **Use via API (curl)**. The same section shows the invoke URL and an
  **Open interactive docs** button — see
  [Use the interactive API docs](../guides/use-interactive-api-docs.md).

The dialog compiles the current canvas into a compiled artifact and shows a
complete `curl` command containing:

- the invoke URL on your deployment's origin —
  `/invocations/v1/flows/invoke`, or `/invocations/v1/flows/invoke/stream`
  when the flow runs in pipeline mode;
- a JSON body whose `graph_id` is the compiled artifact's 26-character id
  and whose `input` is the entry node's input message as a JSON object.

Click the copy button, then replace `$AXIOM_API_KEY` with your key — or
keep the variable and change the quotes around the `Authorization` header to
double quotes so your shell expands it.

The `graph_id` is pinned to this version of the flow: if you edit the flow,
reopen the dialog to get an updated command.

## Invoke the flow and read the result

The copied command has this shape (your origin, `graph_id`, and input are
filled in by the dialog; the header is quoted here so `$AXIOM_API_KEY`
expands):

```bash
curl -X POST 'https://flows.example-axiom-host.com/invocations/v1/flows/invoke' \
  -H "Authorization: Bearer $AXIOM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "graph_id": "01JX3F8Q4ZJ4M9W4Y0B8T2K7RD",
  "input": { "text": "hello" },
  "wait": true
}'
```

`"wait": true` makes the request block until the execution completes —
up to 30 seconds by default; set `"timeout_seconds"` to wait longer. On
success the response is HTTP 202 with the result inline:

```json
{
  "accepted": true,
  "execution_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "result": {
    "success": true,
    "output": { "text": "hello" },
    "completed_at": 1765432100000
  }
}
```

- `execution_id` identifies this execution everywhere — execution history,
  debug events, and traces all reference it
  ([Debug a flow](../guides/debug-a-flow.md)).
- `result.output` is the terminal node's output message, decoded to JSON.

The request body accepts these fields:

| Field | Required | Meaning |
|---|---|---|
| `graph_id` | yes | The compiled artifact to execute. |
| `input` | yes | The entry node's input message as a JSON object; the platform converts it to the typed message. |
| `wait` | no | Block until the execution completes and include `result` in the response. Without it the response returns immediately with just `accepted` and `execution_id`. |
| `timeout_seconds` | no | How long to wait for completion (default 30). |
| `config_id` | no | Named flow config profile to apply — see [Flow configs](../guides/flow-configs.md). |

A missing, invalid, or revoked key gets HTTP 401 with
`{"error":"unauthorized"}`. Other failures return a structured error body —
see the [error catalog](../reference/error-catalog.md) and the full
[HTTP API reference](../reference/http-api.md).

## Stream a pipeline flow over SSE

A flow in pipeline mode emits a sequence of result frames instead of a
single response, so it uses the streaming endpoint
`/invocations/v1/flows/invoke/stream`. The **Use via API** dialog detects
pipeline mode and generates this variant automatically:

```bash
curl -N -X POST 'https://flows.example-axiom-host.com/invocations/v1/flows/invoke/stream' \
  -H "Authorization: Bearer $AXIOM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "graph_id": "01JX3F8Q4ZJ4M9W4Y0B8T2K7RD",
  "input": { "text": "hello" }
}'
```

`-N` disables curl's output buffering so frames print as they arrive. The
response is Server-Sent Events (`Content-Type: text/event-stream`); each
frame is one `data:` line of JSON:

```text
data: {"execution_id":"4bf92f3577b34da6a3ce929d0e0e4736","frame_index":0,"payload":{"text":"chunk 1"},"is_final":false}

data: {"execution_id":"4bf92f3577b34da6a3ce929d0e0e4736","frame_index":1,"payload":{"text":"chunk 2"},"is_final":false}

data: {"execution_id":"4bf92f3577b34da6a3ce929d0e0e4736","frame_index":2,"payload":{"text":"chunk 3"},"is_final":false}

data: {"execution_id":"4bf92f3577b34da6a3ce929d0e0e4736","frame_index":3,"is_final":true,"success":true}
```

- `payload` is the terminal node's output message for that frame, decoded
  to JSON. Payload-carrying frames always have `"is_final": false`.
- The stream ends with a separate final frame — `"is_final": true` and no
  `payload`; its `success` reports whether the execution succeeded, and on
  failure `error` carries the message.
- The stream times out after 30 seconds by default; set
  `"timeout_seconds"` in the body to extend it. A timeout ends the stream
  with a final frame whose `error` is
  `"timeout waiting for pipeline result"`.
- `wait` does not apply here — streaming always delivers frames as they are
  produced.

A flow that contains a pipeline node always runs in pipeline mode; for
other flows, the flow type is shown in the run dialog and can be switched
there — see [Execution model](../concepts/execution-model.md).

## Next steps

Your flow is now an endpoint any program can call. From here:

- [Use the interactive API docs](../guides/use-interactive-api-docs.md) —
  browse and try your flow's generated OpenAPI docs from the inspector.
- [Flow configs](../guides/flow-configs.md) — run the same flow with named
  parameter profiles via `config_id`.
- [Debug a flow](../guides/debug-a-flow.md) — inspect any `execution_id`
  you got back from the API.
- [HTTP API reference](../reference/http-api.md) — every endpoint, field,
  and error shape.
