API reference
Add a milestone
/ api/ v1/ milestonesAdds work a contractor is to be paid for, checked by the rules of the console's Add milestone form, and recorded in the ledger as create_milestone with via: "api" and the key's id. It is added as the key's issuer's: if the agent holds it on its own judgment, someone other than the issuer must choose Pay now, unless the issuer is the workspace's only approver.
It starts pending, and the API cannot verify it. A GitHub pull request in verificationSource is checked by the agent, which verifies the milestone once the pull request is merged; any other link is evidence for the person who verifies it on Contractors. Once it is verified, the agent decides the payment with every guardrail, and pays only to the contractor's confirmed address. A contractorId the workspace does not hold, or a client's, answers 400.
Send a workspace API key with read and write access as Authorization: Bearer …. A read-only key gets 403.
Parameters
Idempotency-Keystringoptionalin: headerMakes a retry safe: up to 255 printable ASCII characters, unique to the record being added, such as its id in your own system. A repeat with the same key and the same body within 24 hours gets the first answer back, with
Idempotent-Replayed: true, and adds nothing. The same key with a different body answers409.
Request body
Example{
"contractorId": "26d6ffed-356d-474a-8d42-89bc942f6b4e",
"title": "TypeScript SDK for the API",
"amount": "0.10",
"verificationSource": "https://github.com/duongnq2798/vestiarion/pull/176"
}contractorIdstringThe
idof the contractor or vendor to pay, fromGET /api/v1/counterpartiesor from the answer that added it. A client is not paid for milestones.titlestringWhat was delivered, 3 to 160 characters.
amountstring | numberWhat the work is paid, in USDC, with at most 6 decimal places. A decimal string such as
"250.00"keeps it exact; a number is read the same way.verificationSourcestring · optionalA link to the delivered work: https, up to 500 characters. A GitHub pull request (
https://github.com/<owner>/<repo>/pull/<number>) is checked by the agent, which verifies the milestone once it is merged. Any other link is evidence for the person who verifies the milestone on Contractors.
Try it
Try it is off for operations that add records: run the sample with your own key.
Code samples
curl "https://www.vestiarion.xyz/api/v1/milestones" \
-X POST \
-H "Authorization: Bearer $VESTIARION_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ci-bounty-pr-176" \
-d '{"contractorId":"26d6ffed-356d-474a-8d42-89bc942f6b4e","title":"TypeScript SDK for the API","amount":"0.10","verificationSource":"https://github.com/duongnq2798/vestiarion/pull/176"}'Response
Example{
"data": {
"id": "354132aa-e7f6-46f8-ae3c-aa6809f9b9b6",
"title": "TypeScript SDK for the API",
"amount": 0.1,
"status": "pending",
"verificationSource": "https://github.com/duongnq2798/vestiarion/pull/176",
"verificationMethod": "unverified",
"verificationStatus": "unverified",
"verificationCheckedAt": null,
"verifiedAt": null,
"verificationDetail": {},
"verified": false,
"decidedAt": null,
"settledAt": null,
"closedAt": null,
"closeReason": null,
"agentReasoning": null,
"txHash": null,
"contractor": {
"id": "26d6ffed-356d-474a-8d42-89bc942f6b4e",
"name": "API Test Contractor",
"riskLevel": "clear"
},
"createdAt": "2026-10-03T15:43:09.204631+00:00"
}
}dataobjectA contractor milestone, how it was verified, and whether it was paid.
19 fields in data
idstringtitlestringamountnumberstatusstringOne of
pendingverifiedpaidheldclosedverificationSourcestring · nullableverificationMethodstringOne of
unverifiedgithubmanualseedverificationStatusstringOne of
unverifiedverifiednot_mergedunavailablefailedverificationCheckedAtstring · nullableverifiedAtstring · nullableverificationDetailobjectverifiedbooleandecidedAtstring · nullablesettledAtstring · nullableclosedAtstring · nullableWhen a person closed the milestone without paying it (status closed), else null.
closeReasonstring · nullableThe reason the person gave for closing it without paying, else null.
agentReasoningstring · nullabletxHashstring · nullableAn on-chain hash when the payment settled on Arc, else null.
contractorobject · nullable3 fields in contractor
idstringnamestringriskLevelstring
createdAtstring
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | An invalid limit or cursor, a filter value outside its allowed values, or a request body that does not validate. The message names the parameter or the field, and lists the accepted values. |
| 401 | unauthorized | No key, or a malformed, unknown or revoked one: "A valid API key is required." |
| 403 | forbidden | The key's scopes do not cover this route: "This key cannot do that." Or, on a write, the person who created the key can no longer add records: "This key's issuer can no longer add records in this workspace." |
| 409 | conflict | The Idempotency-Key was already used for a different request, or the first request with it is still being handled. |
| 429 | rate_limited | Too many requests; wait as long as Retry-After says before trying again. |
| 500 | internal | An unexpected server error. Implementation details are not exposed. |