Podcast account setup — Apple Podcasts Connect + Spotify for Podcasters
One-time setup for submitting the Graphene podcast feeds to the two directories that drive 80%+ of audio-fiction discovery. Read this start-to-finish before opening either portal — half the questions Apple/Spotify ask are answered by values that already live in code, and getting them wrong post-submission is annoying to undo.
Time: ~90 minutes total, mostly waiting on art generation. Active form-filling is ~20 minutes per platform.
Repeatability: The network-level setup is one-time. Per-show submissions are repeated for each published show (~30 minutes for the first, ~10 minutes for each subsequent one — see the Phase 0 launch checklist for the per-show flow).
0. Before you start
Three decisions to make BEFORE opening either portal. Changing them after Apple verifies is painful, so settle them now.
0a. Cover art (REQUIRED — Apple rejects below 1400×1400)
The canonical network cover currently points at https://www.hivejournal.com/icon.png (the 32×32 bee favicon) — that's a placeholder per the comment in apps/backend/src/constants/graphene.ts. Apple will reject the submission with Invalid cover art (must be 1400×1400 minimum, 3000×3000 recommended).
What you need:
- 3000×3000 PNG or JPG, sRGB color space
- No transparent backgrounds (Apple flattens to white, looks bad)
- Show the network name "Graphene" or the logo prominently — this is what appears in directory search results at thumbnail size
- File size under 500 KB ideal, hard cap 3 MB
Where it needs to live:
- Public HTTPS URL (no auth, no redirect chain)
- Best path: upload to Supabase storage in the
public/bucket, get the public URL, point at it - Or: drop it in
apps/frontend/public/graphene-cover-3000.pngand referencehttps://www.hivejournal.com/graphene-cover-3000.png
After uploading, update apps/backend/src/constants/graphene.ts:
coverImageUrl: 'https://www.hivejournal.com/graphene-cover-3000.png',
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// your new URL
Push, deploy, verify by hitting https://www.hivejournal.com/api/podcast/rss and confirming the <itunes:image> tag points at the new URL.
0b. Owner email — brand vs personal
The canonical owner email is currently sandonjurowski@gmail.com (set in graphene.ts). This:
- Is the email Apple/Spotify use for ownership verification (they email you a code)
- Appears publicly on the Apple Podcasts listing (under "Contact")
If you want the public-facing email to be a brand address (hello@graphene.fm, podcast@graphene.fm, etc.):
- Set up the address FIRST — point the MX record to your mail provider (Google Workspace, Fastmail, Resend Inbound, etc.) and confirm you can receive mail at it
- Update
ownerEmailingraphene.ts→ push, deploy - Hit
https://www.hivejournal.com/api/podcast/rssand confirm the<itunes:owner><email>tag updated - THEN submit to Apple/Spotify
Why this order matters: Apple sends the verification code to whatever email is in the RSS at submission time. If you submit with one email and change it after, you'll need to repeat the verification dance (and Apple's UI for that is bad).
0c. Apple ID / Spotify account choice
Both Apple Podcasts Connect and Spotify for Podcasters tie podcasts to an account. You have three choices:
| Option | Pros | Cons |
|---|---|---|
| Personal Apple ID / Spotify | Easiest — already exists | Bound to your personal name, harder to hand off to a co-founder later |
Brand-dedicated Apple ID (podcast@graphene.fm + new Apple ID) | Clean separation, transferable | New Apple ID = no purchase history, occasional "verify this is you" prompts |
| Apple Business Connect / Spotify for Podcasters team account | Multi-user, audit log | More setup, may require business verification |
Recommendation for Phase 0: use the personal Apple ID + personal Spotify. Switching to a brand account later requires re-submitting the show, which Apple handles via the "transfer ownership" flow but takes ~2 weeks. Not worth blocking Phase 0 on it.
1. Network-level metadata (source of truth)
All these values flow from apps/backend/src/constants/graphene.ts into the RSS feed at /api/podcast/rss. You don't type these into Apple/Spotify forms — they're auto-populated when the portal validates your RSS URL. They're listed here so you can verify the portal's auto-pick matches.
| Field | Current value | Apple portal label |
|---|---|---|
| Show name | Graphene | "Show Name" / <title> |
| Author | Graphene | "Author" / <itunes:author> |
| Owner name | Graphene Network | "Show Owner" |
| Owner email | sandonjurowski@gmail.com | "Contact Email" / <itunes:owner><email> |
| Language | en-us | "Language" / <language> |
| Category | Fiction | "Category" |
| Subcategory | Drama | "Sub-Category" |
| Tagline | Written. Spoken. Yours. | (appears in <itunes:subtitle>) |
| Description | (400 chars — see graphene.ts) | "Description" / <description> |
| Cover art | ⚠️ needs replacement per 0a | <itunes:image> |
| Explicit | No (default) | "Explicit Content" |
| Show type | Episodic | "Type" — Episodic, since chapters can be enjoyed standalone-ish |
If any of these need to change long-term, edit the constant + redeploy. The RSS regenerates on every fetch, so the change flows to Apple/Spotify on their next crawl (which Apple does daily for existing feeds). See section 8 for exactly how episodes enter/leave the feed after submission and how many publish at once.
2. Apple Podcasts Connect submission
2.1 Network feed (one time)
The network feed at /api/podcast/rss mixes all chapters from all shows into one listing called "Graphene." Submit this once — it's the discovery anchor for the brand.
- Confirm cover art is fixed per 0a.
- Open podcastsconnect.apple.com → sign in with the Apple ID chosen in 0c.
- Click
+(top-left) →New Show. - Choose
Add a show with an RSS feed. - Paste:
https://www.graphene.fm/api/podcast/rss— proxied via a Next.js rewrite inapps/frontend/next.config.jsto the Railway backend's/api/podcast/rss. Brand-correct URL Apple/listeners see is graphene.fm, never the Railway preview URL. - Click
Add. - Apple validates the feed (5–60 seconds). Possible outcomes:
- Valid + auto-populated: the form fills in name, description, category, etc from the feed. Verify they match the table in section 1. Click
Submit. - Validation errors: see "Common Apple rejections" below. Fix the underlying RSS issue (probably in
apps/backend/src/services/season-distribution.tsor the constants file) → redeploy → re-validate.
- Valid + auto-populated: the form fills in name, description, category, etc from the feed. Verify they match the table in section 1. Click
- Apple emails the verification code to the
<itunes:owner><email>address. Enter it. - Wait 5–7 business days for the listing to go live. Apple emails when it's approved OR if it was rejected.
2.2 Per-show feeds (repeat per published show)
After the network feed is live, submit each individual show separately at /api/podcast/rss/show/<season-id>. Per-show feeds rank independently for genre search and are how listeners find specific shows vs the umbrella brand. Use /dashboard/admin/podcast-feed to track which shows are pending — see the Phase 0 launch checklist section 1 for the step-by-step.
3. Spotify for Podcasters submission
3.1 Network feed
- Open podcasters.spotify.com/pod/dashboard/podcast/get-started → sign in.
- Choose
I already have a podcast(you do — you've got the RSS). - Paste the network RSS URL:
https://www.graphene.fm/api/podcast/rss - Click
Next. - Spotify validates (faster than Apple — usually <30 seconds).
- Ownership verification: Spotify emails a code to the
<itunes:owner><email>address. Enter it. - Confirm metadata — category, language, explicit flag. Spotify auto-fills from the RSS.
- Submit. Listing typically appears in 1–3 days; you get an email when it's live.
3.2 Per-show feeds
Spotify accepts the same per-show RSS pattern as Apple. Same flow as 3.1 but with the per-show URL from the share kit or the podcast-feed admin page.
Note: Spotify shows have a "claim podcast" flow if someone else already submitted — for fresh submissions this doesn't apply, but if you see "this feed is already claimed" you'll need to use Spotify's transfer flow (slow, support-ticket-driven).
4. Common Apple rejections + how to fix
Apple's validator is strict. The most-seen failures:
| Rejection | Cause | Fix |
|---|---|---|
Invalid cover art (must be at least 1400×1400) | Cover URL points at the small bee favicon | Section 0a — generate + upload a 3000×3000 PNG, update coverImageUrl |
Missing <itunes:owner> | Owner block wasn't included | Already in the RSS — check the feed itself with curl https://.../api/podcast/rss and verify the <itunes:owner> tag is present |
Invalid <itunes:image> href | URL has query params, redirects, or returns non-200 | Use a clean public URL with no query string, served directly |
Episode <enclosure length> is 0 | An episode has no audio_url set, or audio_url returns 0-byte content | Check the offending episode in story_episodes — chapter_audio_url should be populated. If it's a half-rendered chapter, either render its audio or untrigger it |
Invalid RFC-822 pubDate | <pubDate> format is wrong (rare — the builder uses correct format) | Check apps/backend/src/services/season-distribution.ts — formatPubDate helper |
Episode duration mismatch | Spotify expects non-zero <itunes:duration> on every episode | Check story_seasons.audio_duration_seconds or story_episodes.chapter_audio_duration_seconds is populated |
If Apple rejects, use the in-house feed validator at /dashboard/admin/podcast-feed — click ↻ Re-run to see Apple-relevant issues with severity, location, and a recommendation per finding.
5. Verifying it worked
After Apple approval (5–7 business days)
- Search the show title in the Podcasts app on iOS or macOS — listing appears
- Tap → confirm the cover art, description, episode list render correctly
- Apple Podcasts Connect inbox: approval email (also flags any post-publish issues like a 404 on a recent episode's audio_url)
After Spotify approval (1–3 days)
- Search the show title on Spotify mobile or web
- Open the show page → confirm the metadata + most recent episodes are crawled
Both platforms
- New chapters published after submission auto-appear within a few hours (Apple crawls daily, Spotify ~hourly)
- The "I just published a new chapter, where is it?" question is usually answered by checking the RSS first — if
/api/podcast/rss/show/<id>includes the new chapter, it's just a crawl delay, not a feed bug
6. Changing things after submission
Metadata change (name, description, category, cover art)
Edit apps/backend/src/constants/graphene.ts → push → deploy. Apple/Spotify crawl the change on their next pass (Apple: daily, Spotify: ~hourly).
Don't change metadata in the Apple/Spotify portal directly — the next crawl will overwrite your portal edit with the RSS values. Always edit the source of truth.
Exception: cover art uploaded via the portal directly will sometimes override the RSS <itunes:image> — Apple's behavior here is inconsistent. Safer to host the cover at a stable URL and edit the constant.
Owner email change
Tricky. Apple/Spotify verified the OLD email. Changing the email in the RSS will:
- Apple: trigger a re-verification flow within 30 days
- Spotify: usually re-verifies silently
If you change the email, watch your old inbox for the next 30 days for any verification request you might miss in the new inbox.
Per-show iTunes metadata (subtitle, category, keywords)
Already settable per-show via the itunes_* columns on story_seasons (migration 266). The per-show RSS feed picks these up automatically. Recommended values for the 8 priority shows live in docs/marketing/2026-05-19-graphene-apple-podcasts-aso-sweep.md — apply via APPLY_ASO_SWEEP.sql (already applied to prod as of 2026-05-24).
Removing or replacing a show
If a show needs to be unpublished from the directories (rare):
- Set
story_seasons.is_published_to_graphene = false— the per-show feed at/api/podcast/rss/show/<id>returns an empty feed - Email Apple/Spotify support (links in their respective portals) to request takedown
- Mark the show as unsubmitted on
/dashboard/admin/podcast-feedso it doesn't surface as "pending" in the tracker
Apple/Spotify don't auto-delete from a feed going empty — they just stop adding new episodes. Takedown is a manual support request.
7. Quick reference — URLs you'll need
| URL | What it is |
|---|---|
https://podcastsconnect.apple.com/ | Apple Podcasts Connect dashboard |
https://podcasters.spotify.com/pod/dashboard/podcast/get-started | Spotify for Podcasters "add show" entry point |
https://www.graphene.fm/api/podcast/rss | Graphene network RSS feed |
https://www.graphene.fm/api/podcast/rss/show/<season-id> | Per-show RSS feed |
| /dashboard/admin/podcast-feed | In-house feed validator + per-show submission tracker |
https://castfeedvalidator.com/ | Third-party RSS validator (for second opinion) |
https://podba.se/validate | Another third-party validator |
8. How the feed stays current after submission (live-feed mechanics)
Once a feed is submitted, you never "publish episodes to Spotify/Apple" one at a time. The RSS feed is a live view of the database, regenerated on every fetch — Apple re-crawls roughly daily, Spotify roughly hourly, and each crawl ingests whatever the feed currently contains. New episodes appear automatically; nothing is pushed.
What's in the feed at any given crawl
An episode <item> is included only when all three hold (enforced in season-distribution.ts, the novel-mode chapter query):
- Audio is rendered — the chapter has a
chapter_audio_url. - The show is published to the podcast — its
season_publishings.podcast_published_atis set (the 🎧 Podcast button on the season page). This is the master gate: flip it on once for a show and every rendered chapter of that show flows into the feed automatically, now and going forward. - It's still in its free window (public feeds only) —
chapter_free_until IS NULL(always free) ORchapter_free_until > now. Chapters past their free window drop out of the public network + per-show feeds. The Graphene+ subscriber feed (/api/podcast/rss/subscriber/:token) skips this filter and always carries the full back catalog.
Journal-mode seasons contribute one item each (the compiled season MP3); novel-mode shows contribute one item per chapter.
How many episodes publish at once
- Hard cap: 500 items, ordered newest-rendered-first (
.order('chapter_audio_rendered_at', desc).limit(500)). That's a ceiling on the entire feed, not a per-crawl batch. - First submission: Apple/Spotify ingest everything currently in the feed in one pass — every published show's still-free chapters, up to the 500 cap.
- Ongoing crawls: the platform picks up whatever is new since its last crawl (usually just the chapters rendered since) and drops anything that fell out of its free window.
- Latency: the feed is CDN-cached for 5 minutes (
Cache-Control: max-age=300), so a freshly rendered chapter is eligible the moment the cache expires + the platform's next crawl runs.
Network feed vs per-show feeds
- Network feed (
/api/podcast/rss) = the "Graphene" show — all chapters from all published shows, mixed into one listing. This is the brand discovery anchor. - Per-show feeds (
/api/podcast/rss/show/<season-id>) = each show as its own Apple/Spotify listing, ranking independently for genre search.
Both obey the same three gating conditions + the 500-item cap above.
Practical gotcha — the public feed is a rolling window
Because the free-window filter drops chapters after they expire, a listener browsing the public Graphene show on Spotify sees recent + still-free chapters, not the complete archive. The full catalog lives only behind the Graphene+ subscriber feed. If you want a show's whole back catalog permanently visible on the public feed, set those chapters' chapter_free_until to NULL (always free) or widen the free window.
Related docs
- docs/marketing/2026-05-24-phase-0-launch-checklist.md — operational checklist for the 5 user-action items; section 1 covers the per-show submission flow after this account-setup is done
- docs/marketing/2026-05-19-graphene-apple-podcasts-aso-sweep.md — per-show iTunes metadata recommendations for the 8 priority shows
apps/backend/src/constants/graphene.ts— canonical network metadata constantsapps/backend/src/services/season-distribution.ts— RSS feed builder