Skip to main content

Add a Custom Section to the Analytics Digest

Premium Intermediate

Add your add-on’s data to any Benecaster Analytics Digest (daily, weekly, or monthly) by injecting a data payload via benecaster_analytics_digest_data and registering a custom section via benecaster_analytics_digest_sections. This is the standard integration pattern used by Listener Support (donation totals), Sponsor Manager (click performance), and any other add-on that wants to contribute content to the digest email.

When to Use This

Use these hooks when your add-on tracks metrics a podcaster would want to see alongside their subscriber numbers in a periodic report — donation totals, sponsor click-throughs, community sign-ups, affiliate revenue, anything episode- or audience-related.

The Hooks

Each hook has a shared variant that fires for all digest frequencies and a type-specific variant that fires for one frequency only. The shared filter fires first; the type-specific variant fires after it.

Hook Fires for Parameters
benecaster_analytics_digest_data All types $data, $type, $show_id, $period
benecaster_analytics_digest_sections All types $sections, $type, $show_id, $data

Each of those has three frequency-specific companions, named by appending _daily, _weekly, or _monthly. They carry one fewer argument — $type is dropped, because the hook name already says it — so a daily data callback receives $data, $show_id, $period.

Reach for a frequency-specific hook when your callback only ever applies to one cadence, and the shared one when a single callback handles several and needs to branch. Both linked pages spell out their own variants.

Prerequisites

  • Benecaster installed with a valid license

  • Analytics Dashboard add-on active

  • Your plugin or add-on loaded before the digest cron runs

How It Works

It is always two steps, in order.

Step 1 — inject data into the payload. Hook the data filter for the frequency you’re targeting, guard on the add-on being active, run your query using the supplied show ID and period, and assign the result into the data array under a prefixed key.

Step 2 — register a section that renders it. Hook the matching sections filter. The data array arriving here already contains the key you set in step 1. Bail without adding anything when that key is empty, then append a section definition with id, title, data, and template (an absolute path to a PHP file in your add-on).

Using the Shared Hook for Multiple Frequencies

If your add-on contributes to more than one digest frequency and the query or section logic differs between them, use the shared filters and branch on the type argument — one callback each instead of one per frequency. The shared filters take four parameters where the type-specific ones take three.

Notes

The guard is required. Every callback on an add-on-specific hook must call benecaster_addon_is_active( 'analytics-dashboard', $show_id ) before doing anything. This ensures your code degrades cleanly if the Analytics Dashboard add-on is deactivated later. The same guard pattern applies everywhere in Benecaster.

Section template. The template file receives the full section as a $section variable — an associative array with id, title, data, and template keys. Read $section['data'] for the payload you injected in step 1. Output HTML directly from the template; the digest builder includes it in the email body at the correct position.

Section ID prefix. Built-in section IDs are reserved: subscriber_snapshot, new_and_lost, episode_pulse, feed_engagement, benchmark_comparison, notable_callout. Prefix custom IDs with your add-on slug (my_addon_top_episodes, not top_episodes) to avoid collision with Benecaster and third-party add-ons.

Empty check before adding. If your data query returns nothing meaningful, don’t add the section. A digest that ends up with no sections after filtering is suppressed entirely — an empty-looking section header doesn’t help anyone and won’t prevent suppression anyway.

Monthly wins on overlap. If the podcaster’s monthly and weekly digest fall on the same calendar day, monthly is sent and weekly is skipped — it is not deferred. If your section only appears in the weekly template, it won’t appear on that day. Where meaningful, add a corresponding monthly section that summarizes the same data over the longer period rather than relying on the weekly to fill the gap.

Period date format, and the timezone trap underneath it. The period array’s start and end are ISO 8601 date strings (YYYY-MM-DD), not datetimes. The recipe appends 00:00:00 and 23:59:59 to build full datetimes for its comparison — adjust for your own column format.

Widening a date into a day is where timezones bite. A day boundary is only meaningful in some timezone, and if the one you widen into is not the one your timestamps are stored in, every digest quietly attributes several hours of activity to the wrong day — a site nine hours off UTC misfiles nine hours of it, every period, with nothing in the output that looks wrong. Totals across a long enough span still reconcile, which is exactly what makes it easy to miss.

Confirm which timezone your own timestamp column is written in and convert explicitly rather than assuming the two agree.

Where the donation figures come from. The example reads the add-on’s own mirror of donation activity, kept up to date from benecaster_listener_support_donation_logged, which fires as each donation is recorded. Mirror what you need into your own storage rather than querying Benecaster’s — our storage layout is internal and can change in any release.

Related

Code

<?php
add_filter(
    'benecaster_analytics_digest_data',
    function ( array $data, string $type, int $show_id, array $period ): array {
        if ( ! benecaster_addon_is_active( 'listener-support', $show_id ) ) {
            return $data;
        }
        global $wpdb;

        // my_addon_donations is the add-on's own mirror, written from
        // benecaster_listener_support_donation_logged.
        $total = (float) $wpdb->get_var( $wpdb->prepare(
            "SELECT COALESCE( SUM( amount ), 0 )
               FROM {$wpdb->prefix}my_addon_donations
              WHERE show_id = %d AND donated_at BETWEEN %s AND %s",
            $show_id,
            $period['start'] . ' 00:00:00',
            $period['end'] . ' 23:59:59'
        ) );
        $data['listener_support_totals'] = [
            'amount'   => $total,
            'currency' => 'USD',
        ];
        return $data;
    },
    10,
    4
);

add_filter(
    'benecaster_analytics_digest_sections',
    function ( array $sections, string $type, int $show_id, array $data ): array {
        if ( ! benecaster_addon_is_active( 'listener-support', $show_id ) ) {
            return $sections;
        }
        $totals = $data['listener_support_totals'] ?? null;
        if ( ! is_array( $totals ) || ( (float) $totals['amount'] ) <= 0.0 ) {
            return $sections; // Skip the section when there is nothing to report.
        }
        $sections[] = [
            'id'       => 'listener_support_totals',
            'title'    => esc_html__( 'Listener support', 'benecaster' ),
            'data'     => $totals,
            'template' => plugin_dir_path( __FILE__ ) . 'templates/digest-listener-support.php',
        ];
        return $sections;
    },
    10,
    4
);

View on GitHub →

Hooks Used

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