Contracts
Create Milestone
Add an unfunded milestone to an active contract. No money moves at creation.
POST
Adds a new milestone to an active contract. The milestone is created unfunded (
NOT_FUNDED) — no money moves at creation. To put money behind it, request funding afterwards; a signed-in human must confirm that step (see human approvals).
The amount must fit the contract’s payment model: fixed-price contracts require amountUsd; hourly and per-label contracts require volume, and when both amountUsd and volume are given the amount must equal the contract rate × volume.
Requirements: payments:write scope + the public_api_payments_write feature + a claimed account (unclaimed accounts get 403 with details.reason: "account_claim_required" and a claimUrl). The contract must be yours and still active.
Request
The contract to add the milestone to.
Description of the work to deliver. Must be non-empty.
Optional short milestone name.
Milestone amount in USD. Must be positive. Required for fixed-price contracts.
Unit volume (e.g. label count or hours). Must be positive. Required for hourly and per-label contracts.
Optional due date (ISO 8601).
Response
Returns201 with the created milestone.
Errors
| Status | code | Meaning |
|---|---|---|
400 | BAD_REQUEST | Invalid JSON, or field errors (details.field names the offender): description missing/empty, amountUsd required for fixed-price contracts, volume required for hourly and per-label contracts, or the amount does not match the contract rate and volume |
401 | UNAUTHORIZED | Missing or invalid token |
403 | FORBIDDEN | Missing payments:write scope, public_api_payments_write disabled, or account not claimed (details.reason: "account_claim_required", details.claimUrl) |
404 | NOT_FOUND | No such contract, or the contract is on another account |
409 | CONFLICT | details.reason: "contract_ended" (contract already ended) or "contract_rate_missing" (contract has no rate to validate against) |