# Substack Publishing

> Substack Publishing

- Skill: `lucadominguez/substack-publishing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lucadominguez/substack-publishing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lucadominguez/substack-publishing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: lucadominguez (https://skillmd.com/u/lucadominguez)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lucadominguez/substack-publishing

---

# Substack Publishing

Publish articles to Substack publications. Two methods: email-to-Substack (fastest, uses Himalaya CLI) and browser automation (Playwright, for full control).

## Prerequisites

1. **Himalaya CLI** installed and configured with an email account
2. **Substack publication** with post-by-email enabled
3. **Playwright** (optional, for browser-based publishing)

## Method 1: Email-to-Substack (Recommended)

Each Substack publication has a secret post-by-email address. Find it at:
`https://{publication}.substack.com/publish/settings` → "Post by email"

### Setup

1. Configure Himalaya (one-time):
```bash
himalaya account configure
```

2. Get the Substack secret email address from the publication settings

3. Send a post:
```bash
cat << 'EOF' | himalaya template send
From: your@email.com
To: {publication}@substack.com
Subject: Article Title

Full article content in markdown or HTML here.
EOF
```

### Rules for Email Posts

- **Subject line** = article title
- **Body** = full article content (Substack supports markdown and HTML)
- **Images** = attach as inline attachments or use hosted URLs
- **First line** after headers becomes the subtitle if it's wrapped in `<h2>` or `##`
- Substack auto-detects formatting and converts to their editor

### Email Send Script

```python
# scripts/send_to_substack.py
import subprocess, sys, os

def send_to_substack(publication_email, title, content_path):
    with open(content_path) as f:
        content = f.read()
    
    email = f"""From: luca.dominguez.couto@gmail.com
To: {publication_email}
Subject: {title}

{content}
"""
    result = subprocess.run(
        ['himalaya', 'template', 'send'],
        input=email, text=True, capture_output=True
    )
    return result.returncode == 0, result.stderr
```

## Method 2: Browser Automation (Playwright)

For full control over formatting, images, metadata, and scheduling.

### Setup
```bash
playwright install chromium
```

### Publishing Steps

1. **Login** to Substack at `https://{publication}.substack.com/publish`
2. **Click** "New Post" button
3. **Fill** title, subtitle, body content
4. **Upload** images via the media uploader
5. **Set** metadata: tags, SEO title, social preview
6. **Schedule** or **Publish**

### Key Selectors
- Title input: `[data-testid="post-title"]` or `[placeholder="Title"]`
- Subtitle input: `[data-testid="post-subtitle"]` or `[placeholder="Subtitle"]`
- Editor body: `[data-testid="editor"]` or the ProseMirror editor div
- Publish button: `[data-testid="publish-button"]`
- Schedule button: `[data-testid="schedule-button"]`

### Playwright Script Template
```python
# scripts/publish_to_substack.py
from playwright.sync_api import sync_playwright

def publish_article(publication_url, title, subtitle, content, tags=None):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        
        # Login (use saved cookies)
        page.goto(f"{publication_url}/publish")
        # ... fill in title, subtitle, content, upload images
        
        # Publish
        page.click('[data-testid="publish-button"]')
        page.click('[data-testid="confirm-publish"]')
        browser.close()
```

## Post Formatting Guide

### Title
- Specific, surprising, or useful
- Never clickbait
- Max 60 chars for email deliverability

### Subtitle
- Tells exactly what the reader will get
- First `##` or `<h2>` in email body becomes subtitle

### Body
- Markdown preferred (Substack converts)
- Images: use hosted URLs (GitHub raw, Imgur, etc.) or attach inline
- HTML tables: Substack supports them
- Code blocks: use triple backtick with language

### Metadata
- Tags: comma-separated, max 5
- SEO title: can differ from display title
- Social preview: custom image URL or auto-generated

## Post-by-Email Addresses

To find the secret email for each publication:
1. Go to `https://{publication}.substack.com/publish/settings`
2. Look for "Post by email" section
3. Copy the `{publication}@substack.com` address

Each publication under your account has its own unique address.

## Pitfalls

1. **Email-to-Substack has size limits** — long posts with many images may need to be split or use browser method
2. **Images in email posts** must be hosted URLs or inline attachments — relative paths won't work
3. **Drafts** — email posts may go live immediately. To draft, add `[Draft]` prefix to subject
4. **Formatting** — complex HTML may not render correctly. Stick to markdown
5. **Cookies expire** — Playwright sessions need fresh cookies periodically. Use email method for reliability
6. **Scheduling** — email method can't schedule. Use browser method for future-dated posts
