API reference
Base URL: https://api.cadae.ai. Interactive documentation lives at
/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.
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.
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 |
Rate limits
600 requests/minute per bearer+IP on /v1, 120/minute on /mcp.