---
title: "Type a flow's input and output"
description: "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."
category: guide
surfaces: [canvas, cli]
related: [getting-started/first-flow, guides/publish-a-flow, guides/use-interactive-api-docs, concepts/type-system, concepts/nodes-packages-flows]
last_reviewed: 2026-07-31
---

# Type a flow's input and output

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:

```text
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:

```text
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:

```text
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:

```yaml
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:

| Message | What it means |
|---|---|
| Nothing reads the request contract | You declared a request but drew no edge out of **Flow input**. |
| Nothing fills the response contract | You declared a response but drew no edge into **Flow output**. |
| The response contract has no fields | A response must declare at least one field. A request may be empty. |
| `"x"` is wired into the response but no longer declared | You renamed or deleted a field and left its wire pointing at the old name. |
| Response field `"x"` is not wired | The field will return a zero value. Wire it, or remove it. |
| Typed contracts aren't supported on pipeline flows | Contracts 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](./build-a-client-sdk.md) are generated from and
the same one shown when you [publish the flow](./publish-a-flow.md).
