Milestone-Based Escrow
Split a single escrow into sequential payment gates. Each milestone releases independently — the seller earns as they deliver, not only when everything is done.
What Are Milestones?
A milestone is a named percentage slice of the total escrow amount. When you add milestones to an escrow, the full amount is still locked upfront — but it is only released in portions as each milestone is completed. This gives buyers protection across long-running contracts, and gives sellers a clear payment schedule with no ambiguity about what triggers each payout.
Milestones are defined at creation time and cannot be added after an escrow is initiated. Each milestone has a title, an optional description, and a percentage. The percentages across all milestones must add up to exactly 100.
Software Projects
Design → Build → QA → Handover. Pay each phase without exposing the full contract amount.
Construction
Foundation → Structure → Fit-out. Payments tied to inspected site progress.
Phased Deliveries
Batch 1 → Batch 2 → Batch 3. Release funds as each shipment is confirmed.
Milestone Fields
Pass the milestones array inside the POST /v1/escrow/initiate body. Omitting the array creates a standard single-release escrow.
| Field | Type | Required | Notes |
|---|---|---|---|
| title | string | Yes | Human-readable name, e.g. Phase 1 — Design |
| description | string | No | Optional detail about what deliverables trigger this release |
| percentage | integer | Yes | Share of the total escrow, 0–100. All milestones must sum to exactly 100 |
Milestone percentages must total exactly 100. A sum of 99 or 101 will return a validation error. The last milestone absorbs any cent-level rounding remainder so the full locked amount is always distributed.
Milestone Statuses
| Status | Meaning |
|---|---|
| PENDING | Funds allocated to this milestone. Release not yet triggered. |
| RELEASED | Payout distributed to seller. This milestone is closed. |
| DISPUTED | An adjustment proposal has been raised against this milestone. |
| CANCELLED | Milestone cancelled via an accepted zero-payout adjustment. Buyer refunded. |
Creating a Milestone Escrow
The example below creates a four-phase software contract worth $10,000. Each milestone maps to a project phase. Funds for all four phases are locked immediately; each releases only when triggered.
curl -X POST "https://api.switchlock.co.zw/v1/escrow/initiate" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-company-id: YOUR_COMPANY_ID" \
-H "Content-Type: application/json" \
-d '{
"buyerId": "usr_buyer_uuid",
"sellerId": "usr_seller_uuid",
"buyerAccountId": "acc_buyer_usd_uuid",
"externalReference": "contract-DEV-2026-001",
"feePayer": "SELLER",
"items": [
{
"name": "Custom ERP System — Full Build",
"quantity": 1,
"pricePerUnit": 1000000
}
],
"milestones": [
{
"title": "Phase 1 — Discovery & Design",
"description": "Wireframes, architecture diagrams, and approved tech spec",
"percentage": 20
},
{
"title": "Phase 2 — Core Backend",
"description": "API, database schema, authentication, and core modules",
"percentage": 35
},
{
"title": "Phase 3 — Frontend & Integration",
"description": "UI implementation and end-to-end integration tests passing",
"percentage": 30
},
{
"title": "Phase 4 — UAT & Handover",
"description": "User acceptance testing sign-off and production deployment",
"percentage": 15
}
]
}'{
"id": "esc_02jy...",
"status": "INITIATED",
"totalAmount": 1000000,
"milestones": [
{ "id": "ms_01", "title": "Phase 1 — Discovery & Design", "percentage": 20, "amount": 200000, "status": "PENDING" },
{ "id": "ms_02", "title": "Phase 2 — Core Backend", "percentage": 35, "amount": 350000, "status": "PENDING" },
{ "id": "ms_03", "title": "Phase 3 — Frontend & Integration", "percentage": 30, "amount": 300000, "status": "PENDING" },
{ "id": "ms_04", "title": "Phase 4 — UAT & Handover", "percentage": 15, "amount": 150000, "status": "PENDING" }
]
}Releasing a Specific Milestone
Pass milestoneId as a query parameter to release a single milestone. Without it, all remaining pending milestones are released at once.
curl -X POST "https://api.switchlock.co.zw/v1/escrow/esc_02jy/release?milestoneId=ms_01" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-company-id: YOUR_COMPANY_ID"curl -X POST "https://api.switchlock.co.zw/v1/escrow/esc_02jy/release" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-company-id: YOUR_COMPANY_ID"| Parameter | Type | Required | Notes |
|---|---|---|---|
| id | path | Yes | The escrow ID. |
| milestoneId | query | No | The specific milestone to release. If omitted, all eligible milestones are released. |
{
"message": "Successfully released milestone(s)",
"escrowId": "esc_02jy...",
"status": "COMPLETED",
"releasedAmount": 200000
}Milestones release in sequence. The auto-release on GRN confirmation and the daily 24-hour sweep both respect milestone boundaries — they will only release the milestones that are currently eligible.
How Fees Work With Milestones
Fees are distributed proportionally as each milestone releases — not all upfront or all at the end. Each milestone's release pays its fair share of the total platform and service fee. The final milestone absorbs any rounding remainder to ensure the full locked amount is always distributed with no dust left in escrow.
| Milestone | % of Total | Locked Amount | Seller Payout |
|---|---|---|---|
| Phase 1 | 20% | $200.00 | $196.00 |
| Phase 2 | 35% | $350.00 | $343.00 |
| Phase 3 | 30% | $300.00 | $294.00 |
| Phase 4 (final) | 15% | $150.00 | $147.00 + rounding |
Example assumes a 2% total fee rate on a $1,000 escrow. Actual amounts depend on your configured platform and service fee rates.