Scopes & routes

Declared in a view’s manifest, approved by an admin, enforced by the host. This table is the whole allowlist — the security boundary of the SDK. A view cannot reach an endpoint merely because the signed-in user could; it has to be on this list, and the manifest has to hold the scope it costs.

What is deliberately missing

The interesting half of the allowlist is what is not on it:

PrefixWhy it's excluded
/api-key/*Returns the user's PERMANENT API key. A view that could read it would walk away with credentials that outlive the session, the module, and the view being uninstalled.
/auth/*Sessions, OTPs, preferences, the Google handshake — none of it is a view concern, and /auth/me leaks the email of someone who never installed the view.
/agent/*Spends the workspace AI credit balance. A view must not be able to bill a customer through their own session.
/uploads/*Multipart, size limits, a Cloudinary bill attached to the workspace. Wanted later, gated on its own scope.
/automations/*Rules that fire on other people's work. Reading them exposes a module's internal shape; writing them is remote code execution with extra steps.
/conversations/*, /messages/*Private messages between colleagues. Never.
/module-access/*Permission grants. A view that can widen access is a view that can grant itself more than it was approved for.
/workspacesRenaming or deleting the workspace a view is merely a guest in.
Adding a row to the allowlist is a permission decision, not a convenience one — it belongs in the same review as the manifest scopes it serves.

The allowlist

MethodPathScopeSummary
GET/modules/:workspaceIdmodules:readList the modules in a workspace
POST/modules/:workspaceIdmodules:writeCreate a module
PUT/modules/:moduleIdmodules:writeRename, recolour or change the visibility of a module
DELETE/modules/:moduleIdmodules:writeDelete a module and everything inside it
GET/collections/:moduleIdcollections:readList the collections on a module
POST/collections/:moduleIdcollections:writeCreate a collection
PUT/collections/:collectionIdcollections:writeRename or recolour a collection
DELETE/collections/:collectionIdcollections:writeDelete a collection and its records
GET/columns/:moduleIdcolumns:readList the columns on a module
POST/columns/:moduleIdcolumns:writeAdd a column
PUT/columns/:columnIdcolumns:writeChange a column
DELETE/columns/:columnIdcolumns:writeDelete a column and every value in it
GET/records/:recordId/sub-recordsrecords:readList the sub-records of a record
POST/records/:recordId/sub-recordsrecords:writeCreate a sub-record
GET/records/:collectionIdrecords:readList the records in a collection
POST/records/:collectionIdrecords:writeCreate a record
PUT/records/:recordIdrecords:writeRename, move, complete or archive a record
DELETE/records/:recordIdrecords:writeDelete a record
GET/record-values/references/:moduleIdvalues:readRead mirrored values across linked modules
GET/record-values/:recordIdvalues:readRead every cell on a record
POST/record-valuesvalues:writeWrite a cell
PUT/record-values/:recordValueIdvalues:writeChange a cell
DELETE/record-values/:recordValueIdvalues:writeClear a cell
GET/amendments/:recordIdamendments:readRead the amendments on a record
POST/amendments/:recordIdamendments:writePost an amendment
PUT/amendments/:amendmentIdamendments:writeEdit an amendment the user wrote
DELETE/amendments/:amendmentIdamendments:writeDelete an amendment
GET/workspace-members/:workspaceIdmembers:readList workspace members
GET/activity/:workspaceIdactivity:readRead the activity feed

The ceiling: scopes never widen access

A scope can never widen what the signed-in user may do. The host proxies every call with that person’s own credentials, and the API re-checks workspace membership and module access on each one — so a view granted records:write inside a module the user may only read still gets a 403 from the server. Scopes narrow; they do not grant.

Degraded, not rejected

A view asking for more than it was approved for is not refused outright — it runs with whatever subset was actually granted, because a view that renders read-only is more useful to the person looking at it than one that refuses to load. Always check cx.hasScope(scope); never assume a requested scope was granted.

storage

One more scope exists outside this table: storage, which gates the per-instance key-value store (cx.storage.*) rather than any CRM endpoint.