# Public feeds and embed When **public feeds** are enabled by the instance admin, your podcast and episodes can have public pages and an RSS feed. You can also embed a single episode player on another site. ## Public feeds If the instance admin has **enabled public feeds**, your podcast's RSS feed and episode pages are visible to anyone with the link. Listeners can subscribe in podcast apps and visit public show/episode pages in the browser. ### Feed URLs - **Feed home** - List or discovery of public podcasts (URL depends on instance; e.g. `/feed` or root). - **Podcast page / RSS** - Public show page and RSS for a single show. Typically `/feed/{podcast-slug}` on the app host; check the show's public link section for the exact URL. - **Episode page** - Public page for a single episode (player, description, poll, reviews). URL usually includes podcast and episode slugs. Your show settings display the **RSS feed URL** to share with listeners and directories. If you use **Podcast Delivery** to push to S3 or another host, use the **public base URL** you configured so the feed and enclosures point to your CDN or bucket. Podcast and episode descriptions preserve newlines on the feed. The episode list supports pagination, search, and sort (newest/oldest). ### Show page blocks Depending on show configuration (see [Podcasts: Podcast 2.0](Podcasts.md#podcast-20-and-page-customizations)): - **Featured Trailer** - Playable trailer above Listen on / Follow when listenable. - **Listen on / Follow** - Platform and social links (including Discord). - **Recommended Podcasts** - Podroll above Reviews. - **Support The Show** - Funding links when set. - **Cast** - Public cast when visibility allows. - **Reviews** - See [Reviews](#reviews). **Page Customizations** control page theme, accent color, and which blocks are visible. Packaged Liquid themes replace the default SPA feed layout. See [Page themes](Page-themes.md). ### Episode player On podcast and episode feed pages, the player includes: - Waveform (when available), current time / duration, **playback speed** (1x, 1.5x, 2x, 2.5x), and volume - Volume and speed persist in `localStorage` across feed, episode, and embed players - **Listen position** is remembered per episode and shared between the feed list and the episode page - Collapsible **Chapters** list when chapter markers exist (jump/play; progress within the current chapter) - **Transcript** button opens a cue list; timestamps seek and play - Collapsible **Soundbites** under Chapters; playing one advances to the next and auto-pauses at the end - Trailer / Bonus type pills on cards when applicable On the feed list, playback controls appear while an episode is playing. On the episode page they stay visible when audio is available. ### Episode polls If the episode has an enabled poll, a collapsible **Poll** card appears in the episode header. Choice options are full-width selectable items; public results show percentages only (rounded to the nearest 2%; short answers omitted). Captcha/email may be required before submit when configured. Creators manage polls and see results in the episode editor. See [Episodes: Polls](Episodes.md#polls). ### Paid access and private RSS When the show accepts [Stripe payments](Stripe-payments.md), listeners use **Subscribe** on the feed. After Checkout, the success page reveals the **access token** and **private RSS** URL once. Use **Manage Subscription** (cookie or pasted token / private RSS URL) for Customer Portal, cancel-at-period-end, renew, regenerate/recover token, and refund requests. Private feed URLs use a per-subscriber token (prefix from `SUBSCRIBER_TOKEN_PREFIX`, default `hfm_sub_`). Full details: [Stripe payments](Stripe-payments.md). ### Custom domain When the podcast uses a **linking or managed domain**: - **Public Page** opens that domain (not `/feed/{slug}` on the app host) - The HarborFM header (name and logo) is hidden on feed pages; an admin **White Label** name can replace "HarborFM" on public surfaces (headers, share titles, meta) when set (see [Admin and settings](Admin-and-settings.md#account-and-access)) - Podcast cover is used as the favicon when available - Open Graph meta uses podcast-specific title, description, URL, and cover - `robots.txt` and `/api/sitemap.xml` resolve to that show’s host only (homepage `/` and `/{episodeSlug}`) - RSS `xml-stylesheet` uses a root-relative `/style.xsl` so browser feed previews work on the custom domain ### See also - [Export and delivery](Export-and-delivery.md) - Deploy feed and audio to a remote host - [Stripe payments](Stripe-payments.md) - Checkout and private RSS - [Embedding the player](#embedding-the-player) - [Admin: Global settings](Admin-and-settings.md#global-settings) - Enable/disable public feeds ## Reviews Podcast and episode **reviews** let listeners submit ratings and comments (name, email, rating, body). Submission is public; the show can optionally require **CAPTCHA** and restrict reviews to **subscribers only** (configured in [Edit Podcast Details](Podcasts.md#show-settings-and-artwork)). After submitting, the author may receive a verification email with links to **verify** or **delete** their review. The public list can filter by verified/approved status and load more pages when available; visitors can delete their own review, and managers/owners can approve, hide, or delete. Hiding a review does not block a new submit with the same email. Spam-flagged reviews from verified accounts stay unapproved so Approve can clear the flag. If the instance has it enabled, an optional **LLM spam check** runs on new reviews. On **scheduled** episodes (and guest review preview links), the page shows **episode** reviews only (not podcast-level reviews). Writing a new review is blocked until the episode is released. Separately, hosts can email **guest episode review** preview links when an episode becomes scheduled or published unlisted. See [Episodes: Guest episode review](Episodes.md#guest-episode-review). ## Embedding the player When public feeds are enabled, you can embed a **single episode player** on another website using an iframe. ### Embed URLs - **Main host** - `https://your-harborfm.example/embed/{podcast-slug}/{episode-slug}` - **Custom domain** - If your podcast uses a custom domain (e.g. `podcast.example.com`), you may use: `https://podcast.example.com/embed/{episode-slug}` (one segment; see main [README](../blob/main/README.md) "Embed" for exact rules). If a visitor opens the embed URL **directly** in the browser (not in an iframe), they are usually redirected to the full episode page. ### Responsive layout Narrow or short iframes adjust controls automatically: hide total duration below 250px width; compact volume below 200px; hide waveform, time, and speed below 150px width (play button centered); hide all playback controls below 150px height; stack season/episode above the brand link at 150px width. Global volume and playback speed from `localStorage` apply here too. ### Resizing the iframe to content The embed page can send its content height to the parent window so you can avoid a fixed iframe height and double scrollbars. In your page that contains the iframe, listen for messages and set the iframe height: ```javascript window.addEventListener('message', function (e) { if (e.data?.type === 'harborfm-embed-height' && typeof e.data.height === 'number') { document.getElementById('your-embed-iframe').style.height = e.data.height + 'px'; } }); ``` In production, check `e.origin` against your HarborFM origin if you want to allow only your embed host to resize the iframe. ### See also - [Public feeds](#public-feeds) - Main [README](../blob/main/README.md) - Embed section