# Flow graph schema

Each flow is a JSON document at `.fetchcatch/flows/{slug}.json`.

## Top-level shape

```json
{
  "schemaVersion": 1,
  "id": "00000000-0000-0000-0000-000000000001",
  "slug": "approve-expense",
  "name": "Approve expense",
  "description": "Optional human description",
  "graph": { ... },
  "publish": false
}
```

| Field | Required | Description |
|-------|----------|-------------|
| `schemaVersion` | yes | Must be `1` for this doc version |
| `id` | yes | Stable GUID; do not change after first apply |
| `slug` | yes | URL-safe identifier; matches filename |
| `name` | yes | Display name |
| `description` | no | Free text |
| `graph` | yes | Flow graph (see below) |
| `publish` | no | **Legacy.** If `true`, `fcc apply` also publishes (back-compat only). Leave `false` or omit in git; ship with `fcc publish` instead — see [sync-and-ci.md](sync-and-ci.md). |

## Graph object

```json
{
  "start": "start_1",
  "nodes": [ ... ],
  "edges": [ ... ],
  "viewport": { "x": 0, "y": 0, "zoom": 1 }
}
```

| Field | Required | Description |
|-------|----------|-------------|
| `start` | yes | Node `id` of the start node |
| `nodes` | yes | Array of node objects |
| `edges` | yes | Array of edge objects |
| `viewport` | no | Designer canvas state (cosmetic) |

## Node object (common fields)

```json
{
  "id": "http_1",
  "type": "http",
  "label": "Fetch user",
  "description": "Loads user profile from CRM",
  "position": { "x": 120, "y": 80 }
}
```

| Field | Description |
|-------|-------------|
| `id` | Unique within graph; must match `^[A-Za-z_][A-Za-z0-9_]*$` |
| `type` | One of: `start`, `http`, `condition`, `transform`, `wait_for_event`, `decision`, `end` |
| `label`, `description` | UI-only; safe for agents to add for clarity |
| `position` | UI-only coordinates |

See [flow-nodes.md](flow-nodes.md) for type-specific fields.

## Edge object

```json
{
  "from": "condition_1",
  "to": "http_2",
  "when": "true"
}
```

| Field | Required | Description |
|-------|----------|-------------|
| `from` | yes | Source node id |
| `to` | yes | Target node id |
| `when` | condition only | `"true"` or `"false"` for branches leaving a condition node |

Non-condition nodes typically omit `when`.

## Validation rules (summary)

- Exactly one `start` node; `graph.start` must reference it.
- Node ids must be unique and match the identifier regex.
- `http` nodes need `apiSourceId` + `operationId`.
- `condition` nodes need `expression`.
- `wait_for_event` nodes need `eventName`.
- Graph must be reachable from `start`; terminal nodes are typically `end`.
- `decision` nodes should have an outgoing edge to `end` (legacy terminal decisions still run but are discouraged).

## Editing guidance for AI agents

1. **Preserve** `id`, `schemaVersion`, and existing node ids unless explicitly renaming.
2. **Do not invent** `apiSourceId` values — they must exist in the tenant (configure in console or pull first).
3. Prefer **small, focused edits** to expressions, edges, or `responseValues`.
4. Run `fcc status` after edits; invalid graphs fail on apply with server-side validation errors.
5. Use `description` on nodes to document intent for future agents.
6. **Do not** set `"publish": true` in CI repos — run `fcc apply` then `fcc publish` (or publish from the console).
