HarborFM

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):

  • 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.

Page Customizations control page theme, accent color, and which blocks are visible. Packaged Liquid themes replace the default SPA feed layout. See Page themes.

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.

Paid Access and Private RSS

When the show accepts Stripe payments, 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.

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)
  • 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

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). 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.

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 "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:

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