SKILL.md
Substack Publisher
Substack has no official public write API and no verified post-by-email address. So the workflow is: produce a clean, paste-ready draft file, then publish through the logged-in web editor with a human in the loop. Never pretend an unattended path exists.
Configuration
| Variable | Meaning |
|---|---|
SUBSTACK_PUB_URL | Publication URL, e.g. https://<PUBLICATION>.substack.com |
SUBSTACK_RSS | Feed URL, $SUBSTACK_PUB_URL/feed |
Do not ask the owner for a publishing email address. That path is dead unless Substack reintroduces a verified post-by-email feature.
Workflow
- Draft in Markdown. Save to
drafts/substack/YYYY-MM-DD-<slug>.md. Front-load a working title and a one-line subtitle at the top of the file; the editor asks for both. - Format for the editor paste. Substack's editor handles pasted rich text better than raw Markdown. Keep structure simple: H2/H3 headings, short paragraphs, pull quotes as blockquotes, images referenced by URL with a caption line under each. No tables (Substack renders them poorly), no footnote syntax (add footnotes in the editor).
- Publish via browser. Open
$SUBSTACK_PUB_URL/publish/post, paste, restore any formatting the paste dropped, set the audience (free vs paid), preview both email and web views, then let the human hit publish or schedule. - Confirm and cross-publish. After publish, the post appears in
$SUBSTACK_RSS. Downstream mirrors (email list sync, social snippets) consume the feed; run them after confirmation, not before.
Hard rule: no unattended publishing
Drafting is autonomous. Publishing is not. Any --publish style automation must refuse and route to the browser flow. If long-form needs true unattended publishing, move that content to beehiiv or ghost-publisher, which have real APIs.
Good vs bad
Bad: Agent writes the essay, finds no API, and "publishes" by emailing the draft to a guessed <publication>@substack.com address. The post never appears, the agent reports success anyway.
Good: Agent writes the essay to drafts/substack/2026-08-12-shipping-solo.md, formats it paste-ready, opens the editor URL, and tells the owner: "Draft ready and loaded. Review the email preview, then publish or tell me what to change."
Verification
After the human publishes, fetch the feed: curl -s $SUBSTACK_RSS | grep -o '<title>[^<]*</title>' | head -5. Expect the new post's title in the output within a few minutes. If it is absent, the post is still a draft or was scheduled: check the editor's post list before telling anyone it shipped.
Completion checklist
- [ ] Draft saved to
drafts/substack/with title and subtitle lines - [ ] Formatting is paste-safe: no tables, no footnote syntax, simple headings
- [ ] Audience (free/paid) set intentionally, not left on default
- [ ] Human reviewed the email preview before publish
- [ ] Post confirmed live via the RSS feed
- [ ] Cross-publish steps run only after confirmation
Any box unchecked: not done. Fix or say so.
Footguns
- Email preview differs from web preview. Long code blocks and wide images get clipped in email clients. Always check the email preview; that is what most subscribers see first.
- Paste drops formatting silently. Blockquotes and nested lists are the usual casualties. Diff the pasted result against the draft by eye before publish, not after.
- Reporting "published" from a draft save. Substack autosaves drafts; a saved draft looks like success in the editor. Only the RSS check proves it is live.