> For the complete documentation index, see [llms.txt](https://developer.laurel.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.laurel.ai/guides/public-api-endpoints.md).

# Public Api Endpoints

This is a document for all of our public API endpoints. Your specific endpoints will be provided to you by our solutions delivery team.

## Identity Service

* <https://identity.laurel.ai>

## Time Service

* <https://api.laurel.ai/time/>

### Starred initiatives

These endpoints add matters to, remove matters from, or list a user's starred (favorite) matters. Star and unstar are incremental: only the matters listed in the request are affected, and the rest of the user's starred list is left unchanged. Authenticate with an OAuth2 client-credentials token as described in [Authentication](/guides/authentication.md).

The `customerId` in the URL identifies the customer whose matters are being updated. The request identifies one target user with their customer-system timekeeper external ID. Each initiative is identified by its PMS matter external ID.

These endpoints only update the target user's starred state. They never grant or change access to a matter.

#### Star initiatives

```
POST /api/v1/public/customers/{customerId}/initiative-props/star
```

Adds the listed matters to the target user's starred list. Matters already starred are unaffected (the item still returns `200`).

Request:

```json
{
  "timekeeperExternalId": "TK-12345",
  "initiativeExternalIds": [
    "MATTER-1001",
    "MATTER-1002"
  ]
}
```

| Field                   | Type      | Required | Description                                                                                                       |
| ----------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `timekeeperExternalId`  | string    | Yes      | The target user's customer-system timekeeper external ID.                                                         |
| `initiativeExternalIds` | string\[] | Yes      | PMS matter external IDs. Must contain between 1 and 100 IDs per request. Duplicate IDs are silently deduplicated. |

#### Unstar initiatives

```
POST /api/v1/public/customers/{customerId}/initiative-props/unstar
```

Removes the listed matters from the target user's starred list. Matters that were not starred are unaffected (the item still returns `200`). The request body and validation rules are the same as for the star endpoint.

```json
{
  "timekeeperExternalId": "TK-12345",
  "initiativeExternalIds": [
    "MATTER-1001"
  ]
}
```

#### Response

Both endpoints return `207 Multi-Status`. Each requested matter has an individual result:

```json
{
  "response": [
    {
      "initiativeExternalId": "MATTER-1001",
      "status": 200
    },
    {
      "initiativeExternalId": "MATTER-1002",
      "status": 403,
      "errorCode": 20083,
      "errorMessage": "initiative access denied"
    }
  ]
}
```

| Per-item status | `errorCode` | Meaning                                                                                                                                                     |
| --------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`           | —           | Star or unstar succeeded. Returned status code will be `200` independent of the matter's previous star status; operations are idempotent and safe to retry. |
| `403`           | `20083`     | The target user does not have access to the matter.                                                                                                         |
| `404`           | `20080`     | The matter external ID was not found for the specified customer.                                                                                            |
| `422`           | `20081`     | The matter is inactive.                                                                                                                                     |
| `500`           | —           | Unexpected processing error.                                                                                                                                |

Requests are limited to 100 matters. Duplicate IDs in a request are silently deduplicated, so the response contains one entry per distinct ID. Clients should page larger sets across multiple requests. Star and unstar operations are idempotent and safe to retry.

If the supplied `timekeeperExternalId` cannot be resolved, the entire request fails with HTTP `404`; no per-item response is returned. If it resolves to multiple users, the entire request fails with HTTP `409`. An internal timekeeper-resolution failure returns HTTP `500`.

#### Get starred initiatives

```
GET /api/v1/public/customers/{customerId}/initiative-props/starred?timekeeperExternalId=TK-12345&limit=2500
```

Returns the target user's currently starred matters as PMS matter external IDs. Only matters the user still has access to are included. Matters the timekeeper created in Laurel rather than sourced from the PMS are never returned, so this list can be shorter than the starred list the timekeeper sees in Laurel.

| Query parameter        | Type    | Required | Description                                                                          |
| ---------------------- | ------- | -------- | ------------------------------------------------------------------------------------ |
| `timekeeperExternalId` | string  | Yes      | The target user's customer-system timekeeper external ID.                            |
| `limit`                | integer | No       | Number of raw pinned props to fetch. Must be between 1 and 2,500. Defaults to 2,500. |
| `cursor`               | string  | No       | Opaque cursor returned as `nextCursor` by the previous page.                         |

Response (`200 OK`):

```json
{
  "initiativeExternalIds": [
    "MATTER-1001",
    "MATTER-1002"
  ],
  "nextCursor": null
}
```

When more results are available, `nextCursor` contains an opaque cursor for the next page. It is `null` on the last page. Always continue paging until `nextCursor` is `null` — a page may contain fewer than `limit` items (even zero) while more results remain, because inaccessible and non-PMS matters are filtered out after the page is fetched. An invalid cursor returns HTTP `400`. An unknown `timekeeperExternalId` returns HTTP `404`; an ambiguous timekeeper external ID returns HTTP `409`; and an internal resolution failure returns HTTP `500`. The caller's credentials must be scoped to the `customerId` in the URL.

## Ingestion Service

* <https://api.laurel.ai/ingestion/>
