Skip to main content

Publishing

Krios's default flow is two states: draft and published. Workflows extend this with configurable state machines (see Workflows).

Per-locale state

ContentLocaleState holds the published flag per (entry, locale). Publishing one locale doesn't publish the others. The route index takes one path per (site, locale, entry).

POST /api/v1/projects/{slug}/entries/{id}/publish
{ "locale": "en-US" }

If the entry has no en-US field values yet, the publish returns 422 required_fields_missing with the missing field api-names in details.fields (a string[]).

Required-reference gate

If any required reference field on the entry points at an unpublished entry, the publish is blocked with 422:

{
"error": "required_references_unpublished",
"message": "Cannot publish — required references are not published.",
"details": {
"unpublished": [
{ "apiName": "category", "targetId": "ckl_…" }
]
}
}

Publish each blocker first or remove the reference. The publish gate always throws on missing required fields or unpublished required references — there is no per-project block/warn configuration.

Scheduling

Pass publishAt / unpublishAt (ISO 8601) on create or update. A ScheduledJob row is enqueued; the Vercel Cron handler (registered hourly, 0 * * * *) processes due jobs on each tick.

PUT /api/v1/projects/{slug}/entries/{id}
{
"version": 4,
"locale": "en-US",
"fields": {},
"publishAt": "2026-06-01T09:00:00Z",
"unpublishAt": "2026-06-30T23:59:59Z"
}

The Calendar tab in the admin UI surfaces every scheduled job — drag to reschedule.

Modified state

After publish, edits to the entry create a new draft version while the live site keeps serving the published snapshot (from ContentVersion, not from the latest field values). The editor's status badge shows "Published — modified" when this divergence is present, and the Publish Changes button promotes the latest draft to live.

Unpublish

POST /api/v1/projects/{slug}/entries/{id}/unpublish
{ "locale": "en-US" }

Drops the locale's RouteIndex row, fires entry.unpublished webhooks, schedules a cache invalidation. The entry stays in the database; its draft is preserved.

Workflow transitions

When a content type has a workflow assigned, /publish and /unpublish are replaced by a single transition endpoint:

POST /api/v1/projects/{slug}/entries/{id}/workflow
{ "locale": "en-US", "direction": "forward" }

direction: "forward" advances to the next state in the workflow's ordered states; "backward" returns to the previous state. Reaching the workflow's final state triggers the same publish-side effects (RouteIndex update, webhooks, cache invalidation) as direct publish.

See Workflows for the full state-machine model.