Content management (content API)
Headless collections and entries for static sites with CORS, service keys, and assets.
Content management (content API)
Content management exposes a headless content API for static sites and client apps. Project admins manage collections, schemas, entries, CORS origins, service keys, and assets from Settings → Content management.
Enablement
- A project admin enables Content management on Settings → Content management (checkbox + save), or approves an MCP proposal (
propose_content_management_schemawithoperation: enable). - Set allowed origins, optional suspend public access, and create service keys on that page.
- Use Manage content to create collections, optionally define a schema (recommended before entries), and add entries.
Copy the project ref (project ID) and content service base URL from Settings → Content management — integrators need both for API calls.
Agents on ArxDeck MCP must call get_project_info (Knowledge group, no arguments) for projectRef, contentServiceUrl, capability flags, and published site URLs. Do not pass secrets or env vars into static build pipelines — builds are sandboxed and cannot read platform configuration at build time.
Public reads (no app users)
Public collections can be read anonymously with GET /v1/collections/{slug}/entries, X-Project-Ref, and a listed Origin header. You do not need the Authentication capability for public-only sites.
Service keys and access rights
Create service keys on the content management settings page for server-side access (Authorization: Bearer cbk_…). Per-collection access rights for service keys are managed on each collection's page under Manage content with only Content management enabled (list and revoke, including existing app-user rows). Granting access to app users and the Content API users section on that page require the Authentication capability.
Rights levels are read, write, and admin (admin includes schema and collection settings).
What agents can change (MCP)
- Schema / collections:
propose_content_management_schema(enable, collections, schema revisions) - Entries:
propose_content_management_data(create, update, delete). Create requiresproposalRefand does not send a resource identity; update and delete identify the entry by collection slug and entry key. Applies immediately when the collection has auto-approve MCP writes enabled.
Inspect state with get_content_management_schema, list_content_management_entries, and get_content_management_entry.
API reference
The public integrator API is served by the content service. Browser clients on Publish-hosted sites should call /_arxdeck/api/... on the site origin (gateway forwards to /v1/...). Server-side integrations continue to use {content-service-base}/v1.
Browser base URL (Publish)
https://{your-published-hostname}/_arxdeck/apiDirect base URL (server-side)
{content-service-base}/v1Replace {content-service-base} with the HTTPS origin shown in Settings → Content management (no trailing slash).
Headers and authentication
| Header | Required | Description |
|---|---|---|
X-Project-Ref | Yes | Project ref from Settings → Content management (same value as the project ID in the dashboard URL). |
Origin | Browser requests | Your static site origin. Must appear in allowed origins for CORS. |
Authorization | Service keys | Bearer cbk_… for server-side automation. Never embed in browser bundles. |
Cookie | Sessions | content_session HttpOnly cookie after POST /v1/auth/login (see Authentication). |
Callers resolve as:
- Public —
X-Project-Refonly (read public collections when not suspended). - Service key —
Authorization: Bearer cbk_…plus matchingX-Project-Ref. - Session —
content_sessioncookie (orBearer css_…session token) plus matchingX-Project-Ref.
403 responses include Origin not allowed (CORS), Forbidden (insufficient collection rights), X-Project-Ref does not match authenticated scope, and User account is suspended. Project-level suspend public access returns 503 for anonymous callers; per-collection suspend returns 503 for public reads on that collection.
Rate limits
| Scope | Limit |
|---|---|
| Project | 100 requests per minute (logged access per project). |
| Login | 10 POST /v1/auth/login attempts per project per 10 minutes. |
| Writes | 60 POST / PATCH / PUT / DELETE requests per project per hour (non-operator callers). |
Exceeded limits return 429 with message Rate limit exceeded.
Pagination
List endpoints accept optional query parameters:
| Parameter | Default | Max | Description |
|---|---|---|---|
limit | 50 | 200 | Page size. |
cursor | — | — | Opaque cursor from the previous response's nextCursor. |
Responses include nextCursor (or null when there is no next page).
List payload truncation
GET /collections/{slug}/entries may truncate each entry's data field to 20 KiB UTF-8 for list responses. When truncated, the entry includes "truncated": true. Use GET /collections/{slug}/entries/{entryKey} for the full document.
Suspend public access
When suspend public access is enabled at the project level, anonymous (X-Project-Ref only) requests fail with 503 Public access is suspended. Authenticated service keys and sessions continue to work. Per-collection public access suspended blocks anonymous reads for that collection only.
Schema field types
Collection schemas are JSON objects with a fields array. Supported type values:
| Type | Description |
|---|---|
text | String with optional min, max, pattern, required, default. |
markdown | Markdown string (same constraints as text). |
number | Finite number with optional min, max, allowedValues. |
boolean | true / false. |
datetime | RFC 3339 timestamp string. |
enum | Value must be in allowedValues (required on enum fields). |
asset | Asset ID string (from the upload flow). |
reference | Entry reference: targetCollections (required), optional multiple, optional deletePolicy (block or nullify). |
array | Array of itemType (text, markdown, number, boolean, datetime, enum, asset, or reference) with optional itemConfig. |
object | Freeform JSON object (no subfield validation). Edit as JSON in the dashboard entry editor. |
Field key is the stable identifier on entries; label is the optional display name in the dashboard. key must match ^[a-zA-Z][a-zA-Z0-9_]*$. MCP proposals accept legacy aliases with warnings: name as key when key is omitted, integer as number, and string as text (use datetime for RFC 3339 timestamps). Posting a new schema revision increments currentSchemaRevision; existing entries keep their stored revision until updated.
Collections
GET /collections
List collections visible to the caller (public collections for anonymous callers; granted plus public-readable collections for sessions and service keys).
Each item includes access: read, write, or admin for that caller (operator: admin; anonymous/public: read; sessions and keys: explicit grant level or read when public-readable).
curl "{content-service-base}/v1/collections" \
-H "X-Project-Ref: YOUR_PROJECT_REF" \
-H "Origin: https://www.example.com"Example fragment:
{
"collections": [
{
"id": "clx9col00000000000000001",
"slug": "pages",
"name": "Pages",
"visibility": "public",
"access": "read"
}
]
}POST /collections
Create a collection (session or service key with rights; creator receives admin on the new collection).
| Field | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Lowercase slug (^[a-z0-9][a-z0-9-]*$, max 64). |
name | string | Yes | Display name (max 200). |
description | string | No | Optional description (max 2000). |
visibility | string | No | private (default) or public. |
validationMode | string | No | strict (default) or lax. |
GET /collections/{slug}
Get one collection metadata object.
PATCH /collections/{slug}
Update collection fields (name, description, visibility, validationMode, autoApproveMcpWrites). Requires admin on the collection.
DELETE /collections/{slug}
Delete a collection and its entries. Requires admin.
Schema
GET /collections/{slug}/schema
Returns { revision, schema } for the collection's current schema revision.
POST /collections/{slug}/schema
Create a new schema revision (requires admin).
{
"schema": {
"fields": [
{ "key": "title", "type": "text", "required": true },
{ "key": "heroImage", "type": "asset" }
]
}
}GET /collections/{slug}/schema/revisions
List revision metadata (revision, createdAt, createdById).
Entries
Entry keys match ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ (max 128).
GET /collections/{slug}/entries
List entries with pagination. Requires read (or public visibility for anonymous callers).
curl "{content-service-base}/v1/collections/pages/entries?limit=50" \
-H "X-Project-Ref: YOUR_PROJECT_REF" \
-H "Origin: https://www.example.com"GET /collections/{slug}/entries/{entryKey}
Get one entry with full data.
curl "{content-service-base}/v1/collections/pages/entries/home.hero" \
-H "X-Project-Ref: YOUR_PROJECT_REF" \
-H "Authorization: Bearer cbk_YOUR_KEY"POST /collections/{slug}/entries
Create an entry (requires write).
| Field | Type | Required | Description |
|---|---|---|---|
entryKey | string | Yes | Unique key within the collection. |
data | object | Yes | Field values validated against the current schema. |
published | boolean | No | Defaults to false. |
PATCH /collections/{slug}/entries/{entryKey}
Merge data fields and/or update published (requires write).
DELETE /collections/{slug}/entries/{entryKey}
Delete an entry (requires write). May return 409 when a reference field with deletePolicy: block still points at this entry.
Uploads and assets
Presigned upload URLs expire after 900 seconds (15 minutes). Asset download redirects expire after 300 seconds (5 minutes).
POST /uploads/presign
Requires an authenticated caller (not public). Returns presigned PUT URL and uploadId.
| Field | Type | Required | Description |
|---|---|---|---|
filename | string | Yes | Original filename (max 200). |
contentType | string | Yes | Allowed MIME type (images, PDF, fonts, plain text, markdown, video, etc.). |
expectedByteSize | number | No | Optional size check against type limits. |
curl -X POST "{content-service-base}/v1/uploads/presign" \
-H "Authorization: Bearer cbk_YOUR_KEY" \
-H "X-Project-Ref: YOUR_PROJECT_REF" \
-H "Content-Type: application/json" \
-d '{ "filename": "hero.png", "contentType": "image/png" }'PUT file bytes to uploadUrl, then complete:
POST /uploads/{uploadId}/complete
| Field | Type | Required | Description |
|---|---|---|---|
byteSize | number | No | Optional verification of uploaded size. |
Returns { asset } with id, storageKey, contentType, byteSize, filename, status.
GET /assets/{assetId}
Returns 302 redirect to a short-lived presigned download URL. Requires authentication (not public).
curl -i "{content-service-base}/v1/assets/ASSET_ID" \
-H "Authorization: Bearer cbk_YOUR_KEY" \
-H "X-Project-Ref: YOUR_PROJECT_REF"CORS checklist
- Published ArxDeck hostnames (production and staging when enabled) are added automatically as platform-managed origins when your site goes live with content or authentication enabled. They appear read-only in Settings → Content management and do not need to be typed manually.
- Add custom domains and any other browser origins in the manual allowed origins list (one HTTPS URL per line). Wildcards are not supported.
- Managed and manual lists are separate; saving manual origins never removes platform-managed entries.
- Enable credentialed requests (
credentials: 'include') in the browser when using session cookies. - Suspend public access temporarily blocks anonymous reads without disabling the capability for editors and service keys.
Related
- Authentication — app-user login and provisioning
- Integrations overview
- MCP — configuration proposals during agent runs
