LoadRequest — Lifecycle State Machine
Generated from
specification/state-machines/load-request.md. Edit the canonical source file, not this page.
RFC 2119 normative language applies throughout.
1. States
Section titled “1. States”| State | Meaning |
|---|---|
draft |
Request assembled but not yet published. Visible only to the owning (shipper) organization. |
published |
Request is live and visible to authorized partners. Available for matching. |
proposed |
At least one MatchProposal has been created linking this request to a CapacityOffer. Awaiting decision. |
matched |
An accepted MatchProposal has committed this request to a carrier. No further matching is performed. |
withdrawn |
Shipper voluntarily removed the request before fulfillment. No further commands are accepted. |
expired |
expiresAt passed before the request was matched or withdrawn. No further commands are accepted. |
cancelled |
Administratively cancelled (e.g. due to policy violation or erroneous entry). No further commands are accepted. |
Terminal states: withdrawn, expired, cancelled
2. State-Transition Table
Section titled “2. State-Transition Table”| From State | Command | To State | Notes |
|---|---|---|---|
| (none) | POST /load-requests |
draft |
Owning organization creates the request. |
draft |
publish (POST /load-requests/{id}/publish) |
published |
Shipper makes request visible to authorized partners. |
published |
propose (system) |
proposed |
Broker creates a MatchProposal; request enters negotiation. |
published |
withdraw (POST /load-requests/{id}/withdraw) |
withdrawn |
Shipper withdraws the live request. |
published |
expire (system, clock) |
expired |
expiresAt reached with no accepted match. |
proposed |
accept (system, on MatchProposal accepted) |
matched |
Associated MatchProposal is accepted by all required parties. |
proposed |
proposalExpired (system) |
published |
All open proposals for this request expired or were rejected; request re-enters matching pool. |
proposed |
withdraw |
withdrawn |
Shipper withdraws even while proposals are outstanding; open proposals are voided. |
proposed |
expire (system, clock) |
expired |
expiresAt reached while proposals pending. |
draft |
cancel (admin) |
cancelled |
Administrative cancellation before publication. |
published |
cancel (admin) |
cancelled |
Administrative cancellation after publication. |
proposed |
cancel (admin) |
cancelled |
Administrative cancellation; open proposals are voided. |
Invalid Transitions
Section titled “Invalid Transitions”Any command not listed above MUST return 422 Unprocessable Entity with error type urn:freyt:protocol:error:invalid-state-transition.
3. Preconditions
Section titled “3. Preconditions”publish (draft → published)
Section titled “publish (draft → published)”- Authorization: Actor belongs to the owning (shipper) organization.
- Scope required:
loads:publish - Relationship state: Owning organization must have at least one
activepartner relationship unless the request uses an open visibility policy. - Version check:
If-MatchMUST match currentresourceVersion. - Policy check:
pickupArea,pickupWindow,deliveryArea,deliveryWindow,requiredCapacity,vehicleRequirements,goodsProfile,expiresAt, andvisibilityMUST all be present and valid. Customer identity, exact addresses, price, and complete goods details MUST NOT appear unless policy explicitly permits disclosure.
withdraw (published | proposed → withdrawn)
Section titled “withdraw (published | proposed → withdrawn)”- Authorization: Actor belongs to the owning organization.
- Scope required:
loads:publish - Version check:
If-MatchMUST match currentresourceVersion. - Policy check: Withdrawal reason MUST be provided. Any open
MatchProposalrecords linked to this request MUST be transitioned towithdrawnbefore or atomically with this command.
propose (system command)
Section titled “propose (system command)”- Authorization: Broker system only.
- Precondition: At least one
CapacityOfferinpublishedorpartiallyMatchedstate is compatible; thePartnerRelationshipbetween carrier and shipper isactive. - Version check: Both offer and request
resourceVersionvalues MUST be observed and included in theMatchProposal.
accept (system command, triggered by MatchProposal acceptance)
Section titled “accept (system command, triggered by MatchProposal acceptance)”- Authorization: Broker system only, triggered after all required
MatchDecisionrecords agree. - Precondition: Associated
MatchProposalstatus is transitioning toaccepted. - Version check: Request
resourceVersionat time of proposal creation MUST match current version; otherwise409 stale-resource-version.
expire (system command)
Section titled “expire (system command)”- Authorization: System clock process.
- Precondition: Current time ≥
expiresAt.
cancel (admin)
Section titled “cancel (admin)”- Authorization: Protocol operator with admin scope.
- Policy check: Cancellation reason MUST be recorded.
4. Postconditions
Section titled “4. Postconditions”| Command | Postconditions |
|---|---|
POST /load-requests |
status = draft; resourceVersion = 1; createdAt recorded. |
publish |
status = published; publishedAt recorded; resourceVersion incremented; event pcx.load-request.published.v1 emitted. |
propose |
status = proposed; proposalIds list updated; resourceVersion incremented; event pcx.match-proposal.created.v1 emitted (on the proposal, not the request). |
accept |
status = matched; matchedAt recorded; accepted proposalId stored; resourceVersion incremented; event pcx.load-request.matched.v1 emitted. |
proposalExpired |
status reverts to published; expired proposal removed from active proposal list; resourceVersion incremented. |
withdraw |
status = withdrawn; withdrawnAt recorded; withdrawal reason stored; resourceVersion incremented; event pcx.load-request.withdrawn.v1 emitted. |
expire |
status = expired; expiredAt recorded; resourceVersion incremented; event pcx.load-request.expired.v1 emitted. |
cancel |
status = cancelled; cancellation reason and actor recorded; resourceVersion incremented. |
5. Concurrency Rules
Section titled “5. Concurrency Rules”- All state-changing commands require
If-Matchwith the current ETag. - Concurrent publish and withdraw: first writer wins; second receives
409 Conflictwithstale-resource-version. - Concurrent match attempts: the broker MUST use optimistic concurrency on both request and offer. If a second match attempt reads a version that was already updated by the first committed match, it MUST abort and not create a duplicate
MatchProposal. - Withdraw racing with match acceptance: a
withdrawand a concurrentaccept(from a completed proposal decision) race. The first committed transaction wins. A committedmatchedstate MUST NOT be reversed by a delayedwithdraw; the shipper MUST be notified and a separate cancellation initiated. - Expire racing with accept: if the request expires while a proposal acceptance is being committed, the expiry MUST NOT overwrite the
matchedstate. Thematchedterminal state takes precedence if committed first. - All transitions MUST be applied atomically.
6. Invariants
Section titled “6. Invariants”- Single active match: A
LoadRequestinmatchedstate MUST be associated with exactly one acceptedMatchProposal. - No re-publication: A
withdrawn,expired, orcancelledrequest MUST NOT be re-published. A new request MUST be created. - ExpiresAt immutability:
expiresAtMUST NOT be changed after the request reachespublished. - Visibility isolation: A
draftrequest MUST NOT be visible to any partner organization. - Data minimisation:
goodsProfilein a published request MUST contain only coarse, non-sensitive classification. Exact goods details, customer identity, and price MUST NOT be included unless policy permits. - Active relationship required: A
MatchProposallinking this request to aCapacityOfferMUST only be created when the owning organizations have anactivePartnerRelationship. - Proposal consistency: The set of
proposalIdson aproposedrequest MUST only referenceMatchProposalrecords that themselves reference thisloadRequestId.