Paylane Freelance Job State Machine (Mode A)

Version: 1.0.0
Last updated: 2026-07-28
Related: CONTRACT_SPEC.md, MONEY_RULES.md, DISPUTE_POLICY.md

This is the full off-chain + on-chain state machine for Mode A (freelance escrow). Mode B (API pay) has no escrow states — see MONEY_RULES.md.


1. States

StateOn-chain?MoneyDescription
DraftNoClient editing job + checklist
PublishedNoVisible listing; not funded
FundedYes FundedUSDC lockedEscrow confirmed
AssignedYes FundedLockedWorker accepted / assigned
InProgressYes FundedLockedWork underway
DeliveredYes DeliveredLockedArtifacts submitted; review clock starts
RevisionRequestedYes DeliveredLockedClient asked changes (≤ K)
AcceptedYes AcceptedReleased to worker − feeTerminal success
DisputedYes DisputedFrozenAuto-release paused
ResolvedYes ResolvedPer admin splitTerminal
AutoReleasedYes AutoReleasedReleased to worker − feeTerminal (silence)
CancelledYes CancelledRefunded if fundedTerminal
ExpiredYes ExpiredRefundedDeadline miss
RefundedAlias of cancel/expire refund completeLedger label

2. Diagram

stateDiagram-v2
  [*] --> Draft
  Draft --> Published: client publish\n(≥1 checklist item)
  Draft --> Cancelled: client discard
  Published --> Funded: client fundJob (USDC)
  Published --> Cancelled: client cancel
  Published --> Expired: draft/listing TTL (optional)
  Funded --> Assigned: assign/accept worker
  Funded --> Cancelled: client cancel (no worker)
  Funded --> Expired: deadline crank (no delivery)
  Assigned --> InProgress: worker starts
  Assigned --> Cancelled: worker cancel → refund
  Assigned --> Expired: deadline crank
  InProgress --> Delivered: worker deliver
  InProgress --> Cancelled: worker cancel → refund
  InProgress --> Expired: deadline crank
  InProgress --> Disputed: either party (rare)
  Delivered --> Accepted: client accept → release
  Delivered --> RevisionRequested: client revision (count < K)
  Delivered --> Disputed: either party
  Delivered --> AutoReleased: silence ≥ reviewWindow
  RevisionRequested --> Delivered: worker re-deliver
  RevisionRequested --> Disputed: either party
  RevisionRequested --> Accepted: client accept
  RevisionRequested --> AutoReleased: silence ≥ reviewWindow\n(window from first delivery)
  Disputed --> Resolved: admin release/refund/split
  Accepted --> [*]
  AutoReleased --> [*]
  Resolved --> [*]
  Cancelled --> [*]
  Expired --> [*]

3. Defaults (configurable via env)

KeyDefaultBounds
PLATFORM_FEE_BPS200 (2%)0–1000
REVIEW_WINDOW_HOURS72 (3 days)24–168
REVISION_LIMIT20–5
DISPUTE_EVIDENCE_HOURS7224–168
UNFUNDED_LISTING_TTL_DAYS30optional
DEADLINE_EXTENSIONmutual agreeoff-chain + optional new deadline tx later

4. Transition table

Legend: C client, W worker, A admin/arbitrator, S system/crank, Any permissionless.

FromToActorGuardsSide effects
DraftPublishedC≥1 acceptance criterion; title; amount > 0audit, notify
DraftCancelledCaudit
PublishedFundedCapprove+fundJob; correct USDC+chainescrow lock; receipt; audit; notify
PublishedCancelledCaudit
FundedAssignedC or W acceptworker wallet set; escrow fundedaudit; notify
FundedCancelledCworker unsetfull refund; receipt
FundedExpiredS/Anypast deadline; not deliveredfull refund
AssignedInProgressWaudit
Assigned/InProgressCancelledWnot deliveredfull refund; reputation penalty
Assigned/InProgressExpiredS/Anypast deadlinefull refund
InProgressDeliveredW≥1 artifact or hash; escrow fundedmarkDelivered; start review clock; notify
DeliveredRevisionRequestedCrevisionsUsed < K; reason + checklist issuesrevisionsUsed++; audit; notify
Delivered/RevisionRequestedDeliveredWre-upload allowedaudit; review window does not reset
Delivered/RevisionRequestedAcceptedCaccept tx; fee; receipts; ratings unlocked
Delivered/RevisionRequestedDisputedC or Wevidence window opensopenDispute; freeze auto-release
Delivered/RevisionRequestedAutoReleasedS/Anynow ≥ deliveredAt + reviewWindow; not disputedautoRelease; fee; receipts
DisputedResolvedAevidence deadline passed or both submitted; amounts sum to escrowresolveDispute; receipts; final
**wrong actor / paused / wrong networkreject; no state change

Blocked transitions (examples)

  • Start InProgress without Funded escrow → reject
  • 3rd revision when K=2 → reject (Accept or Dispute only)
  • Dispute after AutoReleased/Accepted → reject
  • Double accept → reject (idempotent; no double-pay)
  • Cancel after Delivered → reject
  • Non-party wallet acting → reject

5. Timers

TimerStartsDurationOn expiry
Review / auto-releaseFirst Delivered (deliveredAt)REVIEW_WINDOW_HOURSautoRelease crank
Job deadlineJob create/fundclient-setexpireRefund if not delivered
Dispute evidenceDisputedDISPUTE_EVIDENCE_HOURSadmin may decide; late evidence discretionary
Unfunded listing TTLPublished30d defaultCancelled/Expired listing

Cron emits warnings at T-24h and T-6h for deadline and auto-release.


6. Permissions matrix (summary)

ActionDraftPublishedFundedAssignedInProgressDeliveredRevReqDisputed
Edit jobCC*
FundC
Assign/AcceptC/W
DeliverWWW†W
AcceptCC
RevisionC
DisputeC/WC/W
ResolveA
Auto-releaseAnyAny
CancelCCC§/WWW

* Limited fields after publish.
† Re-deliver after revision.
‡ Dispute from Funded/Assigned/InProgress allowed only for fraud/no-show edge cases; MVP UI emphasizes Delivered+.
§ Client cancel only if worker unset.


7. Audit & notifications (every transition)

  1. Validate actor role + wallet match
  2. Validate timing + escrow status (DB + optional chain read)
  3. Apply transition in DB transaction (idempotency key)
  4. Write AuditLog
  5. Write LedgerEntry / receipt if money moved
  6. Notify counterparties (in-app; email optional)

8. Mode separation

ConcernMode AMode B
States aboveYesNo
EscrowYesNo
Accept / dispute / auto-releaseYesNo
Payment modelLock → releaseInstant access-for-payment (x402)

UI must never mix Mode A dispute UX into Mode B successful API responses (non-disputable by default).