Scopes & permissions
The full scope catalogue, sensitive vs operational defaults, the strict-mode fine-grained enforcement model, and how to pick the right scopes for your integration.
Construction AI uses a fine-grained scope model. Every API key is granted a set of scopes at creation time, and every request is checked against them. Scopes default to minimal — you grant a key only what it needs, and sensitive scopes default off.
The scope shape
Scopes follow <verb>:<resource> form. The verb is read, write, invoke, or impersonate. The resource is a module name (projects, rfis, drawings, etc.) or a sensitivity category (financial-detail, pricing, purchase-ledger).
Examples:
read:projects— read project metadatawrite:rfis— create or update RFIsread:financial-detail— read CVR, cost reports, payment applicationsinvoke:agent— invoke the AI agent (consumes BYOK Claude tokens)impersonate:user— act as a user other than the key's linked user
A key with ["read:projects", "read:rfis"] can read projects and RFIs and nothing else.
The catalogue
Operational reads (default-on, low sensitivity)
These cover the day-to-day construction data — what most integrations need.
| Scope | Covers |
|---|---|
read:projects | Project metadata, members, folder tree |
read:drawings | Drawing register, revisions, OCR-extracted data, markups |
read:specifications | Specifications, OCR-extracted text |
read:rfis | RFIs, threads, distributions, attachments |
read:communications | CCIs, RFCs, SIs, RFPs and project correspondence |
read:submittals | Material submittals, workflows, distributions |
read:rams | Risk assessments and method statements |
read:programme | Schedules, tasks, baselines, dependencies |
read:site-management | Site diary, inspections, snags, photos |
read:tender-packages | Tender package metadata (no pricing detail) |
read:project-knowledge | Semantic search over your project content (consumes Voyage AI quota) |
read:settings | Company contacts, users (name + role), branding |
read:library | Library content — schedules, scope templates, prelims, document templates |
Financial reads — split by sensitivity
The financial surface is divided into two consent decisions because contract headers and detailed cost data have different sensitivity profiles.
| Scope | Default | Covers |
|---|---|---|
read:financial-headers | on | Payment application totals, variation agreed values, contract sums (header level) |
read:financial-detail | off | CVR, margin, internal valuations, variation buildups, cost reports |
read:pricing | off | Tender pricing, master pricing, subcontract pricing, rate book detail, bid comparisons |
read:purchase-ledger | off | Supplier commitments, invoices, accruals |
The default-off scopes are commercially sensitive — the kind of data a contractor wouldn't share with a third-party vendor by accident. Granting them is an explicit decision at key creation, with a warning shown on the consent screen.
Computational and cross-project
| Scope | Default | Covers |
|---|---|---|
read:analytics | off | Computational tools — reports, summaries, comparisons. Query-budget gated. |
read:portfolio | off | Cross-project aggregations and dashboards. Cross-project visibility — review carefully. |
Write scopes (all default-off)
Used by vendor bridges and partner integrations writing canonical data into the platform via the public REST API.
| Scope | Sensitivity |
|---|---|
write:projects | Standard mutation |
write:rfis | Standard mutation |
write:drawings | Standard mutation |
write:documents | Standard mutation |
write:site-management | Standard mutation |
write:submittals | Standard mutation |
write:rams | Standard mutation |
write:communications | Standard mutation |
write:tender-packages | Standard mutation |
write:financial-headers | Header-level financial mutation |
write:financial-detail | Commercially sensitive mutation |
write:pricing | Commercially sensitive mutation |
write:purchase-ledger | Commercially sensitive mutation |
write:library | Library mutation — rate books, pricing schedules, document templates |
Identity
| Scope | Default | Covers |
|---|---|---|
impersonate:user | off | Allow the key to act as a user other than its linked user, via the X-User-Id header. See Authentication. |
BYOK cost
| Scope | Default | Covers |
|---|---|---|
invoke:agent | off | Invoke the Construction AI assistant. Consumes BYOK Claude tokens. |
How scopes interact with HTTP methods
Two enforcement modes coexist on the platform. Which one applies depends on the route.
Strict mode (sensitive routes)
Routes that touch sensitive data declare a requiredScope at the framework level. The key must hold that exact scope verbatim. Coarse scopes like read do NOT satisfy a fine-grained requirement.
For example, the CVR endpoint requires read:financial-detail:
- Key with
["read:financial-detail"]→ allowed - Key with
["read", "read:rfis", "read:drawings"]→ denied (noread:financial-detail) - Key with
["read:financial-detail", "read:rfis"]→ allowed
If a key is denied for missing a scope, the response includes the specific scope name:
{
"success": false,
"error": "forbidden",
"message": "API key missing required scope: read:financial-detail"
}Strict mode is currently applied to 37 sensitive routes covering CVR, cost reports, applications, pricing, and purchase ledger. See the Endpoint reference (coming soon) for the route-by-route mapping.
Legacy coarse mode (operational routes)
Other routes use the legacy coarse-scope model — a backward-compatibility path for existing integrations.
- Any
read*scope (e.g.read,read:rfis,read:projects) grants read access. - Any
write*scope grants write access. - If the key has only module-restricted scopes (e.g.
["read:rfis"]), the route's detected module must match.
This mode is being migrated to strict mode route-by-route as the API surface hardens.
Picking the right scopes — examples
"I want to sync our RFIs from Procore into Construction AI."
You need to write RFIs and probably read projects + members to attach them correctly. Minimum scopes:
["read:projects", "read:settings", "write:rfis"]
"I'm building a Claude desktop integration that summarises overdue RFIs."
Read-only. Need RFIs and probably projects for context. Use the OAuth flow (Claude desktop) which gives you these as default-on:
["read:projects", "read:rfis"]
"I want our internal cost-tracking system to pull CVR data nightly."
This is sensitive. Default-off scopes need explicit grant:
["read:projects", "read:financial-detail"]
"Our procurement system needs to push purchase orders into the platform."
Sensitive write scope:
["read:projects", "read:settings", "write:purchase-ledger"]
"I want a vendor integration that acts on behalf of different users in our org."
The integration needs impersonate:user plus whatever scopes those users' operations need. Set X-User-Id per call:
["read:rfis", "write:rfis", "impersonate:user"]
Scope hygiene
- Default to minimal. Don't request scopes you don't need. A key with fewer scopes has a smaller blast radius if compromised.
- One key per integration. Don't share a
["read:rfis", "write:rfis"]key between a sync job and a dashboard. Mint separately. - Sensitive scopes are deliberate. If you find yourself routinely granting
read:financial-detailto lots of keys, ask whether the integration actually needs detailed financial data or whetherread:financial-headerswould suffice. - Audit periodically. The dashboard shows last-used timestamps. Keys not used in 90 days are candidates for revocation.
What's NOT a scope
Some things look like permissions but are enforced differently:
- Tenant isolation — not a scope, it's architectural. Every token is scoped to one tenant; cross-tenant queries are physically prevented by row-level security.
- Project restriction — set at key creation via
allowed_projects. The key gets403on any project not in the allow-list, regardless of scopes. - Rate limits — also set at key creation. See Rate limits.
Where to go next
- Authentication — how tokens are minted and managed.
- Rate limits — defaults and headers.
- Errors — what the platform returns when something goes wrong.