Flow externalId
Seller-scoped Flow externalId identity, collision, immutability, and runtime boundary contract.
Status: adopted contract; runtime implementation is pending the Flow aggregate.
externalId is the seller-provided join key for a Flow and its related
artifacts. It is not a globally unique identifier and it is not an internal
Flow ID.
Identity and uniqueness
The uniqueness key is the tuple:
(seller account, flow type, externalId)
seller accountis the authenticated seller scope. A value in one seller's namespace does not collide with the same value in another seller's namespace.flow typeis the canonical Flow type, compared after the API's normal enum parsing. A value may be reused for a different Flow type in the same seller.externalIdis compared as the submitted identifier after the API's normal validation/normalisation. The runtime implementation must document that normalisation before accepting writes; it must not silently trim or case-fold values as an undocumented compatibility rule.
A create request whose tuple already exists is a conflict, not a second Flow.
The API returns HTTP 409 Conflict and includes the existing Flow's
canonical flowId in the error details. The response must not create or mutate
any Flow, event, invoice, or other linked artifact.
This collision response is also the retry/idempotency rule for externalId:
retrying a create with the same tuple converges on the existing flowId; it
must never create a duplicate. A separate request idempotency key, if added to
a Flow endpoint, cannot widen this uniqueness scope or override the conflict.
Immutability and lifecycle
externalId is write-once. It is required on Flow creation, is returned on
reads, and cannot be changed by update, transition, close, or reopen operations.
The seller cannot reuse the tuple after a Flow closes: closure does not release
or recycle the uniqueness reservation. A replacement Flow must use a new
externalId (or a different Flow type).
The identifier may be copied into events and invoice metadata as a join reference, but those copies do not establish identity and cannot be used to change the source Flow.
Runtime boundary
Runtime implementation is tracked separately in FLOW-09b / #3309. That follow-up is blocked on FLOW-00 / #3225, because the Flow aggregate and public endpoint do not exist yet.
This document is the contract for the future Flow aggregate only. There is currently no Flow aggregate, persistence constraint, handler, or public
money-moving endpoint in this repository. No implementation may claim that
409, immutability, or Flow-linked artifact propagation is live until the
aggregate, seller-scoped unique constraint, transaction boundary, and handlers
are implemented and covered by runtime tests.
The implementation follow-up must at minimum prove:
- concurrent creates arbitrate on the seller/type/externalId unique constraint;
- the losing create returns
409with the winner'sflowIdand creates no linked rows; - update and lifecycle writes reject any externalId change before persistence;
- a closed Flow still reserves its tuple; and
- authenticated seller scope is part of every lookup and write.