Template Overrides (Developer Reference)
Benecaster renders episode pages, the episode archive, and subscriber account pages using PHP template parts. Every template part can be overridden from a child theme or parent theme — no plugin code required. Replacing a single file is enough to change any part of the output.
Looking for email templates? They use the same override mechanism but a different directory, and they’re documented separately in Customizing Email Templates — which lists every email template, the variables each one receives, and the benecaster_email_wrapper_args filter that handles branding without requiring an override at all. One difference: content an email editor add-on has stored for an email beats your theme’s override of it.
Override Path
Place an overriding file at:
wp-content/themes/[your-child-theme]/benecaster/[template-relative-path].php
The path mirrors the plugin’s own templates/ directory exactly. For example, to override the episode title:
Plugin default: wp-content/plugins/benecaster/templates/episode/title.php
Your override: wp-content/themes/my-child-theme/benecaster/episode/title.php
Resolution order: child theme → parent theme → registered add-on directories → plugin default. The first readable file found is used.
Your theme override wins over everything, add-ons included. An add-on can register its own template directory (see below), but it sits third in that list — it can replace one of Benecaster’s own defaults and it can never outrank a file you put in your theme. If you have overridden a part and it has stopped taking effect, an add-on is not the cause.
An override keeps the wording it was copied with. When Benecaster changes the text in one of its templates, a copy in your theme keeps showing the old text indefinitely, and nothing warns you. For example, the subscriber account page’s buy-up section is now headed Extras, but a copy of account/buyups.php made earlier still says Add-ons. After an update, compare your overrides with the current templates and copy across any changes you want.
Episode Template Parts
All episode template parts receive at minimum $episode_id (int) and $show_id (int).
| Template file | Additional $vars |
Description |
|---|---|---|
episode/single.php |
— | Outer wrapper; loads all other episode parts via benecaster_get_template_part() |
episode/title.php |
— | Episode title and episode type badge (badge suppressed for Full type) |
episode/explicit-badge.php |
$episode_id (int), $show_id (int), $context (string), $show_clean (bool) |
Explicit-content badge; shared by the single page, archive cards, [benecaster_episodes], [benecaster_player], and [benecaster_explicit_badge]. Renders nothing for clean episodes unless $show_clean. See template-episode-explicit-badge |
episode/player.php |
$episode_id (int), $show_id (int), $meta (EpisodeMeta), $user_tier (string), $can_access (bool) |
The player. episode/single.php chooses between this and player-locked.php. Print benecaster_get_episode_preview_video_html( $episode_id ) ahead of the audio branch; an override without it leaks nothing and shows no video preview. Plays the audio URL; otherwise, only when $can_access is exactly true, the embedded audio, then the video URL, then the embedded video. See What the player parts play below |
episode/player-locked.php |
$episode_id (int), $show_id (int), $user_tier (string) |
Locked state shown instead of the player: padlock, a line saying why, and a login or upgrade prompt. For a signed-in subscriber the line comes from (new \Benecaster\Episode\LockedEpisodeNotice())->message( $episode_id, $show_id, $user_id, $user_tier ): “Available to you on {date}.” or “This episode isn’t included in your plan.”, or null for the generic copy. An override should ask that class rather than work the rule out itself, and pass upgrade_message => '' to the upgrade prompt when it got a line. The whole output still passes through benecaster_locked_content_message |
episode/download.php |
$meta (EpisodeMeta) |
The “Download episode” link beneath the player. Links the audio URL, as the player plays it (benecaster_get_episode_audio_url()); on an episode with no audio URL, the video URL through benecaster_episode_video_url( $url, $episode_id, $show_id ). Renders nothing for an episode with only an embed code. Fires benecaster_before_episode_download and benecaster_after_episode_download, each with ($episode_id, $show_id). An override that returns when the audio URL is empty shows video-only episodes no link, even with Show a download link on video-only episodes on. |
episode/transcript.php |
$meta (EpisodeMeta) |
The “Read the transcript” link, straight after the download link. It links to the episode’s transcript file; it does not display the text. Fires benecaster_before_episode_transcript and benecaster_after_episode_transcript, each with ($episode_id, $show_id). Renders nothing when the episode has no transcript URL. |
episode/description.php |
— | Episode show notes / description (HTML) |
episode/content.php |
$can_access (bool), $user_tier (string) |
Extended episode content; may show teaser when $can_access is false |
episode/guests.php |
— | Guest list (rendered when episode has guest data from Guest Manager add-on) |
episode/references.php |
$references (array) |
Show notes references grouped by group_id; each entry: {id, reference_id, label, library_label, url, description, display_label, group_id, display_order}. Render url as it is — the plugin’s template adds nothing to it — and apply the benecaster_episode_references filter, as the plugin’s copy does. An override copied from an older version may still add an ?ep= parameter to every link; refresh it. |
episode/custom-fields.php |
$field_groups (array) |
Custom field values; each group: {group, fields, values} |
episode/share.php |
$share_links (array) |
Share links; each entry: {label, url, class} |
episode/subscribe.php |
— | Subscribe CTA for non-subscribers; suppressed for subscribers |
episode/navigation.php |
$prev_episode (\WP_Post|null), $next_episode (\WP_Post|null) |
Previous/next episode links within the same show |
episode/download.php and episode/transcript.php are included by episode/single.php, and the parts themselves do not check access. single.php includes the download link only when the episode’s download setting is on and the visitor can play the episode (the show’s Show a download link on episode pages for an episode with an audio file, Show a download link on video-only episodes for one whose only file is a video), narrowed by the benecaster_show_episode_download filter. It includes the transcript link only when the episode has a transcript URL and the visitor can access the episode, narrowed by the benecaster_show_episode_transcript filter. A theme that overrides single.php must make the same decisions itself by calling \Benecaster\Episode\EpisodePageLinkGate::shows_download() and shows_transcript() before including the parts, or the links never appear. Don’t include either part from anywhere else, because an override of it assumes the check already happened.
$user_tier is the subscriber’s internal tier slug, or an empty string when the visitor is not subscribed. $required_tier is the minimum tier required to access the episode, or an empty string when undetermined.
What the player parts play
episode/single.php and archive/episode-card.php include their player part when the benecaster_show_episode_player filter says so, and pass it $can_access: whether the visitor passed the access check, not whether that filter opened the player. The difference matters because a callback can open the player for a visitor who cannot access the episode (the preview clip does), and the clip is swapped in on the audio URL only. Nothing swaps an embed or a video, so the parts play those only when $can_access is exactly true. A player opened for anyone else plays the audio URL, or nothing.
Overriding episode/single.php? Keep its do_action( 'benecaster_after_episode_content_area', … ) line. The Transcription Service add-on prints the episode’s Transcript section there; without the line the section disappears and Benecaster’s Read the transcript link comes back in its place, under the same transcript visibility.
Overriding episode/single.php or archive/episode-card.php? Pass 'can_access' => $can_access to the player part, and in archive/episode-card.php to the excerpt part too. Without it each part treats every visitor as locked: an episode with no audio URL (an embed, or a video only) shows no player to anyone, and the card’s excerpt shows the teaser.
Overriding either player part? Apply the same check before you play an embed or a video. Print an embed code through \Benecaster\Episode\EmbedHtml::sanitize(), never wp_kses_post(): WordPress’s post allowlist has no <iframe>, so it strips the embed and leaves an empty player. EmbedHtml::sanitize() keeps an <iframe> only when it loads from an https:// address.
When a player part plays a video or an embed, it loads Benecaster’s base stylesheet, which sizes the video to its column and caps an embed’s width at it. An override of either part keeps its own markup and does not load the stylesheet; call \Benecaster\Shortcodes\ShortcodeBaseStyles::enqueue() from it, or size the video in your theme. See Styling the Shortcodes.
Archive Template Parts
All archive template parts receive at minimum $show_id (int).
| Template file | Additional $vars |
Description |
|---|---|---|
archive/archive.php |
— | Outer archive wrapper; loads header, episode cards, pagination |
archive/header.php |
$title (string), $description (string) |
Archive heading and show description |
archive/filters.php |
— | Sort and filter controls bar |
archive/episode-card.php |
$episode_id (int), $can_access (bool) |
Episode card outer wrapper; loads all card sub-parts |
archive/episode-card/artwork.php |
$episode_id (int), $can_access (bool) |
Episode card artwork image |
archive/episode-card/title.php |
$episode_id (int), $can_access (bool) |
Episode card title link |
archive/episode-card/meta.php |
$episode_id (int), $meta (array) |
Card meta: episode_number, season_number, duration, pub_date |
archive/episode-card/excerpt.php |
$episode_id (int), $can_access (bool) |
Episode card excerpt. When $can_access is false, or not passed, it shows the episode’s teaser, or nothing when the teaser is off. Text inside [benecaster_tier_gate] or [benecaster_access_gate] is never quoted, [benecaster_else] text included |
archive/episode-card/player.php |
$episode_id (int), $show_id (int), $meta (EpisodeMeta), $user_tier (string), $can_access (bool) |
Inline player in the card (mini player). Print benecaster_get_episode_preview_video_html( $episode_id, 'card' ) ahead of the audio branch, as for episode/player.php. Plays the audio URL; otherwise, only when $can_access is exactly true, the embedded audio, then the video URL, then the embedded video (through benecaster_episode_embed_video). See What the player parts play below |
archive/episode-card/locked.php |
$episode_id (int) |
Locked state overlay on card when $can_access is false |
archive/pagination.php |
$total_pages (int), $pagination_html (string|null) |
Pagination controls |
archive/no-results.php |
— | Empty state shown when no episodes match the current filters |
Show Page Template Parts
All show page template parts receive $show_id (int).
| Template file | Additional $vars |
Description |
|---|---|---|
show/single.php |
— | Outer show page wrapper; loads all other show parts |
show/header.php |
$title (string), $description (string), $artwork_url (string) |
Show title, artwork, and description |
show/subscribe-links.php |
$links (array) |
Platform subscribe links; each entry: {platform, url} |
show/tiers.php |
$user_id (int), $user_tier (string) |
The Subscribe section. Reads the show’s tiers itself, from whichever source the show uses (a membership plugin’s tier mapping, or built-in membership’s tiers), runs them through the benecaster_show_tiers filter, and embeds [benecaster_subscribe]. |
show/episode-list.php |
$episodes (WP_Query) |
Embedded episode listing |
show/stats.php |
$episode_count (int) |
Public stats block (hidden by default; enable via benecaster_show_stats_visible filter) |
Global Template Parts
Locked state templates receive at minimum $episode_id (int) and $show_id (int).
| Template file | Additional $vars |
Description |
|---|---|---|
global/locked-message.php |
$required_tier (string), $user_tier (string) |
Locked state shown when a visitor cannot access an episode; renders padlock indicator + either upgrade prompt (wrong tier) or login prompt (not logged in) |
global/upgrade-prompt.php |
$episode_id (int), $show_id (int), $required_tier (string), $user_tier (string), $upgrade_message (string|null, optional) |
Upgrade CTA shown inside the locked state to logged-in visitors. $upgrade_message absent or null shows the default “This episode is available to subscribers only.”; '' leaves the line out and keeps the View subscription options button |
global/login-prompt.php |
— | Login/subscribe CTA shown inside the locked state to logged-out visitors |
Account Template Parts
Most account template parts receive $show_id (int) and $user_id (int). The wall name prompt receives $user_id only — the wall name is not show-scoped.
| Template file | Additional $vars |
Description |
|---|---|---|
account/subscription.php |
$sub (array), $show_id (int), $user_id (int), $since_date (string), $tier_display (string) |
One subscription card. $sub holds the show, tier, masked feed URL and app links; $sub['upcoming_price_changes'] lists pending price changes (built-in membership); $sub['promote_signup_url'] is the payment link during a Promote grace period (empty string: show no link) |
account/buyups.php |
$buyups (array), $has_native (bool) |
The Extras section, which is what subscribers see buy-ups called — one card per buy-up available to the subscriber’s tier on this show. Wrapped by the benecaster_before_account_buyups / benecaster_after_account_buyups actions, and contains the benecaster_buyup_soldout_display filter. The per-row shape is documented in the file header. See the note below before overriding this one. |
account/tier-switch.php |
$cadence (array) |
The Your Plan section — the subscriber’s self-service move to another tier, built-in membership only. It takes the same $cadence payload as account/cadence-switch.php: both render one description of the subscription, which is why they can’t disagree about what is pending. Wrapped by benecaster_before_account_tier_switch / benecaster_after_account_tier_switch. See the note below before overriding this one or cadence-switch.php. |
account/cadence-switch.php |
$cadence (array) |
The Billing period section — the subscriber’s self-service switch between their tier’s billing periods, built-in membership only. $cadence carries current, options, blocked and pending; the option shape is documented in the file header. Wrapped by benecaster_before_account_cadence_switch / benecaster_after_account_cadence_switch. See the note below before overriding this one. |
account/free-tier-switch.php |
$cadence (array) |
The Free Plan section, built-in membership only: a paying subscriber’s move to the show’s free plan, or, for a member already on it, the link to take out a paid plan. It reads free_option, pending_free and upgrade_url from the same $cadence payload and renders nothing when all three are empty. Its root keeps data-cadence-switch and data-cadence-kind="free", which the front-end script needs to drive the buttons. Wrapped by benecaster_before_account_free_switch / benecaster_after_account_free_switch. |
account/billing-portal.php |
$sub (array) |
Billing management panel for subscribers on Benecaster-managed billing |
account/feed-url.php |
$sub (array), $masked_url (string) |
The subscriber’s feed URL with a Copy button. Shows the working URL from $sub['feed_url'] when it can be recovered, and publishes it as data-feed-url for the front-end script, which enables Copy, the QR code and the app links from it. When $sub['feed_url'] is null it shows $masked_url (the first 8 characters of the token) with Copy disabled. Reports “This feed is not currently active…” with no URL when $sub['feed_is_dark'] is true. |
account/qr-code.php |
$sub (array) |
QR code for the subscriber’s feed URL, drawn client-side from the data-qr-value attribute, which carries $sub['feed_url']. Without a recoverable URL the canvas stays empty until the subscriber resets their feed link. |
account/app-links.php |
$sub (array), $spotify_url (string) |
Deep links to podcast apps from $sub['app_links'] (app key to URL), built on the server from $sub['feed_url']. The array is empty when the URL cannot be recovered, and the buttons stay disabled until a reset. |
account/token-reset.php |
$sub (array) |
Self-service Reset my feed link button and its two-step confirmation, whose warning states what a reset costs the subscriber. An override copied from an older version keeps the older, softer warning. Renders nothing when $sub['feed_is_dark'] is true — see note below. |
account/wall-name-prompt.php |
$wall_name_prompt_prefill (string), $prompt_show_name (string) |
First-run overlay prompting new subscribers to set their wall name; shown once, then suppressed. Gated by benecaster_show_wall_name_prompt. Receives $user_id only — no $show_id. |
$sub['feed_url'] is null whenever the URL cannot be recovered — a token created before encrypted copies were stored, a changed or missing encryption key, a host without libsodium — and always when the feed is dark. Treat null as ordinary and fall back to $masked_url; never build a URL from anything else. An override that drops the data-feed-url or data-qr-value attribute silently disables Copy, the QR code and the app buttons. Because these three templates render the working URL, an override must only ever be shown to the subscriber who owns it. An override of feed-url.php, qr-code.php or app-links.php copied from an older version of Benecaster keeps showing the masked URL and disabled buttons until you copy the current template again.
Overriding the Buy-ups and Billing Period Templates
These two templates carry the price a subscriber agrees to, not just the price they see. Each button echoes an amount back to the server, and the server refuses the request if it doesn’t match what it would charge. An override that shows the wrong figure therefore breaks the action, not just the display. Copy both from the current built-in template, and re-copy them when the plugin changes them.
account/buyups.php. Each $buyups row describes the price that matches this subscriber’s billing period: price (major units, and nullable), amount_cents, currency, interval_unit and interval_count. A null price means the buy-up isn’t sold on their period. Never treat it as 0, because 0 is a real price (a buy-up included free). The Add to my plan button must carry data-buyup-amount-cents from that row’s amount_cents. An override written before per-period pricing shows everyone the buy-up’s default (monthly) price, and every purchase through it fails with 409 amount_mismatch. Nothing server-side points at the template as the cause.
The plan and billing-period sections share one pending change. Only the section that owns the pending change shows it with a Cancel this change button; the other points at it. Two override traps follow, and both look like the feature failing rather than an old copy:
- An override of
account/dashboard.phpmade before the plan section existed doesn’t include it, so subscribers see no Your Plan section at all. - An override of
account/cadence-switch.phpmade before the plan section existed renders its own pending block, so a pending plan move shows a second, working Cancel this change button.
Refresh both from the current built-in templates.
account/cadence-switch.php. Each option’s amount_formatted is the tier price alone. When the subscriber holds buy-ups, the option also carries buyups, buyups_amount_cents, total_formatted, buyups_included and buyups_dropped, and the Switch to… button must carry data-buyups-amount-cents. An override without it can’t switch a subscriber who pays for a buy-up (400 confirm_amount_required), and one that omits the included and lost lines lets a subscriber lose a free buy-up without being told. See The Subscriber Account Page.
account/tier-switch.php. Each option in tier_options carries lands: now for an upgrade, which is charged the day the subscriber asks, and renewal for everything else. The template prints a different line for each — “Starts today. You pay the difference for the rest of this billing period now…” or “Starts {date}, at your next renewal. Nothing is charged today.” — and lists buyups_ending, the paid buy-ups the new plan doesn’t offer, which end at the end of the current period. When pending.awaiting_payment is true, an upgrade is waiting for the subscriber’s bank, and the pending block says so and gives the Cancel this change button a data-awaiting-payment attribute. An override made before upgrades took effect straight away prints the renewal line under an upgrade that is charged today, and leaves out the ending buy-ups. The charge itself is still right — the amount comes from a price check the plugin’s script runs when the button is clicked, and the confirmation box shows it before anything is taken — but the page contradicts it. Re-copy the template. See A Subscriber Was Charged When the Page Said Nothing Would Be Charged Today.
account/avatar.php and account/dashboard.php are overridable the same way and have their own pages — see Account Avatar Template.
Template Functions
Each of these has a full reference entry in the function library — signature, parameters, return values, and examples.
benecaster_get_template_part( $template, $vars = [], $echo = true )
Load a template part with child theme → parent theme → registered add-on directories → plugin default resolution.
// Load and echo a template part (typical usage in another template):
benecaster_get_template_part( 'episode/title', [
'episode_id' => $episode_id,
'show_id' => $show_id,
] );
// Load and return the rendered HTML:
$html = benecaster_get_template_part( 'episode/title', [
'episode_id' => $episode_id,
'show_id' => $show_id,
], false );
Variables in $vars are extracted into the template’s local scope — $vars['episode_id'] becomes $episode_id inside the template file.
benecaster_locate_template( $template )
Check whether a theme override exists for a template part. Returns the absolute path of the override file if found, or false if the plugin default will be used.
$override = benecaster_locate_template( 'episode/title' );
if ( $override ) {
// A theme file is overriding this template part.
}
This searches the child theme and parent theme only. It deliberately does not see registered add-on directories — an add-on directory is not a theme override, and this function answers the theme question. For “which file would actually load?”, use benecaster_get_template_path() below.
benecaster_get_template_path( $template )
Return the full path of the file that would be loaded — theme override, registered add-on directory, or plugin default. Returns an empty string when no file exists at any of the four locations.
$path = benecaster_get_template_path( 'episode/title' );
// '/var/www/html/wp-content/themes/my-child-theme/benecaster/episode/title.php'
// or the plugin's own default path if no theme override exists.
benecaster_register_template_directory( $path )
For add-on authors. Registers a directory that Benecaster will search when resolving template parts, so an add-on can ship its own parts — both ones that replace a Benecaster default and ones Benecaster has never heard of. Call it from a benecaster_boot callback.
add_action( 'benecaster_boot', function (): void {
benecaster_register_template_directory( plugin_dir_path( __FILE__ ) . 'templates' );
} );
The directory mirrors the plugin’s templates/ structure, so {$path}/guest/profile-card.php answers the template name guest/profile-card.
Registered directories sit third, behind both themes. A part shipped by an add-on is still fully overridable from your theme at {theme}/benecaster/guest/profile-card.php — registration does not change that, and add-on authors should treat any part they ship as a public template.
Among add-ons the order is registration order, which is add-on boot order — so two add-ons registering the same template name is not a supported arrangement.
benecaster_get_header( $name = null )
Load the site header in a way that is compatible with both classic and block (FSE) themes. Use this in outer template files (single.php, archive.php) instead of calling get_header() directly.
- On block themes: renders the
headerblock template part viablock_template_part( 'header' ). - On classic themes with
header.php: callsget_header( $name )normally. - On themes without a header file: degrades silently — no output, no deprecation notice in the log.
Eliminating the deprecation log spam from FSE themes was the primary reason this helper was added. If you are overriding templates/episode/single.php, templates/show/single.php, or templates/archive/archive.php in your theme, replace any get_header() call with benecaster_get_header().
<?php benecaster_get_header(); ?>
<main class="benecaster-episode-single">
<?php benecaster_get_template_part( 'episode/title', [ 'episode_id' => $episode_id ] ); ?>
<?php // ... ?>
</main>
<?php benecaster_get_footer(); ?>
benecaster_get_footer( $name = null )
Counterpart to benecaster_get_header(). Use in outer template files instead of get_footer().
- On block themes: renders the
footerblock template part viablock_template_part( 'footer' ). - On classic themes with
footer.php: callsget_footer( $name )normally. - On themes without a footer file: degrades silently.
Two Customization Layers
Template overrides replace whole files. When you only need to change a value, tweak markup, or inject content without replacing a template, use the complementary hook layers instead:
Before/after action hooks fire around every rendered section:
// Inject content before the episode navigation without overriding navigation.php:
add_action( 'benecaster_before_episode_navigation', function ( int $episode_id, int $show_id ): void {
echo '<hr class="my-divider">';
}, 10, 2 );
Output filters intercept specific values before they reach the template:
// Add a Bluesky link to episode/share.php without overriding it:
add_filter( 'benecaster_episode_share_links', function ( array $links, int $episode_id ): array {
$links['bluesky'] = [
'label' => 'Share on Bluesky',
'url' => 'https://bsky.app/intent/compose?text=' . rawurlencode( get_permalink( $episode_id ) ),
'class' => 'benecaster-share-bluesky',
];
return $links;
}, 10, 2 );
See Filter Hooks Reference and Action Hooks Reference for the full catalog.
Styling the Shortcodes
Benecaster ships a base stylesheet for its front-end shortcodes — the player, episode lists, latest-episode card, pricing table, episode navigation, related episodes, and the share, social, platform and RSS links. It loads only on a page where one of them appears, or where the episode page or an archive card plays a video or an embed (the stylesheet fits the video to its column; audio needs none). If your site already styles these shortcodes, expect them to look a little different after updating, and check them once.
Adjust it. Every rule has zero specificity, so a plain class selector in your theme always wins. The common knobs are CSS custom properties:
:root {
--benecaster-gap: 1.5rem;
--benecaster-radius: 0;
--benecaster-border: 1px solid #ddd;
--benecaster-icon-size: 2rem;
--benecaster-card-min: 16rem;
}
Turn it off. For a theme that styles the shortcodes itself, add this to functions.php or a plugin:
add_filter( 'benecaster_shortcode_base_styles', '__return_false' );
wp_dequeue_style() does not work for this stylesheet: the shortcodes load it while the page is rendering, after a theme’s dequeue has already run. Use the benecaster_shortcode_base_styles filter instead. It is read once, on init, so it cannot go inside a template. Turning it off also removes it from block editor and Elementor previews, so the editor matches the front end.
Use your own icons. The icon styles of the social, platform, share and RSS link shortcodes draw a built-in icon for every platform. To draw your theme’s icons instead, use the benecaster_platform_icon_svg filter — see the recipe Use your own icon set for social, platform and share links.
Templates Management REST Endpoints
The Settings → Templates screen is backed by four REST endpoints. Use these for headless setups, add-ons that bundle a custom template, or custom admin UI.
| Endpoint | Purpose |
|---|---|
GET /benecaster/v1/templates |
List all installed templates; response includes slug, name, preview_url, active (bool), built_in (bool), plus can_upload (bool) and upload_blocked_reason (string, or null when can_upload is true) |
POST /benecaster/v1/templates/upload |
Upload a template ZIP (multipart/form-data, field name template_zip) |
POST /benecaster/v1/templates/{slug}/activate |
Activate an installed template for the current show |
DELETE /benecaster/v1/templates/{slug} |
Remove an installed template; returns 403 when built_in: true |
All endpoints require manage_options capability and a valid WP REST nonce. POST /benecaster/v1/templates/upload also needs the install_plugins capability, on a site where installing code from the admin is allowed (DISALLOW_FILE_MODS not set), because a template runs PHP on your site. Otherwise it answers 403 benecaster_template_upload_forbidden with the reason. GET /benecaster/v1/templates reports whether the current user may upload as can_upload, with the reason in upload_blocked_reason when they may not. Built-in templates (Default Light, Default Dark) are always present and cannot be deleted.
Recipes
Worked examples for the patterns on this page live in the recipe library, each with runnable code you can copy:
- Override a single episode template part from a child theme
- Load a custom template part from an add-on
- Check episode access in a shortcode or widget
- Replace the episode share links with a custom share card
- Inject a “Listen now” CTA button into every archive episode card
- Hide the sort controls on the episode archive
- Turn off, or restyle, Benecaster’s base styles for its shortcodes
- Use your own icon set for social, platform and share links
- Customize the upgrade prompt for a specific show
- Add a custom section to the show page
- Add a custom section below the QR code on the subscriber account page
See Also
Need this built rather than just documented? See our services →