Graph portability and explicit dependencies

Submit plain Comfy API graphs, name media and models by content-addressed identity, keep credentials out, and know what validation rejects.

Updated

Wavemaker runs graphs as ordinary ComfyUI API JSON. A graph you write, export or adapt stays a plain Comfy graph: no Wavemaker wrapper, no hidden steps, and no credentials inside it. Media and models enter it by content-addressed identity, so the same graph with the same inputs names the same work wherever it runs.

Submit a graph

  1. Discover what the runtime offers: search with kinds: ["node"], ["graph"] or ["model"], then read wm://node/{class}, wm://graph/{id} or wm://model/{id}.
  2. Upload each input file and keep its sha256. See HTTP API.
  3. Check the graph without running it: generate with graph and validate_only: true.
  4. Price it: the same call with quote_only: true.
  5. Run it: the same call with a request_id, then follow the job. See Quote and run work.
curl --fail-with-body \
  --header "Authorization: Bearer $WAVEMAKER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"args": {"graph": {...},
                    "inputs": {"media": {"12.image": "9f86d0..."},
                               "bindings": {"6.text": "A cold brew can on a train seat", "3.seed": 42}},
                    "validate_only": true}}' \
  https://api.wavemaker.io/v1/tools/generate
# → 200 {"ok": true, "data": {"valid": true, "problems": [], "available": true, "graph": "inline", ...}}

Replace {...} with your Comfy API graph. In this example node 12 is an image loader whose image input takes the uploaded file, node 6 holds the prompt text and node 3 the seed; use your own graph's node ids and input names. Each key must name exactly one existing node input, or the call is refused with invalid_graph.

Check that it worked

  • valid is true and problems is empty.
  • available is true: a runtime can take the graph now.
  • A graph the service refuses answers valid: false with problems[], each with a path (such as graph/12), a message and a code. Other failures, such as a media sha256 your workspace doesn't own or a busy pause, come back as ordinary errors. Validation needs no request_id, and nothing is quoted, held or charged.

What a graph is

  • Plain Comfy API JSON. An object of node ids, each {class_type, inputs}. A link is [source_node_id, output_index]. Catalog files may wrap the graph as {meta, graph}; send the inner graph.
  • Exactly one target. generate takes one of graph, model or route. graph is an inline graph or a catalog id (wm://graph/{id}). model runs a model profile through its reviewed graph.
  • Two kinds of input. inputs.media stages owned files into upload fields, keyed by node id and input name, such as {"12.image": "9f86d0..."}. inputs.bindings sets literal values the same way, such as {"3.seed": 42}. Any other key under inputs is refused with invalid_graph, so a misspelled input never runs silently on a placeholder.
  • Takes. count (up to 8) runs takes that differ: each take adds its index to every literal seed and noise_seed input, and take 0 runs as sent.
  • Outputs. The service collects every output the graph produces. Each becomes an artifact named by its sha256 under the job's result.artifacts[].

Content-addressed identities

WhatHow a graph names it
Media you upload or made earlierIts sha256, through inputs.media. Only files your workspace owns can be staged.
Outputswm://artifact/{sha256}. The same bytes always have the same name.
A model your workspace registeredwm://model/{name}/{version}, or its file name {name}-v{version}.safetensors, in a loader input. The service checks that your workspace registered that version and still holds its exact bytes.
A catalog graphwm://graph/{id}. A reviewed catalog revision runs only when its file matches the sha256 the catalog recorded.

A registered model whose weights are missing is refused with conflict. An unknown wm://model/ reference is not_found. A file name nobody registered is left to the runtime's own models.

Make dependencies explicit

A graph only names node classes, inputs and links. The runtime that executes it owns the node schemas and the models. Before you share or move a graph, record what it needs:

  • the custom node packs and their versions;
  • each model's weights, by name and sha256;
  • the input files, by sha256, and which node input each one feeds;
  • any declared license for the nodes and weights;
  • for native recipes, the Python packages, FFmpeg or other tools they call.

An empty dependency list is not a portability guarantee. Structural validity doesn't prove the models load, the output is useful, or that you may distribute the dependencies.

Keep credentials out of graphs

A graph carries no secrets. Provider keys belong in Connections, entered on the provider's private enrollment page. Don't put keys in graph input defaults, exported source, prompts or public examples. See Credentials.

Partner nodes inside a graph run on Wavemaker's credits, never on keys in the graph, and a saved provider key in Connections doesn't fund them. One graph take's partner calls may hold up to 100 credits unless you set params.partner_max_microcredits, and never more than what that take's share of max_microcredits leaves after its GPU hold. Where partner nodes can't reach their supplier, the graph is refused before anything is held, with route_unavailable; use the partner's route with generate and route instead.

What validation rejects

The service checks a graph before it holds any credits. A refusal is invalid_graph (422), or a validate_only report with valid: false.

Shape and schema

A graph that isn't a non-empty object of nodes, each with class_type and inputs, is refused at once with invalid_graph. A graph no runtime admits is refused with invalid_graph and details.rejected[]: each distinct reason once, such as node_not_admitted (a runtime lacks a node class), resource_bounds (an input above a runtime's maximum), graph_node_limit or graph_schema_invalid. A graph_schema_invalid reason lists up to 10 errors, each with its node, input and one of these codes:

ProblemCode
A node class the runtime has no schema forunknown_node
A required input missingmissing_input
A link to a node that isn't in the graph, or to an output slot it doesn't havemissing_source, output_index
A link between ports of different typesport_type
A value of the wrong type, outside the schema's bounds, or not one of the listed choicesvalue_type, value_range, enum_value
A dynamic input not sent through its declared portsdynamic_input
A cyclecycle
An empty-latent width or height that isn't a multiple of 8, or an H3 image-to-video or reference-to-video width or height that isn't a multiple of 32model_alignment

An input the schema doesn't declare is a warning (unknown_input), not a refusal.

Safety rules

  • Node classes you may send. A graph you write may use only the node classes Wavemaker's own catalog graphs and model profiles use and ComfyUI's built-in nodes. Classes from other community packs are added only after review. Another class is refused with invalid_graph and details.reason: "node_not_permitted".
  • Denied classes. Nodes that delete or probe files, dump GPU memory, evaluate code, or save model weights never run.
  • Path, URL and expression inputs. An input named like a path, folder, file, URL or expression takes only a literal value, never a link from another node. A free-text value there is refused; a value picked from the input's list of choices is allowed. ComfyMathExpression is allowed, with an expression of at most 256 characters.
  • Output names. filename_prefix must stay inside the output folder.
  • Media inputs. Only declared upload fields can take staged files.

Placement

The service places each graph on a runtime that has its nodes and weights. When none admits it, the refusal message names what to fix first, for example an input the runtimes that have its model refused, or an input above their maximum. A graph over a runtime's node limit is refused too. While graph runs are paused for maintenance, a graph is refused with busy (503) and retry_after_s; retry with the same request_id.

Validate locally with the CLI

The wavemaker CLI checks a local graph file without running it. With a direct Comfy runtime connected (wavemaker runtime connect), graph validate checks against that runtime's node schemas; without one, it asks the selected Wavemaker service to validate it:

wavemaker graph inspect graph.api.json --json
wavemaker graph validate graph.api.json --json
wavemaker graph diff graph.api.json revised.api.json

graph bind writes a copy with new input values, graph slice extracts a node and its upstream nodes, and graph import converts a ComfyUI workflow into API JSON using a directly connected Comfy runtime's schemas; the hosted service has no workflow import. Validation is structural; the runtime that executes the graph stays authoritative. See CLI.

Run it only when you mean to

Run a graph with generate and a request_id only when you intend to pay for it. The same request_id with the same graph and inputs returns the original jobs instead of running again. See Quote and run work and Graphs in the web app.

Related guidance