The publishing pipeline
Every idea in Contentpipe travels the same route โ from a one-line stub to a live article, spawning channel promos along the way. Here's how each status works and who moves it forward.
A publication is a single article. It carries exactly one status at a time, and that status determines what actions humans and agents are allowed to take on it. Statuses only ever move forward โ except reopen, which sends an approved item back to draft for edits.
draft onward has a dateA publication carries a scheduled date from draft up. You do not have to pick one:
promoting a stub to draft sets it to a week from now, which is a placeholder to move
rather than an answer. The point is that the piece appears on the campaign calendar, where
a wrong date is visible and fixable, instead of being invisible because it has none.
stub and archived are exempt โ a stub has nothing to schedule yet, and an abandoned
stub still needs to be archivable.
Once a piece is promoting its date stops being a plan and becomes a record of when it
went out, so it can no longer be moved โ same for a published promo. Everything else
about a live piece stays editable, including the canonical URL it requires.
A promo can never be scheduled before the publication it promotes; that is enforced in the database, on every write path.
A stub is a title and an optional note. Agents can pick one up and draft it, or you can promote it to draft yourself and start writing. Batch-creating stubs is how campaign planning happens.
Overviewโ
The pipeline is deliberately linear so that state is never ambiguous โ at any moment you know exactly who owns an item and what happens next. Publications and their promos share draft and reviewing, so the same review muscle memory applies everywhere; the state an approval lands in then diverges, because what happens next diverges. A publication becomes approved and waits for a human to publish it by hand; a promo becomes scheduled, which on an auto channel means its post is already booked with upload-post.
stub โโโถ draft โโโถ reviewing โโโถ approved โโโถ promoting โโโถ archived
โฒ โ โ
โโโโโโโโโโโ โ (request_changes)
โโโโโโโโโโโโโโโโโโโโโโโโ
The six statusesโ
Each status pairs a color with a glyph, so the pipeline reads at a glance and stays legible for color-blind users.
| Status | Used by | What it means |
|---|---|---|
stub | publications | A raw idea โ title plus an optional note. |
draft | both | Being written by a person or an agent. |
reviewing | both | Awaiting critique before it can be approved. |
approved | publications | Signed off and ready to ship. |
scheduled | promos | Approved, and booked with upload-post โ see below. |
promoting | publications | Live at its canonical URL, fanning out into channel promos. |
published | promos | The post has gone out on its channel. |
archived | publications | Retired from the pipeline โ the outlier. |
A publication has no separate published status. Entering promoting requires the canonical URL โ the address the piece now lives at โ and stamps published_at at the same moment. The two were never independent: a promo is a post linking to the article, so it can't be written before the article has a URL, and the article has no URL before it's live. Splitting them only let a publication sit in a state its promos could never be worked from. Nothing follows promoting but archived; a piece that's live and never promoted simply has no promos.
Once a publication hits approved, Contentpipe can generate promos for every connected channel. They go out once it reaches promoting and has a URL to link to.
Promosโ
An approved publication can start collecting channel promos โ one or more per channel (Substack, X, LinkedIn, Bluesky, and more) โ and they go out once it's promoting. Each promo runs its own mini-pipeline: draft โ reviewing โ scheduled โ published.
draft โโโถ reviewing โโโถ scheduled โโโถ published
โฒ โ
โโโโโโโโโโโโ (request_changes)
Promos on auto channels are sent through upload-post's own scheduler โ the promo is uploaded with its dispatch time and their side posts it (no local cron). Channels without upload-post support (like Substack) are manual โ a human publishes and records the URL. Publications always publish manually.
On an auto channel, approving records the verdict and books the post with upload-post in one step โ there's no separate "now schedule it" action to forget. Because of that, a promo with no dispatch time, or one whose time has already passed, is refused: it stays in review until you set a future one. Bulk approve applies the same rule per promo, so untimed ones are rejected while their neighbours go out.
Manual channels are exempt rather than refused โ nothing to book โ so they approve and wait for you to publish them and record the URL.
Promos respect each channel's limits. A draft over the character cap (e.g. 280 / 280 on X) can't leave review until it fits.
Automating with MCPโ
AI clients drive the pipeline over the Model Context Protocol. A client calls a workflow-level tool with an item id; the server validates the transition in the database and returns the updated item.
await client.callTool("submit_for_review", {
publication: "pub_9f21",
});
The tool maps to a SECURITY INVOKER Postgres function, so RLS and the transition triggers apply to the calling user exactly as they would in the admin UI.
Skipping statuses is rejected. Trying to jump a publication from draft straight to promoting raises an invalid-transition error naming the current status and the allowed next states โ so the client can self-correct. Always go through review.