Public beta — not for production use. Data may be wiped at any time. Questions? Contact us.
Documentation menu

Type a flow's input and output

Declare a flow's own request and response messages in the canvas editor — add Flow input / Flow output boundary cards, document their fields, grow them from a wire, or import a published message.

View as Markdown

By default a flow's public contract is implicit: callers must send whatever its first node happens to consume, and they get back whatever its last node happens to emit. That works, but it leaks your flow's internals into its API — rename a node or swap the last step and the contract changes underneath your callers.

A flow can instead declare its own request and response messages. They are what the interactive API docs, the marketplace listing, and every generated client show, and they stay put no matter how you rewire the inside.

This guide covers the canvas editor. The same contract is authored in flow.yaml as input_facade / output_facade, and a flow round-trips between the two — a contract you declare here opens correctly in the CLI, and vice versa.

The Flow input and Flow output cards

A typed contract lives on the canvas as two boundary cards:

  • Flow input — a source. Draw an edge from it to any node, and that node receives a field of the request. Several nodes may each draw their own edge, so different request fields can reach different nodes.
  • Flow output — a sink. Draw an edge into it from the node that produces the result. Several nodes may feed it, and their values are combined.

Select a card and the Inspector shows its Contract tab — the message name, its description, and its fields. That tab is where the contract is edited.

Start from the contract you already have

If your flow already works, the fastest route is to make its existing contract explicit and then edit it. With nothing selected, the Inspector's Contract section shows the current state:

Contract                    Implicit
ParseRequest → ZoneResult

Click Make typed. Axiom copies the first node's input message and the last node's output message into two contracts, adds both boundary cards, and wires them up. Your flow's public contract is unchanged at that moment — callers see exactly what they saw before. From there you rename fields, drop the ones you never wanted to expose, and document the rest.

Once typed, the same section reads:

Contract                    Typed
ParseRequest → ZoneResult
8/9 fields documented

Make typed needs one unambiguous entry and exit point. If your flow forks into several starting nodes, the Inspector says so instead of guessing.

Build a contract from scratch

In the Library panel, the Flow contract section has two entries — Flow input and Flow output. Click either to place its card on the canvas. Then either add fields directly in the Contract tab, or let them grow from the wires (below).

Grow fields from a wire

Usually you know a contract field is needed because you already know what fills it. Select a boundary edge and the mapping drawer offers Add to the response contract (or request contract) listing the fields on the node end that the contract doesn't have yet.

Clicking one adds the field to the contract and binds the wire that fills it, in a single step. The field arrives with the same type, the same repeated / optional flags, and the same documentation as the node field it came from — so it lines up by construction instead of having to be retyped.

Import a published message

The Contract tab's Import from message picks any published message from the marketplace and copies its shape — nested messages, flags, and each field's documentation — into your contract.

This is a one-time copy, not a link. Edit the result freely; it will not follow later changes to the original. The reason to reach for it is the documentation: a well-documented published message gives you a fully documented contract for one click.

Document every field

Descriptions are not decoration. They are the only thing a caller — a person reading your marketplace page, or an agent reading your OpenAPI schema — has to go on. Each field row has a description input next to it, and the Contract tab tracks your coverage:

8/9 fields + 1/1 messages documented

Write what the field means and, where it is not obvious, its format or an example — "IANA zone id, e.g. America/New_York", not "the zone". Coverage is a nudge, never a blocker: an undocumented contract still compiles.

axiom flow validate reports the same counts for the same flow, so the CLI and the editor never disagree about what is documented.

Give the request an example

Descriptions say what each field means. An example payload gives a caller a call that works — and it is what makes a flow demonstrate itself:

  • the Run modal opens pre-filled with it, so anyone who opens your flow can press Run and get a real result;
  • the Use via API curl snippet embeds it, so the command works as pasted;
  • the interactive API docs show it as the request example, instead of a body of empty strings and zeros.

Without one the Run form opens empty, every untouched field is dropped on submit, and the flow runs on default values — a call that succeeds and shows nothing.

In flow.yaml it is one whole payload under the request contract:

input_facade:
  message_name: DateInput
  description: A natural-language datetime to normalize into a target zone.
  fields:
    date_text:
      kind: string
      description: Datetime to parse, in natural language.
    target_zone:
      kind: string
      description: IANA zone id to convert into, e.g. "America/New_York".
  example_input:
    date_text: "next friday 3pm"
    target_zone: "America/New_York"

One coherent payload, not per-field samples — what makes a demo call valid is the fields agreeing with each other. Three rules:

  • Request side only. A response is what the flow hands back and a caller never supplies it, so example_input on output_facade is rejected.
  • It has to fit the contract. The example is checked against your declared fields when the flow compiles: an unknown key or a wrong type fails the compile. A stored example that would fail at invoke is worse than none.
  • Keep it small. Up to 16 KiB of JSON. Use a payload you have actually run.

axiom flow validate points out a request contract with no example, and the example survives axiom flow pull.

What the editor warns you about

The Contract section flags the problems that would otherwise surface as a compile error later:

MessageWhat it means
Nothing reads the request contractYou declared a request but drew no edge out of Flow input.
Nothing fills the response contractYou declared a response but drew no edge into Flow output.
The response contract has no fieldsA response must declare at least one field. A request may be empty.
"x" is wired into the response but no longer declaredYou renamed or deleted a field and left its wire pointing at the old name.
Response field "x" is not wiredThe field will return a zero value. Wire it, or remove it.
Typed contracts aren't supported on pipeline flowsContracts are for unary flows. Remove the contract or the pipeline nodes.

Removing a contract

Select the boundary card and use Remove … contract in the Contract tab, or delete the card from the canvas. Either way the declaration and every wire into or out of it go together, in one undoable step, and the flow reverts to its implicit contract.

See it as a caller does

Open Open interactive docs in the Inspector's API section: the request and response schemas shown there are your contract, descriptions included. That is the same document client SDKs are generated from and the same one shown when you publish the flow.