---
title: "API reference"
description: "Chaque endpoint REST et scope Cadae : authentification, codes d'erreur, filtres de liste et limites pour api.cadae.ai/v1."
language: fr-FR
canonical: https://cadae.ai/fr-FR/docs/api-reference
---

# API reference

Chaque endpoint REST et scope Cadae : authentification, codes d'erreur, filtres de liste et limites pour api.cadae.ai/v1.


Base URL: `https://api.cadae.ai`. Interactive documentation lives at
[`/v1/docs`](https://api.cadae.ai/v1/docs), generated from the published
OpenAPI description.

## Authentication

Send a bearer token in the `Authorization` header. Three kinds:

| Prefix | Kind | Acts as |
| --- | --- | --- |
| `cad_osk_…` | Portfolio token | The Portfolio (created by an Owner) |
| `cad_pat_…` | Personal token | You, scopes capped by your role |
| `cad_at_…` | OAuth access token | A consenting user |

Create a Portfolio or personal token in **Settings → Developer**. An OAuth
client gets its access token through the flow in [OAuth & scopes](https://cadae.ai/fr-FR/docs/oauth).

A request is refused with **`401`** only when the credential itself fails: it is
missing, malformed, unknown, revoked or expired, or it names an account that no
longer exists, such as a `cad_osk_` token whose creator's account was deleted.
A credential that is valid but not allowed is refused with **`403`**: the person
behind it has no active membership in the Portfolio (suspended, removed or never
a member), a `cad_osk_` token's creator is not an Owner, or the token belongs to
a Cadae Super Admin (who cannot hold API tokens).
REST and MCP answer the same way, and a REST `403` on a scope check also names
the `requiredScope`.

## Errors

Every non-2xx answer is `{ error, code, reason?, requiredScope? }`. `error` is a
sentence, `code` is the machine-readable reason — branch on that, not on the
words. `reason` narrows a `code` when the refusal is one you may want to tell
apart from its neighbours; today the only value is `MODULE_DISABLED`:

| Status | `code` | When |
| --- | --- | --- |
| `400` | `BAD_REQUEST` | the input failed validation |
| `401` | `UNAUTHORIZED` | the credential itself is not one |
| `403` | `FORBIDDEN` | the credential is valid and its holder or its scope does not cover this; a scope refusal also carries `requiredScope`, and a hotel whose module is switched off carries `reason: "MODULE_DISABLED"` |
| `404` | `NOT_FOUND` | no such row |
| `409` | `CONFLICT` | the world moved while your command was in flight, or the name is taken |
| `412` | `PRECONDITION_FAILED` | a condition you can satisfy is unmet |
| `429` | `TOO_MANY_REQUESTS` | too many requests |

`reason: "MODULE_DISABLED"` means the Hotel does not have that module enabled —
it is **not** an empty result and must never be rendered as a zero. Read
`GET /v1/hotels/{hotelId}/modules` for the Hotel's own flags. A `403` with no
`reason` is an access refusal, and says nothing about the Hotel's modules.

## List filters

A repeated query parameter is an array: `?type=role_changed&type=member_invited`
(`style: form, explode: true`, as the OpenAPI document declares). A comma is
never a separator — `?name=Grand,Central` is one value.

## Scopes and endpoints

Scopes are capped by the holder's role in the Portfolio, from the permission
matrix: `organisation.write` is Owner only; `hotel.read|write`,
`members.read|write`, `invites.write` and `activities.read` need an Owner or an
Administrator. A personal token can never carry a scope its holder could not use.

{/* parity:doors — generated. Do not edit by hand. */}

### Scopes

`viewer.read` · `organisation.read` · `organisation.write` · `hotel.read` · `hotel.write` · `members.read` · `members.write` · `invites.write` · `activities.read` · `notifications.read` · `phi.read` · `phi.write` · `projects.read` · `projects.write` · `projects.approve` · `budgets.write` · `tasks.read` · `tasks.write` · `documents.read` · `documents.write` · `events.read` · `events.write` · `risks.read` · `risks.write`

…plus the aliases `apis.all` and `apis.read`. More scope names are reserved for capabilities
not yet released; a token can only carry one an endpoint below needs.

### Endpoints

| Endpoint | Scope | What |
| --- | --- | --- |
| `GET /v1/access-events` | `activities.read` | Access and membership audit events |
| `GET /v1/activities` | `activities.read` | Portfolio audit feed |
| `GET /v1/asset-types` | `projects.read` | Asset types this Portfolio can use, with their active configurable fields |
| `GET /v1/context` | `viewer.read` | Resolve and re-authorise a route context |
| `GET /v1/context/landing` | `viewer.read` | Where the signed-in user lands |
| `GET /v1/context/tree` | `viewer.read` | Authorised Portfolios and hotels for the switcher |
| `GET /v1/documents` | `documents.read` | Documents attached to a record, or held at a scope |
| `GET /v1/documents/{documentId}` | `documents.read` | One document, with its versions and links |
| `POST /v1/documents/{documentId}/links` | `documents.write` | Attach a document to a record |
| `DELETE /v1/documents/{documentId}/links` | `documents.write` | Detach a document from a record |
| `POST /v1/documents/{documentId}/versions` | `documents.write` | Replace a document with a new version |
| `POST /v1/documents/references` | `documents.write` | Record reference-only evidence and link it to the record it supports |
| `POST /v1/documents/upload-url` | `documents.write` | Reserve a document and mint a signed upload URL |
| `GET /v1/documents/versions/{versionId}/download` | `documents.read` | Download a document version (REST streams the file) |
| `GET /v1/documents/versions/{versionId}/download-url` | `documents.read` | Mint a signed download URL for a document version |
| `POST /v1/documents/versions/{versionId}/finalise` | `documents.write` | Finalise an uploaded document version |
| `POST /v1/documents/versions/{versionId}/mark` | `documents.write` | Mark a version superseded or added in error |
| `GET /v1/hotel-names/check` | `hotel.write` | Would this hotel name be refused as a duplicate? |
| `GET /v1/hotels` | `hotel.read` | Hotels I can access |
| `POST /v1/hotels` | `hotel.write` | Create hotel |
| `GET /v1/hotels-compare` | `phi.read` | Up to five hotels side by side |
| `GET /v1/hotels-league-table` | `phi.read` | The Portfolio's hotels league table |
| `GET /v1/hotels-league-table/export` | `phi.read` | The Portfolio's hotels league table as CSV (REST streams the file) |
| `GET /v1/hotels/{hotelId}` | `hotel.read` | One hotel |
| `PATCH /v1/hotels/{hotelId}` | `hotel.write` | Update hotel |
| `GET /v1/hotels/{hotelId}/activity` | `activities.read` | A hotel's activity |
| `GET /v1/hotels/{hotelId}/annual-confirmations` | `phi.read` | The hotel's annual property-model confirmations |
| `POST /v1/hotels/{hotelId}/annual-confirmations/{confirmationId}/confirm` | `phi.write` | Answer an annual property-model confirmation |
| `GET /v1/hotels/{hotelId}/applicability-overrides` | `phi.read` | The hotel's applicability overrides |
| `GET /v1/hotels/{hotelId}/assumptions` | `phi.read` | The hotel's assumption needs and values |
| `POST /v1/hotels/{hotelId}/assumptions` | `phi.write` | Propose that an assumption is needed |
| `GET /v1/hotels/{hotelId}/assumptions/approved` | `phi.read` | The hotel's approved assumptions for a period |
| `GET /v1/hotels/{hotelId}/bill-inbox-address` | `phi.read` | The hotel's bill forwarding address |
| `POST /v1/hotels/{hotelId}/bill-inbox-address` | `phi.write` | Mint the hotel's bill forwarding address |
| `POST /v1/hotels/{hotelId}/bill-suggestions/{suggestionId}/confirm` | `phi.write` | Confirm a suggested match |
| `POST /v1/hotels/{hotelId}/bill-suggestions/{suggestionId}/decline` | `phi.write` | Decline a suggested match |
| `GET /v1/hotels/{hotelId}/bills` | `phi.read` | The hotel's bill inbox |
| `POST /v1/hotels/{hotelId}/bills` | `phi.write` | Add a finalised document to the bill inbox |
| `GET /v1/hotels/{hotelId}/bills/{billDocumentId}` | `phi.read` | One inbound bill and its suggested matches |
| `POST /v1/hotels/{hotelId}/bills/{billDocumentId}/archive` | `phi.write` | Archive an inbound bill |
| `POST /v1/hotels/{hotelId}/bills/{billDocumentId}/attach` | `phi.write` | Attach a bill to a source and a draft month by hand |
| `POST /v1/hotels/{hotelId}/bills/{billDocumentId}/retry-extraction` | `phi.write` | Read an inbound bill again |
| `GET /v1/hotels/{hotelId}/bills/{billDocumentId}/suggestions` | `phi.read` | The suggested matches for one bill |
| `GET /v1/hotels/{hotelId}/boundary-rules` | `phi.read` | The hotel's calculation-boundary rules |
| `POST /v1/hotels/{hotelId}/boundary-rules` | `phi.write` | Propose a calculation-boundary rule |
| `GET /v1/hotels/{hotelId}/boundary-rules/approved` | `phi.read` | The hotel's boundary rules in force on a date (approved, or superseded after it) |
| `GET /v1/hotels/{hotelId}/budgets/{budgetYear}` | `projects.read` | The delegated budget position for one hotel and budget year |
| `PUT /v1/hotels/{hotelId}/budgets/{budgetYear}` | `budgets.write` | Set a hotel's delegated budget for one budget year |
| `GET /v1/hotels/{hotelId}/budgets/{budgetYear}/ledger` | `projects.read` | The append-only budget ledger for one hotel and budget year |
| `GET /v1/hotels/{hotelId}/correction-requests` | `phi.read` | The hotel's correction requests and Cadae's answers |
| `GET /v1/hotels/{hotelId}/data-gaps` | `phi.read` | The hotel's open data gaps |
| `POST /v1/hotels/{hotelId}/data-gaps/{gapId}/acknowledge` | `phi.write` | Acknowledge a data gap |
| `GET /v1/hotels/{hotelId}/data-sources` | `phi.read` | The hotel's data-source register |
| `POST /v1/hotels/{hotelId}/data-sources` | `phi.write` | Register a data source for the hotel |
| `PATCH /v1/hotels/{hotelId}/data-sources/{dataSourceId}` | `phi.write` | Change an active data source of the hotel |
| `POST /v1/hotels/{hotelId}/data-sources/{dataSourceId}/close` | `phi.write` | Close a data source of the hotel |
| `POST /v1/hotels/{hotelId}/data-sources/{dataSourceId}/replace` | `phi.write` | Replace a meter of the hotel, with its closing and opening readings |
| `GET /v1/hotels/{hotelId}/data-sources/active` | `phi.read` | The hotel's data sources active on a date |
| `POST /v1/hotels/{hotelId}/lifecycle` | `hotel.write` | Set hotel lifecycle status |
| `GET /v1/hotels/{hotelId}/members` | `members.read` | Hotel members |
| `POST /v1/hotels/{hotelId}/members` | `members.write` | Grant hotel access |
| `DELETE /v1/hotels/{hotelId}/members/{userId}` | `members.write` | Revoke hotel access |
| `GET /v1/hotels/{hotelId}/modules` | `hotel.read` | Modules enabled for a hotel |
| `POST /v1/hotels/{hotelId}/period-findings/{findingId}/explain` | `phi.write` | Explain a validation finding |
| `GET /v1/hotels/{hotelId}/periods` | `phi.read` | The hotel's reporting months |
| `GET /v1/hotels/{hotelId}/periods/{month}` | `phi.read` | One reporting month |
| `PATCH /v1/hotels/{hotelId}/periods/{month}` | `phi.write` | Save a month's draft data |
| `POST /v1/hotels/{hotelId}/periods/{month}/correction-requests` | `phi.write` | Ask Cadae to correct a locked or published month |
| `GET /v1/hotels/{hotelId}/periods/{month}/history` | `phi.read` | The input versions of one reporting month |
| `GET /v1/hotels/{hotelId}/periods/{month}/readiness` | `phi.read` | The month's ready-to-submit checklist |
| `POST /v1/hotels/{hotelId}/periods/{month}/submit` | `phi.write` | Submit a month to Cadae |
| `GET /v1/hotels/{hotelId}/periods/{month}/submitted-version` | `phi.read` | The submitted input version of a month |
| `POST /v1/hotels/{hotelId}/periods/{month}/validate` | `phi.write` | Re-run the validation catalogue over a month |
| `POST /v1/hotels/{hotelId}/periods/baseline` | `phi.write` | Create the hotel's baseline reporting months |
| `GET /v1/hotels/{hotelId}/phi-contacts` | `phi.read` | The hotel's PHI data contacts |
| `PUT /v1/hotels/{hotelId}/phi-contacts` | `phi.write` | Set the hotel's PHI data contacts |
| `GET /v1/hotels/{hotelId}/phi-summary/actions` | `phi.read` | A hotel's action progress: Savings Task buckets, Projects by stage and what completed in the period |
| `GET /v1/hotels/{hotelId}/phi-summary/money` | `phi.read` | A hotel's money story: savings realised in the period, the remaining opportunity and the investment return — three figures, never added |
| `GET /v1/hotels/{hotelId}/phi-summary/risks` | `phi.read` | A hotel's risk summary: open findings by severity, the oldest unresolved and the top types |
| `GET /v1/hotels/{hotelId}/phi/dashboard/category-drivers` | `phi.read` | The hotel's largest PHI category drivers |
| `GET /v1/hotels/{hotelId}/phi/dashboard/coverage` | `phi.read` | The hotel's PHI data coverage |
| `GET /v1/hotels/{hotelId}/phi/dashboard/intensity` | `phi.read` | The hotel's operational intensity measures |
| `GET /v1/hotels/{hotelId}/phi/dashboard/resource-trend` | `phi.read` | One resource's monthly trend |
| `GET /v1/hotels/{hotelId}/phi/dashboard/series` | `phi.read` | The hotel's PHI over time |
| `GET /v1/hotels/{hotelId}/phi/dashboard/summary` | `phi.read` | The hotel dashboard's headline cards and notices |
| `GET /v1/hotels/{hotelId}/phi/dashboard/yearly-totals` | `phi.read` | One resource's published calendar-year totals |
| `GET /v1/hotels/{hotelId}/phi/import-mappings` | `phi.read` | A hotel's saved import source mappings |
| `POST /v1/hotels/{hotelId}/phi/imports` | `phi.write` | Open a staged import over an uploaded workbook |
| `GET /v1/hotels/{hotelId}/phi/imports` | `phi.read` | A hotel's staged imports |
| `GET /v1/hotels/{hotelId}/phi/results` | `phi.read` | The hotel's published PHI results |
| `GET /v1/hotels/{hotelId}/phi/results/for-projects` | `phi.read` | Published PHI quantities with their publication ids |
| `GET /v1/hotels/{hotelId}/phi/results/latest` | `phi.read` | The hotel's latest published PHI result |
| `GET /v1/hotels/{hotelId}/phi/source-selections` | `phi.read` | The source selections recorded for a hotel |
| `GET /v1/hotels/{hotelId}/phi/template` | `phi.read` | The hotel's PHI data template (REST streams the .xlsx workbook) |
| `POST /v1/hotels/{hotelId}/primary-contact` | `members.write` | Set a hotel's primary contact |
| `GET /v1/hotels/{hotelId}/project-assets` | `projects.read` | A hotel's asset records, with their configured field answers |
| `POST /v1/hotels/{hotelId}/project-assets` | `projects.write` | Create an asset record, with its first configured answers |
| `GET /v1/hotels/{hotelId}/projects-setup` | `projects.read` | A hotel's Projects setup: baselines, price forecasts, delegated budget and readiness |
| `PATCH /v1/hotels/{hotelId}/projects-setup` | `projects.write` | Save a hotel's Projects baselines and price forecasts |
| `GET /v1/hotels/{hotelId}/projects-setup/baselines` | `projects.read` | A hotel's annual Energy and Water baselines, with what PHI proposes |
| `PUT /v1/hotels/{hotelId}/projects-setup/baselines/{resource}` | `projects.write` | Accept PHI's baseline, or override it with a reason |
| `GET /v1/hotels/{hotelId}/projects-setup/prices` | `projects.read` | A hotel's utility prices for this budget year and the next three, VAT-exclusive |
| `PUT /v1/hotels/{hotelId}/projects-setup/prices` | `projects.write` | Set utility prices, or a yearly % increase, for the current budget year and the next three |
| `GET /v1/hotels/{hotelId}/projects-setup/readiness` | `projects.read` | Whether a hotel's Projects setup is ready, and what is missing |
| `GET /v1/hotels/{hotelId}/projects-summary/pipeline` | `projects.read` | One hotel's remaining opportunity: gross, adjusted for overlaps, on hold and awaiting verification |
| `GET /v1/hotels/{hotelId}/property-model` | `phi.read` | The hotel's property model, with every record |
| `GET /v1/hotels/{hotelId}/property-model/approved` | `phi.read` | The approved property model on a date or across a period |
| `POST /v1/hotels/{hotelId}/property-model/building-sections` | `phi.write` | Create or change a building section of the hotel's draft property model |
| `DELETE /v1/hotels/{hotelId}/property-model/building-sections/{logicalId}` | `phi.write` | Remove a building section from the hotel's draft property model |
| `PATCH /v1/hotels/{hotelId}/property-model/draft` | `phi.write` | Save one section of the hotel's draft property model |
| `POST /v1/hotels/{hotelId}/property-model/facility-closures` | `phi.write` | Record or change a closure in the hotel's draft property model |
| `DELETE /v1/hotels/{hotelId}/property-model/facility-closures/{logicalId}` | `phi.write` | Remove a closure from the hotel's draft property model |
| `PUT /v1/hotels/{hotelId}/property-model/facility-presence/{kind}` | `phi.write` | Answer whether the hotel has a facility |
| `POST /v1/hotels/{hotelId}/property-model/facility-systems` | `phi.write` | Record or change a facility system in the hotel's draft property model |
| `PUT /v1/hotels/{hotelId}/property-model/facility-systems/{logicalId}/areas` | `phi.write` | Set the usage areas a facility system serves |
| `POST /v1/hotels/{hotelId}/property-model/facility-systems/{logicalId}/close` | `phi.write` | Close a facility system of the hotel's draft property model |
| `GET /v1/hotels/{hotelId}/property-model/field-history` | `phi.read` | Every value one property-model field has held |
| `GET /v1/hotels/{hotelId}/property-model/review` | `phi.read` | The property model's review: section states, totals and submission blockers |
| `GET /v1/hotels/{hotelId}/property-model/sections` | `phi.read` | The property model's sections, fields and answers |
| `GET /v1/hotels/{hotelId}/property-model/setup-overview` | `phi.read` | The hotel's PHI setup overview: workstreams, readiness and blockers |
| `POST /v1/hotels/{hotelId}/property-model/submit` | `phi.write` | Submit the hotel's property model for Cadae review |
| `POST /v1/hotels/{hotelId}/property-model/usage-areas` | `phi.write` | Create or change a usage area of the hotel's draft property model |
| `DELETE /v1/hotels/{hotelId}/property-model/usage-areas/{logicalId}` | `phi.write` | Remove a usage area from the hotel's draft property model |
| `GET /v1/hotels/{hotelId}/property-model/versions` | `phi.read` | The hotel's property-model versions |
| `GET /v1/hotels/{hotelId}/source-record-charges` | `phi.read` | The charge lines of a set of records |
| `POST /v1/hotels/{hotelId}/source-records/{recordId}/mark-duplicate` | `phi.write` | Mark a record as a duplicate of another |
| `POST /v1/hotels/{hotelId}/source-records/{recordId}/resolve-duplicate` | `phi.write` | Clear a record's duplicate flag |
| `POST /v1/hotels/{hotelId}/source-records/{recordId}/void` | `phi.write` | Void a utility source record |
| `GET /v1/hotels/{hotelId}/tasks` | `tasks.read` | A hotel's Savings Tasks across its Projects, with overdue derived in the hotel's time zone |
| `GET /v1/invites` | `members.read` | List invitations |
| `POST /v1/invites` | `invites.write` | Invite a member |
| `PATCH /v1/invites/{invitationId}` | `invites.write` | Edit and resend an invitation |
| `POST /v1/invites/{invitationId}/cancel` | `invites.write` | Cancel an invitation |
| `POST /v1/invites/{invitationId}/resend` | `invites.write` | Resend an invitation |
| `POST /v1/invites/{invitationId}/retry-delivery` | `invites.write` | Retry a failed invitation delivery |
| `POST /v1/invites/batch` | `invites.write` | Invite several people |
| `GET /v1/members` | `members.read` | Portfolio members |
| `PATCH /v1/members/{userId}` | `members.write` | Change a member's role, department or hotels |
| `DELETE /v1/members/{userId}` | `members.write` | Remove a member from the Portfolio |
| `POST /v1/members/{userId}/access-instructions` | `members.write` | Email a member their sign-in instructions |
| `GET /v1/members/{userId}/removal-preview` | `members.read` | Open work and assignments a removal would affect |
| `POST /v1/members/{userId}/restore` | `members.write` | Restore a suspended Portfolio membership |
| `POST /v1/members/{userId}/suspend` | `members.write` | Suspend a Portfolio membership |
| `GET /v1/notifications` | `notifications.read` | My notifications |
| `GET /v1/organisations` | `organisation.read` | Portfolios I can open |
| `GET /v1/organisations/{organisationId}` | `organisation.read` | Portfolio detail |
| `PATCH /v1/organisations/{organisationId}` | `organisation.write` | Update a Portfolio |
| `GET /v1/people` | `members.read` | People and access |
| `GET /v1/people-summary` | `members.read` | People and access summary counts |
| `GET /v1/people/{userId}` | `members.read` | One person's access |
| `POST /v1/people/check-contacts` | `members.read` | Check contacts before inviting |
| `GET /v1/phi-events` | `events.read` | PHI events I can see, filtered |
| `POST /v1/phi-events` | `events.write` | Create a PHI event as a draft |
| `GET /v1/phi-events-timeline` | `events.read` | The PHI event markers for a chart |
| `GET /v1/phi-events/{id}` | `events.read` | One PHI event |
| `PATCH /v1/phi-events/{id}` | `events.write` | Edit a PHI event |
| `POST /v1/phi-events/{id}/archive` | `events.write` | Archive a PHI event |
| `POST /v1/phi-events/{id}/date` | `events.write` | Move a PHI event's date |
| `POST /v1/phi-events/{id}/publish` | `events.write` | Publish a PHI event |
| `GET /v1/phi-summary/actions` | `phi.read` | The Portfolio's action progress: Savings Task buckets, Projects by stage and what completed in the period |
| `GET /v1/phi-summary/money` | `phi.read` | The Portfolio's money story: savings realised in the period, the remaining opportunity and the investment return — three figures, never added |
| `GET /v1/phi-summary/risks` | `phi.read` | The Portfolio's risk summary: open findings by severity, the oldest unresolved, the top types and the hotels affected |
| `GET /v1/phi/categories` | `phi.read` | The Portfolio's PHI category breakdown |
| `GET /v1/phi/comparison-groups` | `phi.read` | The Portfolio dashboard's four hotel comparison groups |
| `GET /v1/phi/confidence` | `phi.read` | Each hotel's PHI confidence, behind the Portfolio counts |
| `GET /v1/phi/context-markers` | `phi.read` | The Portfolio's cohort context markers |
| `GET /v1/phi/coverage` | `phi.read` | The Portfolio's PHI data coverage |
| `GET /v1/phi/dashboard/points/{resultId}` | `phi.read` | One point of the PHI series, in detail |
| `GET /v1/phi/imports/{batchId}` | `phi.read` | One staged import |
| `POST /v1/phi/imports/{batchId}/confirm` | `phi.write` | Apply a validated import to the hotel's drafts |
| `POST /v1/phi/imports/{batchId}/discard` | `phi.write` | Discard a staged import |
| `POST /v1/phi/imports/{batchId}/remap` | `phi.write` | Map several source labels to registered meters |
| `GET /v1/phi/imports/{batchId}/rows` | `phi.read` | The staged rows of one import |
| `POST /v1/phi/imports/{batchId}/rows/{rowId}/resolve` | `phi.write` | Decide what to do with one staged row |
| `POST /v1/phi/imports/{batchId}/validate` | `phi.write` | Parse and classify a staged import |
| `GET /v1/phi/intensity` | `phi.read` | The Portfolio's intensity measures, as ratios of totals |
| `GET /v1/phi/publications/{publicationId}` | `phi.read` | One PHI publication and the windows it published |
| `GET /v1/phi/publications/{publicationId}/history` | `phi.read` | The correction history of a published window |
| `GET /v1/phi/resource-trend` | `phi.read` | One resource's monthly totals across the Portfolio |
| `GET /v1/phi/results/{resultId}/breakdown` | `phi.read` | One result's category totals and intensity measures |
| `GET /v1/phi/results/{resultId}/co2` | `phi.read` | One published month's CO₂ outputs |
| `GET /v1/phi/results/{resultId}/confidence` | `phi.read` | One result's confidence level and reason |
| `GET /v1/phi/results/{resultId}/cost` | `phi.read` | One published month's eligible cost and theoretical cost gap |
| `GET /v1/phi/summary` | `phi.read` | The Portfolio's PHI headline cards |
| `GET /v1/phi/trend` | `phi.read` | PHI over time, one line per hotel |
| `GET /v1/programmes` | `projects.read` | The Portfolio's programme labels |
| `POST /v1/programmes` | `projects.write` | Create a programme label |
| `PATCH /v1/programmes/{programmeId}` | `projects.write` | Rename a programme or change its description |
| `POST /v1/programmes/{programmeId}/archive` | `projects.write` | Archive a programme label |
| `GET /v1/project-assets/{assetId}` | `projects.read` | One asset record, with its answers and (optionally) their history |
| `PATCH /v1/project-assets/{assetId}` | `projects.write` | Change an asset record's name, location, quantity or details |
| `POST /v1/project-assets/{assetId}/archive` | `projects.write` | Archive an asset record (kept, read-only) |
| `PUT /v1/project-assets/{assetId}/field-values` | `projects.write` | Answer an asset record's configured fields |
| `GET /v1/project-templates` | `projects.read` | Project templates this Portfolio can create a Project from |
| `GET /v1/project-templates/{templateVersionId}` | `projects.read` | One Project template version and everything it gives a Project |
| `GET /v1/projects` | `projects.read` | Projects I can see, filtered |
| `POST /v1/projects` | `projects.write` | Create a Project in Draft |
| `GET /v1/projects-dashboard/analytics` | `projects.read` | The Projects Analytics panels — status, type, hotel, return, budget, confidence and exceptions, over the filtered set |
| `GET /v1/projects-dashboard/board` | `projects.read` | The Projects Pipeline board — six lifecycle columns, their totals and the exception lane |
| `POST /v1/projects-dashboard/board/reorder` | `projects.write` | Move a Project within its column of the Pipeline board's manual order |
| `GET /v1/projects-dashboard/filter-options` | `projects.read` | The Projects dashboard's filter options, from the Projects the caller can read |
| `GET /v1/projects-dashboard/list` | `projects.read` | One page of the Projects List — filtered, sorted and paged on the server |
| `GET /v1/projects-dashboard/summary` | `projects.read` | The Projects dashboard's five headline cards, with the Projects behind each figure |
| `GET /v1/projects-dashboard/timeline` | `projects.read` | The Projects Timeline — planned bars, milestone markers and the Projects whose dates are required |
| `POST /v1/projects-from-template` | `projects.write` | Create a Project in Draft from a Project template |
| `GET /v1/projects-open-work` | `projects.read` | The open work one person still holds: the Projects they own and the Savings Tasks they own or contribute to |
| `POST /v1/projects-open-work/reassign` | `projects.write` | Reassign one person's open work — the Projects and Savings Tasks they own, their open task contributions and the milestones they own |
| `GET /v1/projects-summary/pipeline` | `projects.read` | The Portfolio's remaining opportunity: gross, adjusted for overlaps, on hold and awaiting verification |
| `GET /v1/projects/{projectId}` | `projects.read` | One Project, with its governance answer, its action bar and its owner |
| `PATCH /v1/projects/{projectId}` | `projects.write` | Update a Project's details (never its status) |
| `POST /v1/projects/{projectId}/approve-under-delegation` | `projects.approve` | Approve (or re-approve) a Project within the hotel's delegated authority |
| `PUT /v1/projects/{projectId}/assets/{assetId}` | `projects.write` | File one of the hotel's asset records under this Project |
| `DELETE /v1/projects/{projectId}/assets/{assetId}` | `projects.write` | Take an asset record out of this Project (it stays with the hotel) |
| `PUT /v1/projects/{projectId}/budget-year` | `projects.write` | Move a Project to another budget year |
| `GET /v1/projects/{projectId}/business-case` | `projects.read` | A Project's business case: Cadae's calculated summary and the hotel team's sections |
| `POST /v1/projects/{projectId}/business-case/export` | `projects.write` | Generate a Project's business case as a PDF, filed in its Documents as Generated by Cadae — answers the export request and its state (stored, pending or failed) |
| `GET /v1/projects/{projectId}/business-case/exports/{requestId}` | `projects.read` | The state of a business-case PDF export you requested: stored (with a download URL), pending or failed |
| `GET /v1/projects/{projectId}/business-case/print` | `projects.read` | A Project's business case as a printable HTML document, in the hotel's language — about 350 KB, most of it the inlined typeface |
| `POST /v1/projects/{projectId}/business-case/review` | `projects.write` | Mark a Project's business-case sections reviewed against its current figures |
| `PUT /v1/projects/{projectId}/business-case/sections/{sectionKey}` | `projects.write` | Write one section of a Project's business case |
| `GET /v1/projects/{projectId}/collaborators` | `projects.read` | Who is on this Project, and which of the hotel's people may be added |
| `PUT /v1/projects/{projectId}/collaborators/{userId}` | `projects.write` | Put somebody on this Project, or change the role they hold |
| `DELETE /v1/projects/{projectId}/collaborators/{userId}` | `projects.write` | Take somebody off this Project |
| `GET /v1/projects/{projectId}/comments` | `projects.read` | A Project's comments, newest first |
| `POST /v1/projects/{projectId}/comments` | `projects.write` | Post a comment, mentioning colleagues with @name |
| `POST /v1/projects/{projectId}/completion` | `projects.write` | Complete a Project — refused 412 until every item of the completion checklist is met |
| `POST /v1/projects/{projectId}/completion/readiness` | `projects.read` | The Complete checklist, over the stored answers or a draft of them — nothing is saved |
| `GET /v1/projects/{projectId}/confidence` | `projects.read` | The confidence judgements recorded on this Project |
| `POST /v1/projects/{projectId}/confidence` | `projects.write` | Record a confidence judgement, with its reason |
| `GET /v1/projects/{projectId}/copy-to-draft` | `projects.write` | Preview restarting a cancelled Project as a new Draft in its own hotel |
| `POST /v1/projects/{projectId}/copy-to-draft` | `projects.write` | Restart a cancelled Project as a new Draft in its own hotel |
| `GET /v1/projects/{projectId}/copy-to-hotel` | `projects.write` | Preview a copy of this Project into another hotel of the Portfolio: what travels and what stays |
| `POST /v1/projects/{projectId}/copy-to-hotel` | `projects.write` | Copy this Project into another hotel of the Portfolio as a new Draft (an Owner's) |
| `GET /v1/projects/{projectId}/costs` | `projects.read` | A Project's actual costs, the cost to date, the forecast and the variances against the approved amount and the preferred quote |
| `POST /v1/projects/{projectId}/costs` | `projects.write` | Record an actual cost (VAT-exclusive) — posted to the hotel's budget ledger as actual spend |
| `PUT /v1/projects/{projectId}/costs/{costId}` | `projects.write` | Correct an actual cost — a changed amount posts its difference to the ledger |
| `POST /v1/projects/{projectId}/costs/proposals/{proposalId}/confirm` | `projects.write` | Confirm a supplier invoice the bill inbox proposed — recorded as an actual cost and posted to the ledger, as the person confirming; the document's figures may be corrected |
| `POST /v1/projects/{projectId}/costs/proposals/{proposalId}/dismiss` | `projects.write` | Dismiss a supplier invoice the bill inbox proposed, with the reason — nothing is recorded or posted |
| `POST /v1/projects/{projectId}/decisions` | `projects.approve` | Record an approval decision on a submitted Project, or re-approve an outgrown one |
| `GET /v1/projects/{projectId}/delivery/readiness` | `projects.read` | The Start delivery checklist: every item with whether it is satisfied and why not |
| `POST /v1/projects/{projectId}/delivery/start` | `projects.write` | Start delivery — refused 412 until every item of the delivery checklist is met |
| `GET /v1/projects/{projectId}/document-requirements` | `documents.read` | The document categories one of a Project's gates requires, each with whether a live document satisfies it |
| `GET /v1/projects/{projectId}/financials` | `projects.read` | A Project's financial position, computed now, with its stored version |
| `POST /v1/projects/{projectId}/financials/accept-recalculation` | `projects.write` | Accept a Project's recalculation: re-pin a stale PHI baseline and recompute its figures |
| `GET /v1/projects/{projectId}/financials/versions` | `projects.read` | The immutable financial versions, newest first |
| `POST /v1/projects/{projectId}/financials/versions` | `projects.write` | Save this Project's figures as a new immutable version |
| `PUT /v1/projects/{projectId}/forecast` | `projects.write` | Record a Project's revised final cost |
| `GET /v1/projects/{projectId}/funding` | `projects.read` | A Project's external funding, confirmed and potential |
| `PUT /v1/projects/{projectId}/funding/{fundingId}` | `projects.write` | Create or replace one external funding row |
| `DELETE /v1/projects/{projectId}/funding/{fundingId}` | `projects.write` | Remove one external funding row from the live set |
| `GET /v1/projects/{projectId}/governance` | `projects.read` | Approval requirement, approval state and budget position |
| `GET /v1/projects/{projectId}/history` | `projects.read` | A Project's lifecycle events and approval decisions |
| `GET /v1/projects/{projectId}/inbox-proposals` | `projects.read` | The supplier invoices and quotes the bill inbox proposed to a Project, pending first — none is a recorded cost or quote until confirmed |
| `GET /v1/projects/{projectId}/investment-lines` | `projects.read` | A Project's investment lines |
| `PUT /v1/projects/{projectId}/investment-lines/{lineId}` | `projects.write` | Create or replace one investment line (VAT-exclusive) |
| `DELETE /v1/projects/{projectId}/investment-lines/{lineId}` | `projects.write` | Remove one investment line from the live set |
| `GET /v1/projects/{projectId}/milestones` | `projects.read` | A Project's milestones, with overdue derived in the hotel's time zone and owner access shown |
| `POST /v1/projects/{projectId}/milestones` | `projects.write` | Add a milestone to a Project's plan |
| `PUT /v1/projects/{projectId}/milestones/{milestoneId}` | `projects.write` | Replace a milestone's fields (a date change keeps the previous date in history) |
| `DELETE /v1/projects/{projectId}/milestones/{milestoneId}` | `projects.write` | Take a milestone out of the plan (a completed one stays) |
| `POST /v1/projects/{projectId}/milestones/{milestoneId}/complete` | `projects.write` | Complete a milestone on a date (the go-live milestone raises project.went_live) |
| `POST /v1/projects/{projectId}/milestones/{milestoneId}/go-live` | `projects.write` | Make this milestone the Project's one operational go-live |
| `PUT /v1/projects/{projectId}/milestones/{milestoneId}/owner` | `projects.write` | Assign or clear a milestone's owner (an active person with access to the hotel) |
| `GET /v1/projects/{projectId}/overview` | `projects.read` | A Project's identity, scope and dates |
| `PATCH /v1/projects/{projectId}/overview` | `projects.write` | Edit a Project's scope, dates and narrative |
| `PUT /v1/projects/{projectId}/owner` | `projects.write` | Assign or clear a Project's owner |
| `PUT /v1/projects/{projectId}/owner-approval` | `projects.approve` | Require (or stop requiring) Owner approval for this Project |
| `PUT /v1/projects/{projectId}/quote-required` | `projects.write` | Say whether Start delivery needs a preferred quote; “no quote required” carries its reason |
| `GET /v1/projects/{projectId}/quotes` | `projects.read` | A Project's quotes, each with its displayed status (Expired derived in the hotel's time zone) |
| `POST /v1/projects/{projectId}/quotes` | `projects.write` | Record a supplier's quote (received; nothing about the Project moves) |
| `PUT /v1/projects/{projectId}/quotes/{quoteId}` | `projects.write` | Replace a quote's typed fields (a confirmed quote's total and recurring cost are fixed) |
| `POST /v1/projects/{projectId}/quotes/{quoteId}/confirm-financials` | `projects.write` | Confirm the preferred quote into a new financial version (investment lines, recurring cost, ROI) |
| `PUT /v1/projects/{projectId}/quotes/{quoteId}/investment-lines` | `projects.write` | Set the investment lines a quote supports (replaces the set) |
| `POST /v1/projects/{projectId}/quotes/{quoteId}/prefer` | `projects.write` | Prefer a quote, with a reason — answers the proposed financial changes and writes none of them |
| `POST /v1/projects/{projectId}/quotes/{quoteId}/withdraw` | `projects.write` | Withdraw a quote from the running; its figures and file stay |
| `GET /v1/projects/{projectId}/quotes/comparison` | `projects.read` | The live quotes side by side, with missing-document, expired and scope-mismatch warnings |
| `POST /v1/projects/{projectId}/quotes/proposals/{proposalId}/confirm` | `projects.write` | Confirm a supplier's quote the bill inbox proposed — recorded as a received quote, as the person confirming; nothing is preferred or confirmed into the figures |
| `POST /v1/projects/{projectId}/quotes/proposals/{proposalId}/dismiss` | `projects.write` | Dismiss a supplier's quote the bill inbox proposed, with the reason — no quote is recorded |
| `GET /v1/projects/{projectId}/relationships` | `projects.read` | Projects this one depends on, overlaps or is an alternative to |
| `PUT /v1/projects/{projectId}/relationships` | `projects.write` | State how this Project relates to another |
| `DELETE /v1/projects/{projectId}/relationships` | `projects.write` | Make two Projects independent again |
| `PUT /v1/projects/{projectId}/relationships/adjusted-pipeline` | `projects.write` | Choose which Projects of an overlap group count in the adjusted pipeline |
| `GET /v1/projects/{projectId}/relationships/overlap-groups` | `projects.read` | The overlap groups this Project sits in, and which options count in the adjusted pipeline |
| `GET /v1/projects/{projectId}/saving-lines` | `projects.read` | A Project's saving lines |
| `PUT /v1/projects/{projectId}/saving-lines/{lineId}` | `projects.write` | Create or replace one saving line |
| `DELETE /v1/projects/{projectId}/saving-lines/{lineId}` | `projects.write` | Remove one saving line from the live set |
| `POST /v1/projects/{projectId}/saving-lines/preview` | `projects.read` | Price one saving line against this Project's assumptions, without saving it |
| `GET /v1/projects/{projectId}/task-proposals` | `tasks.read` | What completed tasks propose to change on a Project — current beside proposed, pending until confirmed |
| `GET /v1/projects/{projectId}/tasks` | `tasks.read` | A Project's Savings Tasks — fields, answers, readiness to complete and what each waits on |
| `POST /v1/projects/{projectId}/tasks` | `tasks.write` | Add a Savings Task to a Project, with its owner, contributors and completion fields |
| `POST /v1/projects/{projectId}/transitions` | `projects.write` | Take a lifecycle action on a Project |
| `GET /v1/projects/{projectId}/utility-assumptions` | `projects.read` | The prices this Project is costed at, per resource and budget year, with their basis |
| `PUT /v1/projects/{projectId}/utility-overrides` | `projects.write` | Set a Project-only unit price for one resource and year |
| `DELETE /v1/projects/{projectId}/utility-overrides` | `projects.write` | Drop a Project price override, so the hotel's forecast answers again |
| `GET /v1/projects/{projectId}/verification` | `projects.read` | A Project's savings verification, its variance against the estimate, and what a reopen kept |
| `PUT /v1/projects/{projectId}/verification` | `projects.write` | Record the verified annual saving per resource, with its method, period and evidence |
| `POST /v1/projects/{projectId}/verification/accept` | `projects.write` | Accept the verification: the verified saving becomes the realised saving, derived once |
| `POST /v1/projects/{projectId}/verification/unable` | `projects.write` | Record that the saving could not be verified, with the reason — it contributes no verified saving |
| `GET /v1/projects/{projectId}/versions` | `projects.read` | A Project's scope and narrative snapshots, newest first |
| `GET /v1/risk-findings` | `risks.read` | Risk findings I can see, filtered |
| `POST /v1/risk-findings` | `risks.write` | Open a risk finding |
| `GET /v1/risk-findings/{id}` | `risks.read` | One risk finding |
| `PATCH /v1/risk-findings/{id}` | `risks.write` | Edit a risk finding |
| `POST /v1/risk-findings/{id}/phi-event` | `risks.write` | Put a risk finding on the PHI timeline |
| `POST /v1/risk-findings/{id}/transition` | `risks.write` | Move a risk finding to another status |
| `POST /v1/risk-findings/from-gap` | `risks.write` | Promote a data gap into a risk finding |
| `POST /v1/task-proposals/{proposedUpdateId}/accept` | `projects.write` | Confirm a task's proposal — the target's own command applies it (a quote is created, a milestone completed, an asset answer saved, a document filed) |
| `POST /v1/task-proposals/{proposedUpdateId}/reject` | `projects.write` | Reject a task's proposal — kept as history; nothing about the Project changes |
| `GET /v1/tasks` | `tasks.read` | Savings Tasks across the Portfolio's hotels where Savings Tasks is on (an Owner's view) |
| `GET /v1/tasks/{taskId}` | `tasks.read` | One Savings Task, with its fields, answers and readiness to complete |
| `PUT /v1/tasks/{taskId}` | `tasks.write` | Replace an open task's details (a changed dependency drops an earlier override) |
| `POST /v1/tasks/{taskId}/block` | `tasks.write` | Block a task — the reason, what is needed, and who is responsible where known |
| `POST /v1/tasks/{taskId}/cancel` | `tasks.write` | Cancel a task with a reason (a required task is a documented exception only with an exception note) |
| `POST /v1/tasks/{taskId}/complete` | `tasks.write` | Complete a task — refused until every required answer and document is there and its dependency is complete or overridden; what it proposes waits for confirmation |
| `POST /v1/tasks/{taskId}/dependency-override` | `tasks.write` | Let a task complete past an open dependency — an Owner's or an Administrator's, with a reason kept in history |
| `PUT /v1/tasks/{taskId}/fields` | `tasks.write` | Replace a task's completion fields (eight types, no conditions; an answered field keeps its type) |
| `PUT /v1/tasks/{taskId}/owner` | `tasks.write` | Assign or clear a task's owner and, when given, replace its contributors (active people with access to the hotel) |
| `PUT /v1/tasks/{taskId}/responses` | `tasks.write` | Answer one completion field of an open task — a document for a photograph or a file; a blank answer clears it |
| `POST /v1/tasks/{taskId}/start` | `tasks.write` | Start a task (Not started → In progress) |
| `POST /v1/tasks/{taskId}/unblock` | `tasks.write` | Unblock a task — it returns to the status it had |
| `GET /v1/viewer` | `viewer.read` | Current user profile |
{/* parity:doors — end generated. */}

## Rate limits

600 requests/minute per bearer+IP on `/v1`, 120/minute on `/mcp`.

