FlowDrop Workflow Specification 1.0-draft

The API maps a client interface onto the stored manifest

What an editor calls an interface entry and what the server stores as a manifest entry are the same thing under two vocabularies. This is the mapping, including what it deliberately ignores.

The rule

Normative: this is the rule
  1. A workflow's API accepts an interface object and maps it onto the stored input and output manifests.
  2. An entry's client-side identifier becomes the server-side input name; renaming either is a breaking change for callers.
  3. An entry carries exactly one binding, whose node and port identifiers become the entry's node_id and port; an entry carrying more than one binding is refused with 400 and nothing is stored.
  4. An entry carrying no binding is a client-side draft: it is skipped, not stored, and not an error.
  5. A declared data type, a schema, a default value and free-form metadata on an entry are ignored on write; the type and schema are derived server-side from the bound port, and the other two have no server representation.
  6. Input-side author metadata (the entry's display name, description, examples and required flag) round-trips as author-written manifest metadata does; an output entry carries only its name and binding.
  7. The mapped manifests are applied before the workflow is validated, so whether a named node or port exists is decided by workflow validation and refused with 422; the API's own 400s cover the entry's shape only.

What it means

An interface entry can only ever name one binding. An entry naming two is refused outright, before anything else about it is even looked at — a shape failure, not a decision about whether the node or port it names exists. An entry naming none is not a failure at all: it is a client-side draft, quietly skipped, and never stored.

That shape check happens before the workflow is validated, so it answers a different question than validation does. A binding whose node or port is made up entirely is not caught here — it is caught by the same check that would catch it on a directly-stored port list, and answered with a different status. The API's own refusals stop at the entry's shape; whether what it points to is real is somebody else's answer.

Example

An entry naming the same binding twice400 — refused
POST /api/flowdrop/workflows

{"name": "Over-bound", "nodes": [  ], "interface": {"inputs": [
  {"id": "numbers", "bindings": [
    {"nodeId": "calc1", "portId": "values"},
    {"nodeId": "calc1", "portId": "values"}
  ]}
]}}
An entry naming no binding, alongside one that does201 — stored
POST /api/flowdrop/workflows

{"name": "Draft entry", "nodes": [  ], "interface": {"inputs": [
  {"id": "draft", "bindings": []},
  {"id": "numbers", "bindings": [{"nodeId": "calc1", "portId": "values"}]}
]}}
An entry whose binding names a node that does not exist422 — refused
PUT /api/flowdrop/workflows/{workflow}

{"name": "Ghost", "interface": {"inputs": [
  {"id": "numbers", "bindings": [{"nodeId": "ghost_node", "portId": "values"}]}
]}}

The draft entry is not stored either, but silently: the response's interface.inputs names only numbers.

Rule identifiers are permanent and are never renumbered. Each implementation publishes its own standing against these rules; this specification does not.spec 1.0-draft · MAN-20 · changed in spec 1.0