Skip to main content

Grant a listener access to an episode or a bundle from an add-on

Premium Intermediate

A grant is non-tier access: a named set of episodes somebody holds outside any membership tier. Core keeps the record and adds the slug to the listener’s feed; your add-on owns the commerce around it (prices, refunds, receipts). It takes two steps, and skipping the first is the mistake that gets made: put episodes behind the grant slug, then grant the slug to the buyer.

A grant slug with no availability rows resolves fine and carries nothing, so the buyer sees no change and there is no error to explain why. expires_at null means a lifetime grant, not an unknown one. Re-granting a slug the person already holds extends the existing row and never shortens it.

A grant is not a buy-up. A grant is owned outright, so it survives an over-limit demotion, a licence grace period and the end of the listener’s membership; a buy-up degrades with the membership it was bought on. Use a grant for a one-time purchase and a buy-up for a recurring upsell.

If your add-on already holds the purchase record and you would rather not duplicate it, answer the question through a filter instead: Sell episode access from your own add-on.

Code

<?php
// 1. Put episodes behind a grant slug. A slug is a NAMED set shared by every
//    holder — a product or a bundle — never a per-person episode list, because
//    the feed cache is keyed by slug. Availability rows are written exactly as
//    they are for a tier.
( new \Benecaster\Availability\AvailabilityRepository() )->upsert(
    $episode_id,
    $show_id,
    'season-one',            // the grant slug
    '2020-01-01 00:00:00'    // available from (site-local; upsert() converts to UTC)
);

// 2. Grant it to the buyer. Keyed to the PERSON, so it survives a token reset,
//    a cancellation and any change of subscriber type.
( new \Benecaster\Entitlement\EntitlementRepository() )->grant(
    user_id:      $user_id,
    show_id:      $show_id,
    grant_slug:   'season-one',
    expires_at:   null,                             // null = lifetime
    source:       'benecaster-addon-episode-sales', // your add-on's slug
    external_ref: $order_id                          // your order or payment id
);

View on GitHub →

Need this built rather than just documented? See our services →