Authentication
Bearer tokens, scoped per workspace, revocable, rate-limited per key.
Every request carries a bearer token:
GET /api/agent/site/units
Authorization: Bearer <your key>A missing or malformed Authorization header is a 401, as is a key that does
not resolve.
Keys belong to a workspace
A key is issued for one workspace and resolves to it. Every query the request makes is scoped to that workspace before it runs — not filtered afterwards. There is no combination of key and parameter that returns another workspace's data.
Keys are stored hashed, never in plaintext, and compared in constant time. That means:
- You cannot recover a key after issue. If it is lost, revoke it and issue a new one.
- A key can be revoked immediately, without affecting any other key.
Scopes
A key carries an explicit list of scopes, and a request is rejected with 403 if
the route's required scope is not among them.
| Scope | Grants |
|---|---|
properties:read | Buildings, units, availability. |
content:read | Blog posts, campaigns, positions, services, site configuration. |
residents:read | Resident records. |
showings:read | Scheduled viewings. |
inquiries:write | Submit an enquiry. |
leads:write | Submit a lead, including a résumé upload. |
analytics:write | Send page-view and event telemetry. |
chat:invoke | Invoke the site assistant. Cost-bearing. |
check-in-links:write | Create a guest check-in link. |
reservation-links:write | Create a reservation link. |
purchases:write | Accept a supplier order. |
ota:ingest | Relay in OTA guest messages. |
Scope your keys narrowly, and issue several rather than one. A public website is normally provisioned as three separate keys — read, actions and telemetry — so that a burst of one traffic class cannot exhaust the budget of another, and so a key embedded in a page cannot read residents.
A key that can read listings cannot read residents. That is the point of the split, and it is worth preserving when you provision.
Rate limits
Requests are counted per key, not per IP, with a default budget of 300 per hour. A key can be given its own budget where a first-party sync tool legitimately bursts.
Exceeding the budget returns 429. Back off and retry; the window is rolling.
Because limits are per key, one integration cannot exhaust another's allowance — another reason to issue separate keys rather than sharing one.
What the API is for
This is a deliberately small, mostly read-oriented surface: enough to drive a public website, feed an external assistant, and accept orders from a supplier. It is not a general-purpose interface onto the whole product, and it is not how you would build a second dashboard.
There is no OpenAPI document yet, so these pages are written by hand and can drift from the implementation. Treat a live response as authoritative over a sample here, and report anything that disagrees.
See also Errors.