# List milestones

`GET /api/v1/milestones`

Contractor milestones, newest first, with the row id breaking equal timestamps: how each was verified, and whether it was paid. Only a real `0x` transaction is exposed as `txHash`.

Send a [workspace API key](https://www.vestiarion.xyz/docs/get-started/authentication) as `Authorization: Bearer <key>`.

## Parameters

| Name | In | Type | Required | Default | Allowed values | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `limit` | query | integer | Optional | `50` | 1 to 200 | How many items to return. Defaults to 50; a larger value is capped at 200. |
| `cursor` | query | string | Optional | — | — | The previous response's `page.nextCursor`, passed back unchanged to continue. Opaque: never decode or construct one. A cursor this endpoint could not have issued is refused with `400`. |
| `status` | query | string | Optional | — | `pending`, `verified`, `paid`, `held`, `closed` | Only milestones in this status. |
| `contractorId` | query | string | Optional | — | — | Only milestones for this contractor. |

## Code samples

```bash
curl "https://www.vestiarion.xyz/api/v1/milestones" \
  -H "Authorization: Bearer $VESTIARION_API_KEY"
```

## Response

Example, `200` `application/json`:

```json
{
  "data": [
    {
      "id": "d01b2b49-2ca3-45fe-898c-f2c0128f6188",
      "title": "Landing page redesign — milestone 2",
      "amount": 0.9,
      "status": "paid",
      "verificationSource": "timesheet:kimai",
      "verificationMethod": "seed",
      "verificationStatus": "verified",
      "verificationCheckedAt": null,
      "verifiedAt": "2026-09-24T11:54:57.14+00:00",
      "verificationDetail": {
        "fixture": true
      },
      "verified": true,
      "decidedAt": "2026-09-24T11:55:54.276+00:00",
      "settledAt": "2026-09-24T11:55:54.276+00:00",
      "closedAt": null,
      "closeReason": null,
      "agentReasoning": "Milestone 'Landing page redesign — milestone 2' for 0.9 USDC is verified via timesheet:kimai, contractor Diego Ramirez has riskLevel 'clear' (not high), and the amount 0.9 is below his paymentLimit of 2.5. No duplicate invoice or PO mismatch indicated, and verification source is confirmed, so immediate release is justified rather than deferring to Net-30.",
      "txHash": "0xa710040ac59af4501a4c91f70287f1fecec1aad037e5d0fc2141fe270f81d969",
      "contractor": {
        "id": "5541f1a4-4e48-4fa5-880c-9ffc97d8953b",
        "name": "Diego Ramirez — Design Contractor",
        "riskLevel": "clear"
      },
      "createdAt": "2026-09-24T11:54:57.933771+00:00"
    }
  ],
  "page": {
    "nextCursor": "eyJrIjoiMjAyNi0wOS0yNFQxMTo1NDo1Ny45MzM3NzErMDA6MDAiLCJpZCI6ImQwMWIyYjQ5LTJjYTMtNDVmZS04OThjLWYyYzAxMjhmNjE4OCJ9",
    "hasMore": true,
    "count": 1
  }
}
```

**Fields**

- `data` (array of object, required)
  - `id` (string, required)
  - `title` (string, required)
  - `amount` (number, required)
  - `status` (string, required) One of `pending`, `verified`, `paid`, `held`, `closed`.
  - `verificationSource` (string, nullable, required)
  - `verificationMethod` (string, required) One of `unverified`, `github`, `manual`, `seed`.
  - `verificationStatus` (string, required) One of `unverified`, `verified`, `not_merged`, `unavailable`, `failed`.
  - `verificationCheckedAt` (string, nullable, required)
  - `verifiedAt` (string, nullable, required)
  - `verificationDetail` (object, required)
  - `verified` (boolean, required)
  - `decidedAt` (string, nullable, required)
  - `settledAt` (string, nullable, required)
  - `closedAt` (string, nullable, required): When a person closed the milestone without paying it (status closed), else null.
  - `closeReason` (string, nullable, required): The reason the person gave for closing it without paying, else null.
  - `agentReasoning` (string, nullable, required)
  - `txHash` (string, nullable, required): An on-chain hash when the payment settled on Arc, else null.
  - `contractor` (object, nullable, required)
    - `id` (string, required)
    - `name` (string, required)
    - `riskLevel` (string, required)
  - `createdAt` (string, required)
- `page` (object, required): Where this page sits in the collection.
  - `nextCursor` (string, nullable, required): Pass back as `?cursor=` to continue. Null when the end is reached.
  - `hasMore` (boolean, required): Whether another page follows this one.
  - `count` (integer, required): How many items this response carries.

## 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." |
| 500 | `internal` | An unexpected server error. Implementation details are not exposed. |
