--- id: MAN-20 family: GR-MAN level: extended profiles: [storage-api] posture: normative-target added: "1.0" changed: "1.0" source: https://flowdrop.io/spec/rules/gr-man/man-20 specification: FlowDrop Workflow Specification 1.0-draft licence: CC BY 4.0 --- # MAN-20 — The API maps a client interface onto the stored manifest *GR-MAN (Part I) · level: extended · profiles: storage-api · added in 1.0* 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 ```http title="An entry naming the same binding twice" verdict="400 refused" POST /api/flowdrop/workflows {"name": "Over-bound", "nodes": [ … ], "interface": {"inputs": [ {"id": "numbers", "bindings": [ {"nodeId": "calc1", "portId": "values"}, {"nodeId": "calc1", "portId": "values"} ]} ]}} ``` ```http title="An entry naming no binding, alongside one that does" verdict="201 stored" POST /api/flowdrop/workflows {"name": "Draft entry", "nodes": [ … ], "interface": {"inputs": [ {"id": "draft", "bindings": []}, {"id": "numbers", "bindings": [{"nodeId": "calc1", "portId": "values"}]} ]}} ``` ```http title="An entry whose binding names a node that does not exist" verdict="422 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`. ## Related rules - Names: MAN-2, MAN-3, MAN-21 - Referenced by: STORE-14, MAN-3, MAN-21 --- Rule identifiers are permanent and are never renumbered. This specification carries no implementation status: each implementation publishes its own standing against these rules. Licensed CC BY 4.0.