CMS environments
Each Ovok project gets one Payload tenant (row in tenants). Within
that tenant, content is further scoped by environment:
| Environment | Typical use |
|---|---|
dev | Sandbox / integration drafts |
staging | Pre-release validation |
prod | Live published content |
How isolation works
- Every tenant-scoped collection (
content-types,content-items,posts, …) includes anenvironmentfield. - Composite unique indexes prevent slug collisions within an
environment, not across them:
(tenant, environment, slug). - ovok-core forwards
x-ovok-environmenton every CMS proxy call. - Writes require a valid environment header; reads default to
devwhen the header is missing (authoring proxy only — public delivery passesenvironmentexplicitly).
Enabling an environment
Environments are enabled per project through the control plane (Console
→ Settings → Payload CMS, or POST /v1/cms/projects/:slug/environments).
Each enablement:
- Records
project_environmentsin control-plane Postgres. - Provisions the Payload tenant if this is the first environment.
- Marks the environment
active.
Suspending an environment sets status: suspended without deleting
content rows.
Public delivery
Published reads must include the target environment as a query parameter:
curl "https://api.sandbox.ovok.com/v1/public/cms/pages/items?environment=dev&projectId=$MEDPLUM_PROJECT_ID" \
-H "Authorization: Bearer $OVOK_CMS_KEY"
| Param | Required | Description |
|---|---|---|
environment | Yes | dev, staging, or prod |
projectId | Yes | Medplum project UUID for the CMS tenant |
The API key alone identifies the Ovok project; projectId resolves
the Payload tenant. Do not use projectSlug on public delivery.
Production write policy
Production read-only enforcement for dashboard writes is handled at the ovok-core proxy, not inside Payload. Payload still scopes data by tenant + environment for all operations.
Next
- Public delivery — frontend reads
- Authoring — backend writes via
/v1/content