Skip to Content
WorkflowsPublishing

Publishing

Editing is autosaved; publishing is a separate, explicit step

This is the single most important thing to understand if you’re coming from WordPress or Drupal, and it’s easy to miss because the P1 dashboard has no visible “Draft” vs “Published” toggle on a page the way WordPress does.

While you edit in the Puck visual editor, every change is autosaved continuously, either over a realtime WebSocket connection (P1’s editor uses CRDT-based collaborative editing, similar in spirit to Google Docs) or via a debounced HTTP save. Each save creates a new Version of the document. This is draft state: it exists on the branch, but the public, unauthenticated read path does not serve it.

Clicking “Publish” (top of the editor toolbar) does something distinct: it flushes any pending autosaved changes, then creates a Checkpoint for that document. The public/local read path (the one your localhost:3000 and your live site actually render from) reads from published checkpoints, not raw draft versions.

We confirmed this by reading P1PuckProvider’s publishDocument implementation directly. Its own comment: “flush CRDT to Postgres, then create the checkpoint.” The Publish button is a real two-step confirmation (“Publish directly to live site?”) for exactly this reason: it’s a distinct, deliberate action, not just another autosave.

The API call underneath that button, if you ever need it directly rather than through the editor UI:

// Publishes a single document on a branch, creates a checkpoint containing // only that document's current version. await client.documents.publish(siteId, branchId, documentId);

WordPress equivalent: Draft autosave vs. clicking Publish on a Post. Drupal equivalent: Saving a node vs. a content moderation “Published” transition. P1’s version is closer to both platforms than the dashboard UI initially suggests: the mechanism is just less visible, because there’s no separate draft/published status field shown on the page itself.

Checkpoints: rollback for a whole branch

Separate from a per-document publish checkpoint, CheckpointsEndpoint supports named, branch-wide snapshots you can revert to:

await client.checkpoints.create(siteId, { branchId, name: "before-nav-redesign" }); // ...later, if something goes wrong: await client.checkpoints.revert(siteId, branchId, checkpointId, "rollback nav redesign");

There’s also per-document version history independent of checkpoints: client.versions.list(...) and client.versions.restore(...), the equivalent of WordPress’s or Drupal’s revision history on a single post/node.

Deleting a document is a soft delete

DocumentsEndpoint.delete() archives a document rather than destroying it outright; there’s a matching restore() to unarchive it:

await client.documents.delete(siteId, branchId, documentId); // soft delete (archive) await client.documents.restore(siteId, documentId); // undo it

WordPress equivalent: the Trash (a deleted post isn’t gone until you empty it). Drupal equivalent: Drupal doesn’t have a built-in trash for nodes the way WordPress does; this is closer to what a “Content moderation” unpublish + later restore gives you.

One more thing worth knowing about document lookups: documents.getByPath(siteId, path, branchId) resolves against that branch’s path: if you omit branchId, it resolves the document’s global/canonical path instead, which matters if a document’s path has been overridden on a specific branch and you query without specifying it.

Last updated on