On this page
A reliable content approval workflow assigns each draft a version, a named reviewer, and a publish handoff. Approval belongs to the exact version reviewed: if an author changes the copy, create a new version and send it through review again. A green approval status must never publish content by itself.
Download the form field specification
Use explicit states and owners
| State | Owner and action | Next state |
|---|---|---|
draft |
Author edits a working copy and submits a version. | in_review |
in_review |
Editor reviews the locked version and records approve or request changes. | approved or changes_requested |
changes_requested |
Author creates a new version and responds to feedback. | in_review |
approved |
The approved version waits for the publisher. | handed_off or archived |
handed_off |
Publisher sends that version to the CMS or publishing queue. | published or handoff_failed |
published |
A receipt records where and when the version went live. | archived or a new draft version |
A sample editorial item could be “Spring registration update.” Version 1 changes the dates and is approved by editor Jordan Lee on October 6. The author then fixes a broken link. Save that edit as version 2 with a new timestamp and content hash; version 1’s approval stays attached to version 1, so version 2 returns to in_review. After the editor approves version 2, the publisher can hand it off and record the CMS URL and receipt. The sequence makes clear who approved what and whether the page was actually published.
Store the version and decision separately

AI-generated editorial concept; not a screenshot of a deployed app.
This suggested schema is an application design, not an Atoms feature specification:
| Table | Example fields | Purpose |
|---|---|---|
content_items |
id, title, content_type, owner_id, current_version_id, status |
One stable identity for the content item. |
versions |
id, content_item_id, version_number, body_snapshot, metadata_snapshot, content_hash, created_by, created_at |
Immutable copy of the text and publishing fields reviewed. |
review_steps |
id, version_id, reviewer_id, decision, comment, decided_at |
One reviewer’s decision on one exact version. |
approvals |
id, version_id, approved_by, approved_at, approval_scope, content_hash |
Explicit record that a given version passed review. |
publish_receipts |
id, version_id, destination, external_id, published_at, result |
Evidence of a separate publishing handoff. |
Give authors permission to create versions and submit their own items. Editors can approve or request changes, but cannot overwrite an approved version. Publishers can hand off only a version whose approval record matches its current content hash. Assigned team readers can see status and approved previews; public visitors see only published content. Restrict internal feedback and drafts to assigned team members. With Supabase, enable row-level security on each exposed table and define grants plus operation-specific policies; the row policy alone does not replace table grants. Supabase Row Level Security guide
When copy changes, do not edit the snapshot that was approved. Create a new versions row, update current_version_id, clear the item’s current approval state, and create a review step. If the system cannot verify that the current hash matches the approved hash, block handoff and return the item to review. This rule covers small edits such as a corrected URL as well as larger rewrites.
Keep approval separate from publication
Approval means the reviewer accepted a version; it does not mean a CMS received it or published it. Make the publisher’s next action explicit: copy the approved content to a publishing queue, call a verified CMS action, or hand off a link to the responsible person. Store the result, destination, external ID, and time in publish_receipts. If the connection fails, show handoff_failed; do not mark the item published until the destination confirms it.
Atoms’ integration guide says each connector has its own actions and permissions, and the available actions can vary by account and release. Check the current connector catalog and review any external-write confirmation before relying on a CMS handoff. Verify the result in the destination after a write. Atoms integration guide
For persistence, choose Atoms Cloud or connect one Supabase project to the Atoms project. The Supabase guide says connecting the database does not add the content workflow automatically; build the feature, then test save, refresh, edit, and readback in both the app and the selected table. Atoms’ Supabase guide
Acceptance checks for the workflow
Seed one author, two editors, one publisher, and two content items. Verify that the author can submit an item, an assigned editor can approve version 1, and the publisher sees it in the handoff queue. Edit the copy after approval and confirm the app creates version 2, marks it in_review, and removes it from the eligible handoff list. Approve version 2, hand it off, and confirm the receipt points to version 2. Then sign in as an unrelated author and verify that private drafts and reviewer notes are unavailable.
Also test a rejected handoff, a retry, an approval by an unauthorized role, and an attempt to publish a version whose content hash has changed. Refresh after each transition and confirm the same state and decision remain. The team can use Atoms’ spreadsheet-to-web-app guide as a starting point for turning an existing tracker into an app.
Atoms build prompt
Copy the build brief and use it to create your version in Atoms.