API key handling
- A key’s secret is generated from 32 random bytes and shown once, when the key is created or rotated. Cactal stores only its SHA-256 hash; nothing on Cactal’s side can reveal the plaintext again.
- Rotation (
POST /v1/apiKeys/{apiKeyId}/rotate) replaces the secret immediately — the old secret stops authenticating the moment the call returns. - Revocation (
POST /v1/apiKeys/{apiKeyId}/revoke) is permanent. Keys can also carry anexpiresAttimestamp; expired and revoked keys fail authentication with401. - REST routes on
api.cactal.aiaccept bearer API keys. The MCP route also accepts audience-bound OAuth access tokens. Requests carrying a browser session cookie are rejected with401, so bearer credentials and sessions can never be confused on the public surface.
Access control
Authorization is capability-based. Roles expand to capability sets, and every operation — over the dashboard, REST, or MCP — checks one required capability before running.- Organization roles:
owner,admin,full_editor,content_editor. - Website-scoped roles:
full_editor,content_editor, granted per website. - API keys carry the same model: organization-scoped or website-scoped access with a role. The
ownerrole is not grantable to keys.
tools/list, so a user or key never sees tools it cannot call. See Access control.
MCP OAuth
- Standard protected-resource and authorization-server metadata let MCP clients discover the authorization flow without Cactal-specific configuration.
- Cactal accepts Client ID Metadata Documents from public HTTPS origins. Client metadata may link to a public homepage on another HTTPS origin, while non-loopback redirect URIs remain bound to the metadata document’s origin. The consent screen identifies discovered clients by that fetched origin. Verified publisher branding is reserved for identities Cactal recognizes, including ChatGPT.
- Clients that use dynamic registration, including Claude’s current connector flow, remain visibly unverified because their publisher details are self-reported. All public clients use the authorization code flow with PKCE; client-credentials grants are not exposed.
- Access tokens are signed JWTs with the Cactal authorization server as issuer and
https://api.cactal.ai/v1/mcpas their exact audience. Tokens without themcpscope are rejected. - Consent is explicit, refresh tokens require
offline_access, and tool calls retain the signed-in user’s grants and audit identity.
Tenant isolation
A website you cannot access returns404 not_found, the same response as a website that does not exist, so website existence never leaks across organizations or across website scopes.
403 forbidden, and 404 not_found means the organization id itself is unknown.
Audit logging
Lifecycle and access-control operations write audit events to the organization’s audit log, whichever interface invoked them: website create/rename/delete/restore/transfer, editor grants and invitations, API keys, domains, organization members and invitations, billing, and analytics CSV export downloads. Source file edits, publishes, CMS content, and asset operations are not audit-logged — review content work through source versions, changed-file lists, and targeted diffs instead. Each audit event records:- The event type and a human-readable summary.
- The affected resource type and id.
- Actor attribution:
actorKinddistinguishes auserfrom anapi_key, with a reference to the specific user or key.
GET /v1/organizations/{organizationId}/audit-log, filtering by actorKind, eventType, resourceType, actorPrincipalId, or websiteId (which matches both the website itself and resources belonging to it). Agent-safe publishing shows how to use this for agent oversight.
Draft privacy
Draft and version preview hosts (draft--<label> and v<n>--<label> on cactal.app) are served with:
Separated planes
The control plane and customer content run on different domains:
Browser sessions exist only on the control plane. Customer-authored content — pages, scripts, assets — is served from the content plane, never from a control-plane origin. See Domains.
Publish build isolation
Publishing builds a website’s source in a disposable sandbox created for that job and discarded afterwards. A deployment serves traffic only after its build reachesready; a failed build does not replace what is currently serving. See Publishing.
Abuse mitigation
The API rate-limits at several layers. Beyond the per-key buckets, two per-IP buckets protect the authentication surface itself:- A pre-authentication bucket caps total requests per IP before any key is checked.
- An authentication-failure bucket allows a burst of 25 failed attempts per IP, refilling at 0.5 per second — throttling key guessing to a crawl.
429 semantics are in Rate limits.