One article got posted again to the blog, again to social, again to the newsletter. Reformatting per channel, tidying links, and separately recording how each performed — every time.
Approve once, publish everywhere
The moment a page flips to approved in Notion, the workflow starts. Content is transformed and published per channel, the team channel is told, and results are collected in one place.
First, the uncomfortable part — not every channel can be automated
The most important thing in this case study comes first. Some platforms provide no public API that lets an outside service post on your behalf. No workflow design changes that. The honest answer to "can it publish to Naver Blog?" — the first question Korean readers ask — is that automated publishing is not available.
- Channels you can automate — your own blog (WordPress, Ghost, Webflow), newsletter services, and social platforms with public posting APIs. The workflow carries these all the way through.
- Channels you can't — platforms with no public write API. This workflow doesn't even attempt them; it posts a "publish this one manually" note to your team channel instead.
- Encoding that distinction in code is the point. Draw an unautomatable channel as if it were automatable and posts that fail to publish quietly disappear. Silent omission is the worst possible outcome.
Plan Publish Targets marks each channel automated: true | false. That's the configuration point for this workflow. Keep only the channels you use, and check whether each one genuinely offers a public publishing API. Leaving something as `true` without checking is the most dangerous thing you can do here.The approval gate already exists — it's the Notion status
Resistance to publishing AI drafts unreviewed is real and reasonable — *"I can't fully trust work a machine churned out"* is a common reaction from people who run the channel. This pipeline doesn't route around it.
- The workflow only fetches pages marked approved. Drafts and in-review pages never publish, however long they sit.
- **Changing that status in Notion *is* the approval gate.** No separate approval system is needed; the human judgement happens in the tool people already use.
- Publishing stops being a task and becomes a state change. Marketers focus on making and judging; only the moving is automated.
- For tighter control, split it in two: post the pending publish list to the team channel first, then publish after confirmation.
The Notion database schema
Your content database needs these properties. Rename them freely — just update the mapping in the Fetch Approved Posts node to match.
title Title (text)
status Status (select) — draft | in review | approved | published
channels Channels (multi-select) — wordpress, newsletter, x, naver_blog ...
publish_at Scheduled date (date, optional)
og_image Cover image (file, optional)
body Body (page content)channels a multi-select is the key decision. Where a piece goes varies per piece, so don't pin channels to the workflow — let each post carry its own destinations. Then adding a channel never means editing the workflow.Preventing duplicate publishing
In a workflow polling every ten minutes, the biggest risk is posting the same piece twice. The fix is recording publications per (post, channel).
CREATE TABLE IF NOT EXISTS content_publications (
page_id text NOT NULL,
channel text NOT NULL,
published_at timestamptz NOT NULL DEFAULT now(),
status text NOT NULL DEFAULT 'published',
PRIMARY KEY (page_id, channel)
);Per (post, channel), not per post — that's what matters. When the blog succeeds and the newsletter fails, the next run retries only the newsletter. Recording at post level would republish to the blog on every retry.
What you need
- n8n — self-hosted or Cloud.
- One Postgres database — a single publication-record table.
- Access to Notion (or whatever content tool you use) — enough to query approved pages. It needn't be Notion: any tool that can return a list of approved posts via API works, changing only the first node.
- API credentials for each channel you publish to — but first confirm each one actually offers a public publishing API.
- A team webhook URL — Discord or Slack.
Which metrics, collected when
"Collect the results" needs to be specific too. This pipeline records the fact of publication; performance metrics are gathered on a separate cadence.
- Recorded at publish time —
page_id,channel,published_at, and the published URL. The workflow does this automatically. - Collected later — views, reactions, and referrals per channel. All zero immediately after publishing, so there's no point reading them then. A separate workflow that collects once at seven days is the practical arrangement.
- Referral tracking — put UTM parameters on published URLs. Without them you cannot later tell which channel produced the traffic. Channel name as
utm_sourceand the content slug asutm_contentis the standard shape. - What to compare — referrals per publication by channel drives decisions better than raw view counts. The channel with the biggest audience is not necessarily the one that converts.
page_id/title each surfaced as problems; and an empty array returned — sending no notification — when there is nothing to do. Node types and versions match our already-published workflows. Not verified: live Notion integration, live channel publishing, live Discord posting. And whether any given platform offers a public API changes over time, so the `CHANNELS` list in the workflow is a starting reference only — confirm each channel against its current developer documentation before you build. Last verified: 2026-08-15.Setup (30 minutes)
- Check the channels first — read each target platform's developer documentation to confirm whether external publishing is possible. This is step one of the whole case.
- Create the table — run the
content_publicationsSQL above. - Tidy the Notion database — create the properties above, with
channelsas multi-select. - Register the Postgres credential — n8n → Credentials →
Postgres→ Test. - Import the workflow — n8n → Workflows →
...→ Import from File. - Fill in the content query node — configure
Fetch Approved Postsagainst your content database, filtered to approved, mapping the response topage_id,title,body,channels[],published_channels[]. - Edit the CHANNELS list — in
Plan Publish Targets, keep only the channels you use and set eachautomatedvalue to match what you confirmed in step one. - Fill in the publish nodes — one per automated channel; use a Switch node to fan out if there are several.
- Set the team webhook — put your Discord or Slack URL into
Notify Team. - Test the manual path first — approve a post whose only channel is a non-automatable one. Nothing should publish; only the "publish manually" notice should appear.
- Test for duplicates — leave an already-published post in place and run the workflow again. Nothing should happen. If it posts twice, your publication-record mapping is wrong.
- Activate — it checks for approvals every ten minutes.
What happens in edge cases
- Some channels fail — only the successes are recorded, so the next run retries only the failures. Nothing rolls back: re-sending to a channel that missed out is safer than trying to retract a post that already went live.
- A post including a non-automatable channel — the automatable ones publish and that channel joins the "publish manually" list. Nothing is silently dropped.
- A post with no channels selected — approved but with
channelsempty gets reported under "needs attention." Approving and forgetting to pick channels happens constantly. - A channel name that isn't in the list — a typo or a new channel. No publish is attempted; it's reported as an unknown channel.
- A post with no image — this workflow doesn't require one. If a channel mandates a cover image, add that check to
Plan Publish Targets. - Channel rate limits — approving several posts at once fires several requests in quick succession. A 429 means that publication isn't recorded, so it retries next run. If approvals cluster at your organisation, add a delay to the publish nodes.
- Republishing an edited post — the publication record prevents it going out again. To republish deliberately, delete that
(page_id, channel)row.
One button and it's done. All the per-channel copy-pasting just vanished.— Content manager