Skip to content
VestiarionDocs

API reference

Add a milestone

POST/api/v1/milestones

Adds 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: header

    Makes 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 answers 409.

Request body

Example
application/json
{
  "contractorId": "26d6ffed-356d-474a-8d42-89bc942f6b4e",
  "title": "TypeScript SDK for the API",
  "amount": "0.10",
  "verificationSource": "https://github.com/duongnq2798/vestiarion/pull/176"
}
Fields
  • contractorIdstring

    The id of the contractor or vendor to pay, from GET /api/v1/counterparties or from the answer that added it. A client is not paid for milestones.

  • titlestring

    What was delivered, 3 to 160 characters.

  • amountstring | number

    What 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 · optional

    A 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
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
201 · application/json
{
  "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"
  }
}
Fields
  • dataobject

    A contractor milestone, how it was verified, and whether it was paid.

    19 fields in data
    • idstring
    • titlestring
    • amountnumber
    • statusstring

      One ofpendingverifiedpaidheldclosed

    • verificationSourcestring · nullable
    • verificationMethodstring

      One ofunverifiedgithubmanualseed

    • verificationStatusstring

      One ofunverifiedverifiednot_mergedunavailablefailed

    • verificationCheckedAtstring · nullable
    • verifiedAtstring · nullable
    • verificationDetailobject
    • verifiedboolean
    • decidedAtstring · nullable
    • settledAtstring · nullable
    • closedAtstring · nullable

      When a person closed the milestone without paying it (status closed), else null.

    • closeReasonstring · nullable

      The reason the person gave for closing it without paying, else null.

    • agentReasoningstring · nullable
    • txHashstring · nullable

      An on-chain hash when the payment settled on Arc, else null.

    • contractorobject · nullable
      3 fields in contractor
      • idstring
      • namestring
      • riskLevelstring
    • createdAtstring

Errors

StatusCodeWhen
400invalid_requestAn 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.
401unauthorizedNo key, or a malformed, unknown or revoked one: "A valid API key is required."
403forbiddenThe 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."
409conflictThe Idempotency-Key was already used for a different request, or the first request with it is still being handled.
429rate_limitedToo many requests; wait as long as Retry-After says before trying again.
500internalAn unexpected server error. Implementation details are not exposed.