# Scopes

Every endpoint declares the scope it needs. Scopes are named `resource:action`
and there is no implicit hierarchy — `properties:write` does not include
`properties:read`, so a workspace owner can read the granted list literally.

## The vocabulary

{/* Keep in sync with SCOPE_REGISTRY `docs` strings in the API.
    `scope-docs-drift.spec.ts` fails if this table and the registry disagree. */}

| Scope | Grants |
| --- | --- |
| `properties:read` | Read properties, including amenities, photos metadata and base pricing. |
| `properties:write` | Create, update and archive properties. Hard deletion is never exposed. |
| `reservations:read` | Read reservations: dates, status, channel and amounts. Excludes guest identity. |
| `reservations:guest_read` | Read guest identity (name, email, phone). Every access is audited. |
| `blocks:read` | Read availability blocks (owner stays, maintenance). |
| `blocks:write` | Create blocks, and update or delete only the blocks this integration created itself. |
| `calendar:read` | Read the daily calendar: price, minimum stay, CTA/CTD rules and availability. |
| `calendar:write` | Update nightly prices and stay restrictions. Changes propagate to the connected OTAs. |
| `webhooks:manage` | Manage this integration’s own webhook endpoints. Grants no additional data access. |

## Why you might be missing one

Your effective scopes are the **intersection of three independent layers**:

```
what Noé approved for your integration
  ∩ what this workspace consented to share
  ∩ what this specific key was given
```

Each layer can only narrow the one before it. So being approved for
`calendar:write` does not mean you have it everywhere — a workspace may have
shared read access only, or issued you a read-only key for one of their systems.

Call [`GET /v1/me`](/developpeurs/api/account) to see what you actually hold for a given
key. It is much faster than inferring it from `403` responses.

There is also a fourth, separate dimension: a grant can be restricted to a
**subset of properties**. When it is, everything outside that subset simply does
not exist for you — a request for one of those properties returns `404`, exactly
as it would for an id that was never real. That is intentional, so the response
cannot be used to discover what you were not given.

## Sensitive scopes

`reservations:guest_read` exposes personal data, so it behaves differently:

- It is never pre-selected when a workspace authorises you. They have to tick it
  deliberately.
- Every read through it writes an audit record — which reservations, by whom,
  when. The record contains ids only, never the personal data itself.
- Without it, the `guest` object is **absent** from the response rather than
  null, and the request still returns `200`. Do not treat a missing `guest` as
  an error; treat it as "not shared with you".

```json
{
  "id": "…",
  "object": "reservation",
  "check_in": "2026-09-12",
  "guest": { "first_name": "Camille", "last_name": "Roux", "email": "…", "phone": "…" }
}
```

## When scopes change

A workspace **widening** your access records a fresh consent. **Narrowing** it
takes effect on your very next request — there is no cache and no token lifetime
to wait out, so handle a scope disappearing gracefully rather than assuming what
worked yesterday still works.
