---
title: "Set per-flow config values"
description: "Create flow config profiles in the console — node timeout and secret overrides — pick one in the Run dialog or via config_id, and see how resolved values reach nodes."
category: guide
surfaces: [console, canvas, http-api, sdk]
related: [guides/manage-secrets, getting-started/first-flow, getting-started/invoke-via-api, concepts/execution-model, reference/http-api]
last_reviewed: 2026-06-10
---

# Set per-flow config values

A **flow config** is a named set of runtime parameters for a flow — today, a
node timeout and optional secret overrides. Configs are not part of the flow
itself: they are resolved at invocation time and injected into the run, so
changing one affects the next run without editing or re-saving the flow. A
config scoped to one flow is called a **profile**; the **tenant default**
applies to every flow you own.

Prerequisites: you are logged in to the Axiom editor and have saved at least
one flow ([Push and run your first flow](../getting-started/first-flow.md)).

## Set the tenant default node timeout

The shortest path to changing config for every flow at once:

1. Click **Console** in the top header, then **Flow Configs** in the
   console sub-nav (the page is at `/console/flow-configs`).
2. Leave the **Flow** picker on **Tenant default (all flows)** — that is the
   default selection.
3. Edit **Default node timeout (seconds)**. The field accepts 1–3600 and
   saves when you click away.

The platform default is **300 seconds** per node invocation. The tenant
default replaces it for all of your flows; a per-flow profile overrides both
for one flow.

## Create a per-flow profile

On the Flow Configs page (`/console/flow-configs`):

1. Pick your flow in the **Flow** picker. With no profiles yet, the page
   shows "No profiles for this flow yet."
2. Click **+ New profile**.
3. Enter a **Profile name** (left blank, it becomes `default` — a flow's
   `default` profile is applied automatically; see "How config values are
   resolved" for the exact layering and its one exception).
4. Set **Node timeout (s)**.
5. Optionally add **Secret overrides**: click **+ Add override**, pick a
   **Secret name** from your existing secrets (or type a name when you have
   none yet), enter a **Value**, and click **Save override**.
6. Click **Create**.

Each profile row lists its name, its timeout, and the names of its secret
overrides, with **Edit** and **Delete** buttons. Override values are write-only:
the page (and the API behind it) returns override names, never values.

Two more ways to reach the same controls:

- **From the editor**: with nothing selected on the canvas, the inspector
  shows a **Run configuration** card containing the same profile list and
  create form for the open flow, plus a link to the tenant default.
- **Deep link**: `/console/flow-configs?graph=<flow id>` preselects that
  flow in the picker.

To manage the secrets themselves (the tenant-wide values that overrides
replace), see [Manage secrets in a flow](manage-secrets.md).

## Pick a profile when you run

In the editor's Run dialog, a **Config profile** selector appears once any
config applies to the flow — a per-flow profile or the tenant default. It
defaults to **Tenant default**; pick a profile to run with its values
instead. The selection applies to that run only.

When invoking over HTTP, set `config_id` in the request body. It accepts
either a profile name (for the flow being invoked) or a profile ID; when
omitted, the default hierarchy applies (tenant default, then the flow's
`default` profile). See [Invoke a flow via the API](../getting-started/invoke-via-api.md)
for the endpoint, authentication, and `graph_id`.

```bash
# Run the flow with the "load-test" profile (assumes AXIOM_API_KEY is exported)
curl -X POST "https://<your-axiom-host>/invocations/v1/flows/invoke" \
  -H "Authorization: Bearer $AXIOM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "graph_id": "<compiled flow id>",
    "input": {"name": "Ada"},
    "wait": true,
    "config_id": "load-test"
  }'
```

## How config values are resolved

At invocation time the platform merges up to four layers, later layers
overriding earlier ones parameter by parameter:

1. **Platform defaults** — `node_timeout_seconds: 300`.
2. **Tenant default** — your config named `default` that is not scoped to
   any flow (the one the Flow Configs page edits under "Tenant default").
3. **Flow default** — the invoked flow's profile named `default`, when one
   exists.
4. **Selected profile** — the profile named in the Run dialog or in
   `config_id`, when it isn't `default`.

Secret overrides merge across the same layers, with the later layer winning
for a given secret name. One exception: selecting a profile by its ID (which
is what the Run dialog sends) applies that profile directly on top of the
tenant default — the flow's `default` profile is not layered in between.

## How nodes receive config values

Resolution happens before the flow starts: the merged parameters and secret
values are injected into the run's variables, which travel with the
execution to every node.

- **`node_timeout_seconds` is consumed by the platform, not by your code.**
  The worker bounds each node invocation with it; a node that exceeds the
  timeout fails that invocation. If the value is missing or invalid, the
  300-second default applies.
- **Secret overrides are invisible to node code.** A node reads secrets
  through `AxiomContext` exactly as described in
  [Manage secrets in a flow](manage-secrets.md); when the run's profile
  overrides a secret, the same read returns the override value instead of
  the tenant-wide one. No code change is needed to support per-profile
  values:

```python
# nodes/summarize.py — gets the tenant-wide OPENAI_KEY, or the selected
# profile's override of it, through the same call
from gen.messages_pb2 import SummarizeRequest, SummarizeReply
from gen.axiom_context import AxiomContext


def summarize(ax: AxiomContext, input: SummarizeRequest) -> SummarizeReply:
    api_key, found = ax.secrets.get("OPENAI_KEY")
    if not found:
        ax.log.error("OPENAI_KEY is not set for this tenant")
        return SummarizeReply()
    ax.log.info("key resolved for this run")  # never log the value itself
    return SummarizeReply()
```

This snippet assumes a Python package with `SummarizeRequest` and
`SummarizeReply` messages and a `Summarize` node — see
[Create a node in Python](create-a-node-python.md) for the scaffold.
