Skip to content

The .nbflow Schema (Light)

A .nbflow is a JSON file that describes a complete workflow — every node, every link, every property. This page covers the schema at a level useful for understanding what the agents produce. The full schema is too dense for a wiki page; this is the navigable subset.

High-level shape

{
  "name": "XYZG1-V0-3",
  "tabs": [
    {
      "name": "Account A",
      "graphData": {
        "nodes": [ /* ... node objects ... */ ],
        "links": [ /* ... link tuples ... */ ]
      }
    },
    {
      "name": "Account B",
      "graphData": { /* ... */ }
    }
  ]
}

Top-level fields:

  • name — the workflow's version-stringed name (matches the file basename)
  • tabs — array. One tab per account in a multi-account workflow.

Each tab has:

  • name — the account code or label
  • graphData — the nodes and links that make up the graph for this account

Nodes

Each node in graphData.nodes is an object:

{
  "id": 12,
  "type": "nanobanana/NanobananaAPI",
  "title": "Scene 03 — image gen",
  "pos": [120, 240],
  "size": [240, 180],
  "inputs": [
    {"name": "prompt", "type": "string", "link": 45},
    {"name": "image 1", "type": "*", "link": 8},
    {"name": "image 2", "type": "*", "link": null}
  ],
  "outputs": [
    {"name": "output", "type": "*", "links": [67]}
  ],
  "properties": {
    "model": "nano_banana_2",
    "outputCount": 4,
    "aspectRatio": "9:16",
    "_savedImages": ["https://pub-xxx.r2.dev/.../scene03-c1.png", "..."]
  }
}

Key fields:

Field Purpose
id Unique numeric ID for this node. Globally unique across all tabs in a multi-account file.
type Node type identifier (e.g. nanobanana/Prompt, nanobanana/NanobananaAPI, nanobanana/Veo3, nanobanana/Media, Approve)
title Human-readable label (shown in PatchWork UI)
pos Canvas position [x, y] for visual layout
size Canvas size [width, height]
inputs Array of input slots. Each has name, type, and link (ID of the link feeding it, or null)
outputs Array of output slots. Each has name, type, and links (array of link IDs originating from it)
properties Type-specific properties (prompt text, model name, R2 URLs of generated outputs, etc.)

Each link in graphData.links is a tuple (5-6 element array):

[
  45,         // link_id
  3,          // source_node_id
  0,          // source_slot_index
  12,         // target_node_id
  0,          // target_slot_index
  "string"    // type (optional)
]

This represents an edge: output slot 0 of node 3 connects to input slot 0 of node 12.

Important: links live in TWO places:

  1. The central tab.graphData.links array (above)
  2. The per-node outputs[].links and inputs[].link references

These must stay in sync. If they don't, you get the stale-per-node-refs bug.

Node types you'll see

Type Purpose
nanobanana/Prompt Holds prompt text. Modes: plain, template (with {placeholder}), dynamic (multi-row)
nanobanana/Media Holds an image (reference upload or generated output)
nanobanana/NanobananaAPI Generates images via G-Labs / NanoBanana 2
nanobanana/Veo3 Generates video clips via Veo 3.1
Approve / Approval Gate node — surfaces candidates in a gallery for the user to pick

Each type has type-specific properties fields. See Chapter 2's Node Types Reference for the per-type properties.

What gets serialized

When the PatchWork Importer assembles a .nbflow:

  • All node positions / layouts
  • All prompt text
  • All node properties (model, outputCount, etc.)
  • All wiring (links + per-node refs)

When the Generation Runner finishes a pass:

  • The generated R2 URLs get added to each gen node's properties._savedImages / properties._savedClips
  • The Approve nodes track which candidate was picked
  • The whole file is exported as -generated.nbflow

The -generated.nbflow is the same shape as the input, just with R2 URLs baked in.

What's NOT in the schema

  • API keys / credentials (these live separately)
  • The G-Labs tunnel URL (set in PatchWork's app-level settings)
  • Generation history beyond the latest pass (use git + version registry for that)
  • User picks across multiple browser sessions (stored in browser local storage, not in the file)

Multi-account specifics

In a multi-account .nbflow:

  • Each tab has its own complete graphData — own nodes, own links
  • Node IDs must be globally unique across ALL tabs (not just per-tab unique)
  • Reference image Media nodes are per-tab — each account's tab has its own Media node with the account's avatar reference URL

This is the source of the "renumber" requirement during fan-out — duplicate IDs across tabs break everything.

When you'd actually read the schema

Most of the time, you don't. You work through PatchWork's UI or by talking to Claude.

You'd dig into the JSON when:

  • Diagnosing a build failure (sanity check told you a specific node is wrong)
  • Writing a custom script that mutates .nbflow files (fan-out, variant patcher)
  • Manually fixing a broken link or node
  • Recovering from a corrupted file

For all of those, the pre-generation sanity check gives you specific node IDs to look at, so you're not reading the whole file — you're inspecting specific nodes.

When you're ready

Next: Pipeline Variants — AI Self / Voice Cloning — specialized pipeline configurations for when the standard workflow doesn't fit.