Skip to content

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.

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

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.

Any command not listed above MUST return 422 Unprocessable Entity with error type urn:freyt:protocol:error:invalid-state-transition.

  • Authorization: Actor belongs to the owning (shipper) organization.
  • Scope required: loads:publish
  • Relationship state: Owning organization must have at least one active partner relationship unless the request uses an open visibility policy.
  • Version check: If-Match MUST match current resourceVersion.
  • Policy check: pickupArea, pickupWindow, deliveryArea, deliveryWindow, requiredCapacity, vehicleRequirements, goodsProfile, expiresAt, and visibility MUST 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-Match MUST match current resourceVersion.
  • Policy check: Withdrawal reason MUST be provided. Any open MatchProposal records linked to this request MUST be transitioned to withdrawn before or atomically with this command.
  • Authorization: Broker system only.
  • Precondition: At least one CapacityOffer in published or partiallyMatched state is compatible; the PartnerRelationship between carrier and shipper is active.
  • Version check: Both offer and request resourceVersion values MUST be observed and included in the MatchProposal.

accept (system command, triggered by MatchProposal acceptance)

Section titled “accept (system command, triggered by MatchProposal acceptance)”
  • Authorization: Broker system only, triggered after all required MatchDecision records agree.
  • Precondition: Associated MatchProposal status is transitioning to accepted.
  • Version check: Request resourceVersion at time of proposal creation MUST match current version; otherwise 409 stale-resource-version.
  • Authorization: System clock process.
  • Precondition: Current time ≥ expiresAt.
  • Authorization: Protocol operator with admin scope.
  • Policy check: Cancellation reason MUST be recorded.
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.
  • All state-changing commands require If-Match with the current ETag.
  • Concurrent publish and withdraw: first writer wins; second receives 409 Conflict with stale-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 withdraw and a concurrent accept (from a completed proposal decision) race. The first committed transaction wins. A committed matched state MUST NOT be reversed by a delayed withdraw; 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 matched state. The matched terminal state takes precedence if committed first.
  • All transitions MUST be applied atomically.
  1. Single active match: A LoadRequest in matched state MUST be associated with exactly one accepted MatchProposal.
  2. No re-publication: A withdrawn, expired, or cancelled request MUST NOT be re-published. A new request MUST be created.
  3. ExpiresAt immutability: expiresAt MUST NOT be changed after the request reaches published.
  4. Visibility isolation: A draft request MUST NOT be visible to any partner organization.
  5. Data minimisation: goodsProfile in a published request MUST contain only coarse, non-sensitive classification. Exact goods details, customer identity, and price MUST NOT be included unless policy permits.
  6. Active relationship required: A MatchProposal linking this request to a CapacityOffer MUST only be created when the owning organizations have an active PartnerRelationship.
  7. Proposal consistency: The set of proposalIds on a proposed request MUST only reference MatchProposal records that themselves reference this loadRequestId.