FlowDrop Workflow Specification 1.0-draft

A machine answers an outbound wait through its own route

A remote system cannot use the route a human uses; it has no session and so cannot present the token that route requires. It gets a route built for its threat model, and an authority that buys it nothing else.

The rule

Normative: this is the rule
  1. A machine caller resolves an outbound wait by posting {"value": …} (the same key the human resolution route takes) to a dedicated callback route addressed by the interrupt's unguessable identifier.
  2. The human route requires a session-bound request token that a machine cannot obtain; the callback route does not require one, and must therefore admit only authentication schemes a cookie-authenticated browser cannot present, so there is no session to ride and nothing for such a token to protect.
  3. Authorisation is a dedicated authority to resolve interrupts by callback: it is not satisfied by, and does not grant, the authority to resolve interrupts as a human.
  4. The route is scoped by interrupt type, not by direction: only an external-call interrupt (the one shape a remote system was invited to answer) is resolvable here, and any other is refused 409.
  5. Every human prompt shape is stamped outward exactly as an external call is, so a direction-only guard would hand the callback credential a person's approval prompt and resume the run as though someone had answered; a direction check is kept behind the type check as defence in depth.
  6. An unknown identifier answers 404; an interrupt that is not pending, or has expired, answers 409, so a replayed callback never resolves twice.
  7. A request omitting value is refused 400 before the interrupt is looked up, so a malformed request reveals nothing about which identifiers exist.

What it means

A remote system has no session to ride, so the human route's request token is not something it can ever present; it gets a callback route built for what it actually is, addressed by the interrupt's own unguessable identifier instead of a session. Reaching that route does not make a caller a human: resolving by callback is a distinct authority that grants nothing else, in particular not the authority to answer as a person would.

The bite is in the guard order, not just its existence. The route is scoped by interrupt type first — only the one shape a remote system was invited to answer is resolvable here — and the direction check sits behind it. Every human prompt is stamped outward exactly as an external call is, so checking direction alone would let the callback credential answer a person's approval prompt. And the missing-value check runs before the interrupt is even looked up: a malformed request gets refused without learning whether the identifier it named exists at all.

Example

A machine caller answering the call it was invited to answer200 — resolved
POST /api/flowdrop/interrupts/{interrupt_id}/callback

{"value": {"ok": true}}
A malformed request against an identifier that does not exist400 — refused
POST /api/flowdrop/interrupts/{interrupt_id}/callback

{}
The same body, against a human-facing prompt409 — refused
POST /api/flowdrop/interrupts/{interrupt_id}/callback

{"value": true}

The second request is refused for what it is missing, before anything is looked up; only a well-formed request against an unknown identifier reaches 404.

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 · INT-19 · changed in spec 1.0