Skip to main content

Overview

Milestones allow escrow funds to be released in stages rather than all at once. They’re ideal for freelancing, construction, or any multi-deliverable contract. A milestone lives inside an escrow. The escrow must be active before milestones can progress.

Status flow

proposed
  └─► approved       (counterparty approves the milestone)
        └─► in_progress  (work begins)
              └─► completed    (worker marks it done)
                    └─► released ✓   (client releases funds)
                    └─► disputed ─► resolved
  └─► changes_requested  (back to negotiation)
  └─► cancelled ✗

API sequence

1. Create a milestone

Either party proposes a milestone:
POST /milestone-create
{
  "escrow_id": "<id>",
  "title": "UI Design mockups",
  "description": "Figma file with 5 screens",
  "amount": 150000,
  "due_date": "2026-05-01"
}

2. Approve or request changes

The counterparty approves:
POST /milestone-approve
{ "milestone_id": "<id>" }
Or requests changes:
POST /milestone-request-changes
{ "milestone_id": "<id>", "note": "Please include mobile breakpoints" }

3. Mark complete

The worker signals delivery:
POST /milestone-mark-complete
{ "milestone_id": "<id>" }

4. Release payment

The client releases funds:
POST /milestone-release
{ "milestone_id": "<id>" }
Funds are credited to the payee’s wallet immediately.

Rules

  • Milestone amounts must not exceed the remaining unfunded escrow balance
  • Milestones can be cancelled by either party while in proposed or changes_requested
  • A released milestone cannot be reversed — raise a dispute if there’s a problem