Skip to main content
Records start unpublished. A record is live when it has a published_at, and what is live is its current content: an edit to a published record reaches your site at once. To hold changes back until they are ready, put them in a draft.

Publish and unpublish

In the web app, open a record and choose Publish or Unpublish. Through the API, publishing is the record’s publication:
DELETE on the same URL unpublishes, and GET says since when the record is live, the live revision and who published it (404 with detail code not_published for a record that isn’t live).
Each is a publish or unpublish revision in the record’s history, and takes a change note and If-Match like any record write. Publishing a published record, or unpublishing an unpublished one, changes nothing. Unpublishing keeps the content, and publishing again sets a new published_at. To publish or unpublish up to 100 records at once, POST /v1/collections/{collection_id}/record-batches with action: "publish" or "unpublish" and their record_ids. Each record is changed as if by its own request, and one that fails doesn’t stop the others. In the web app, Editors and every role above can publish; Viewers can’t. API keys publish with records:write.

Hold changes back in a draft

A draft holds changes to a record until they are merged into it, while the record keeps its current content. In the web app, edit a published record and choose Save as draft from the save menu; the record then shows its pending changes, with Publish changes, Schedule changes and Discard changes. On an unpublished record, Publish changes reads Apply changes.
  • One draft per record. Starting a second is 409 with detail code draft_exists and the existing draft_id.
  • Previews. A draft’s data is the record as it would read after the merge, and GET /v1/drafts/{draft_id} takes expand, so a preview route on your site can render it. changed_fields lists what it changes.
  • Merging changes content only. A live record stays live and keeps its published_at; an unpublished one stays unpublished. The merge is one merge revision that names the draft and everyone who edited it. Webhooks get record.updated with the draft_id.
  • Publishing never merges. Publishing a live record that has a draft is 409 with detail code draft_pending. Publishing an unpublished record makes its current content live and keeps the draft.
  • Edits made since win. Fields the record changed after the draft started keep their values at the merge, unless the draft changes them too. The merge is validated again, unique values included (422).
  • Bring back an earlier version for review. from_revision starts the draft from that revision’s values instead of the record’s current ones.
Discarding a draft (DELETE /v1/drafts/{draft_id}) leaves the record unchanged.

Schedule publishing

A record’s schedule publishes it at publish_at and unpublishes it at unpublish_at, within a minute of each time. In the web app, choose Schedule, set Publish at and Unpublish at, and choose Save schedule.
The request sets the whole schedule: a time you leave out is cleared, and DELETE cancels both. Times must be in the future. publish_at needs an unpublished record, and unpublish_at a published one or a publish_at before it. Publishing by hand clears publish_at, unpublishing by hand clears unpublish_at, and deleting the record clears both. A scheduled change is a revision of its own, attributed to the system and made via: scheduler, with the same webhooks as a change by hand. One that can’t be made when it is due is dropped: its time is cleared, the schedule’s last_error says why, and a record.schedule_failed event goes to the activity log and to webhooks that subscribe to it.

Schedule a draft

A draft merges on its own at merge_at (Schedule changes in the web app). That is how you schedule changes to a live page, such as new prices from Monday. A merge that can’t be made leaves the draft as it is, with schedule.last_error, and sends draft.schedule_failed.
GET /v1/collections/{collection_id}/scheduled-changes lists every pending change of a collection’s records, soonest first.

Published-only API keys

A key with Published records only on (a nohead_pk_ key) reads what your site shows, and nothing else. New keys in the web app have it on by default. Such a key:
  • lists, reads, counts and searches published records only, and finds an unpublished or deleted record as 404
  • leaves unpublished records out of expanded relations, and reads only the assets that published records use
  • gets 403 for drafts, schedules, publications, record history and schema history
  • holds read scopes only (records:read, schema:read, assets:read, search:read)
Use a published-only key for your site’s build or server, so a draft or an unpublished record can never reach a page.
With it you don’t need a published_at filter; a full-access key lists unpublished records too, unless you filter with filter[published_at][exists]=true. Keep it out of browser code, since the API accepts browser requests only from the web app; for search from the browser, use public search. To rebuild pages when what is live changes, subscribe a webhook to record.* (merges send record.updated, reverts record.reverted) and field.migrated: see Webhooks. Every endpoint is in the API reference.