# List milestones

Operation ID: `milestones`

Group: PSA › Milestones

[Open in the interactive reference](https://docs.api.lensing.ai/lensing/v1/endpoints/milestones)

`GET /psa/milestones`

Read-only: Reads and returns; changes nothing.

Lists milestones. The psaProjectId, invoiceId, productId, and productLineId on a milestone are numeric ids, not UUIDs, so you can't use them to read the project, invoice, product, or product line they point to.

Milestones can't be filtered by name or description, and no other endpoint finds a milestone by name.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | string · v1 | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |

## Query surface

- `id` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between, like
- `currency` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between, like
- `dueDate` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between
- `status` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between, like
- `billable` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between
- `psaProjectId` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between
- `lastModifiedDate` — filter by eq, ne, gt, gte, lt, lte, in, notIn, between
- Orderable: id, name, status, currency, dueDate, psaProjectId, lastModifiedDate
- Selectable: id, name, description, amount, currency, dueDate, status, billable, psaProjectId, invoiceId, productId, productLineId, lastModifiedDate
- `page[size]` — Page size
- `page[cursor]` — Opaque continuation cursor

## Response

`200` Success

Under `data`, an array of milestonesRow:

| Field | Type | Required | Filter | Sort | Description |
| --- | --- | --- | --- | --- | --- |
| `id` | string | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`), Pattern (`like`) | yes | The unique identifier for the milestone. Example: `"9b1c7f42-3d8a-4e05-b6c1-27fa5e903d18"`. |
| `name` | string \| null | no | no | yes | The display name of the milestone. Example: `"Phase 1 delivery"`. |
| `description` | string \| null | no | no | no | A free-form description of the milestone. Example: `"Signed off by the customer's PMO."`. |
| `amount` | decimal \| null | no | no | no | The milestone amount, in the currency given by currency. Example: `"15000.00"`. |
| `currency` | string \| null | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`), Pattern (`like`) | yes | The currency of amount. Format ISO 4217, for example 'USD'. Example: `"USD"`. |
| `dueDate` | date \| null | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`) | yes | The date the milestone is due for completion. Format YYYY-MM-DD. Example: `"2026-09-30"`. |
| `status` | string \| null | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`), Pattern (`like`) | yes | The status of the milestone. Possible values: 'pending', 'accomplished', 'invoiced'. Example: `"pending"`. |
| `billable` | boolean \| null | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`) | no | Whether the milestone is billable to a customer. Example: `true`. |
| `psaProjectId` | number | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`) | yes | The id of the PSA project this milestone belongs to. It is a numeric key, not the UUID that GET psa/projects/:id takes. Example: `32341`. |
| `invoiceId` | number \| null | no | no | no | The id of the invoice this milestone has been billed on. Absent when the milestone has not been invoiced. It is a numeric key, not the UUID that GET accounting/invoices/:id takes. |
| `productId` | number \| null | no | no | no | The id of the product this milestone is associated with. It is a numeric key, not the UUID that GET accounting/products/:id takes. |
| `productLineId` | number \| null | no | no | no | The id of the product line this milestone is associated with. It is a numeric key, not the UUID that GET accounting/product-lines/:id takes. |
| `lastModifiedDate` | date-time \| null | no | Exact (`eq`, `ne`), Range (`gt`, `gte`, `lt`, `lte`, `between`), Any of (`in`, `notIn`) | yes | The date and time the milestone record last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. Example: `"2026-08-14T11:20:31.004Z"`. |

```json
{
  "data": [
    {
      "id": "9b1c7f42-3d8a-4e05-b6c1-27fa5e903d18",
      "name": "Phase 1 delivery",
      "description": "Signed off by the customer's PMO.",
      "amount": "15000.00",
      "currency": "USD",
      "dueDate": "2026-09-30",
      "status": "pending",
      "billable": true,
      "psaProjectId": 32341,
      "invoiceId": 0,
      "productId": 0,
      "productLineId": 0,
      "lastModifiedDate": "2026-08-14T11:20:31.004Z"
    }
  ],
  "pagination": {
    "hasMore": true,
    "nextCursor": "<nextCursor>"
  }
}
```

## Statuses

- `400` — Invalid request or business failure
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
