benecaster_follower_signup()
Signs one email address up as a free follower of a show. This is the public entry point for custom signup surfaces — a popup overlay, a Gutenberg block, a WP-CLI command, a landing page posting to your own endpoint.
Everything the built-in [benecaster_follower_signup] shortcode does, this does: it provisions a WordPress user when the address is new, writes a follower token row, fires benecaster_token_generated (which sends the welcome email) and then benecaster_follower_signed_up. It is the same code path, so you inherit all of it without reimplementing any of it.
Idempotency is the whole value, and it is why the return is an array rather than an ID. Calling this twice for the same address and show is safe and is expected — a second call produces no second account, no second token and no second welcome email.
Follower signup is not licence-gated. It works identically on the free WordPress.org plugin and on any paid plan. The only limit is the 100-follower cap on an unlicensed install, enforced at signup: at 100 the form stops accepting new followers and shows the podcaster an upgrade prompt. Existing followers are unaffected, and no follower is ever demoted to the public feed.
THE CAP IS A NEW FAILURE MODE YOU MUST HANDLE: this function returns a WP_Error with code benecaster_follower_cap_reached once an unlicensed show holds 100 followers. A refusal provisions nothing — no WordPress user, no token, no welcome email — so a caller that assumes an array return and reads $result['created'] will fatal on a WP_Error. Check is_wp_error( $result ) first. Existing followers are untouched by a refusal, and the same address can be submitted again successfully once the show is connected.
The cap has no filter, and that is deliberate rather than an omission. benecaster_unlicensed_follower_cap and benecaster_get_unlicensed_follower_cap() were deleted on 2026-08-15; the cap is now the constant FollowerSignup::UNLICENSED_FOLLOWER_CAP with nothing in front of it. Do not write a recipe that raises it — the previous filterable gate is the reason the old one was removed.