Reports
The reporting system in Burst Statistics allows you to create, schedule, and send analytics reports via email. Reports can be sent on a daily, weekly, or monthly schedule, and support two formats: a classic email with data tables, and a "story" format with a link to a shareable dashboard.
Overview
Reports are stored in the {prefix}burst_reports database table. Each report has a set of content blocks, a list of email recipients, a frequency, and a delivery schedule. When a scheduled report is due, it is dispatched via WP-Cron using a batched email queue, with delivery status tracked in a separate {prefix}burst_report_logs table.
Pro
The full reporting system (scheduled reports, multiple formats, content blocks, delivery logs) is a Pro feature.
Database Tables
{prefix}burst_reports
Stores report configuration.
| Column | Type | Description |
|---|---|---|
ID | int unsigned | Auto-incremented primary key |
name | varchar(255) | Report display name |
date_range | varchar(255) | The date range used for report data |
format | varchar(32) | Report format (classic or story) |
frequency | varchar(16) | Send frequency (daily, weekly, monthly) |
fixed_end_date | varchar(16) | Fixed end date (Y-m-d) used as the data range end |
day_of_week | varchar(16) | Day of week for weekly/monthly reports (e.g. monday) |
week_of_month | int | Occurrence of the weekday in the month (1–4, or -1 for last) |
send_time | varchar(16) | Time to send the report in HH:MM format (site timezone) |
last_edit | int unsigned | Unix timestamp of last modification |
enabled | tinyint(1) | Whether the report is active |
scheduled | tinyint(1) | Whether the report is sent automatically on schedule |
content | longtext | JSON-encoded array of content block definitions |
recipients | longtext | JSON-encoded array of recipient email addresses |
{prefix}burst_report_logs
Tracks report delivery history.
| Column | Type | Description |
|---|---|---|
ID | bigint unsigned | Auto-incremented primary key |
report_id | bigint unsigned | Associated report ID |
queue_id | varchar(32) | Date-based queue identifier (Y-m-d, or test-Y-m-d-{timestamp} for test sends) |
batch_id | int unsigned | Batch number (null for parent/queue-level log entries) |
status | varchar(32) | Delivery status string |
message | text | Human-readable status message |
time | int unsigned | Unix timestamp of the log entry |
date | varchar(10) | Date string extracted from queue ID (Y-m-d) |
Report Formats
The format field on a report controls how the email is constructed.
| Value | Description |
|---|---|
classic | Full HTML email with data tables for each content block |
story | Short teaser email with a button linking to a shareable story dashboard |
Report Frequency
The frequency field controls how often a scheduled report is sent.
| Value | Description |
|---|---|
daily | Sent every day at the configured send_time |
weekly | Sent on the configured day_of_week each week |
monthly | Sent on the nth occurrence of day_of_week within each month, determined by week_of_month |
For monthly reports, week_of_month accepts 1–4 for the first through fourth occurrence of the weekday, or -1 for the last occurrence.
Report Date Ranges
The date_range (or reportDateRange) field on a report defines which period the report data covers.
Accepted values are defined by the Report_Date_Range domain type. The default when creating a report from onboarding is last_week.
Content Blocks
Each report contains a content array of block definitions. For the classic format, these blocks map to data table sections rendered in the email.
Block objects stored in the content field have the following structure:
| Property | Type | Description |
|---|---|---|
id | string | Block identifier (must be a valid Report_Content_Block ID) |
filters | array | Query filters applied to this block's data |
content | string | Optional custom text content |
date_range | string | Optional per-block date range override |
date_range_enabled | bool | Whether the per-block date range override is active |
fixed_end_date | string | Per-block fixed end date (Y-m-d) |
comment_title | string | Optional comment block title |
comment_text | string | Optional comment block body text |
The compare block (identified by Report_Content_Block::COMPARE) is handled specially: it renders a summary table comparing pageviews, sessions, visitors, and bounce rate against the previous period.
Scheduling
The burst_every_hour cron hook triggers Reports::maybe_send_report(), which checks all enabled scheduled reports. A report is eligible to send when the current time falls within the report's scheduled send window for the day (within a 24-hour grace window after the scheduled timestamp). Eligible reports are dispatched via wp_schedule_single_event using the burst_send_email_batch hook, scheduled 5 minutes in the future.
Actual email sending happens in Reports::handle_email_batch(), which processes one batch of up to 10 recipients per cron run. If a report has more recipients than the batch size, additional batches are automatically scheduled 5 minutes apart.
REST API Endpoints
GET /wp-json/burst/v1/reports
Returns all reports, ordered by last edit date. Requires manage-level access and a valid burst_nonce nonce parameter.
Response:
{
"request_success": true,
"data": {
"reports": [ { ... } ]
}
}
POST /wp-json/burst/v1/do_action/report/{action}
Performs a write action on a report. Requires admin access. The {action} segment maps to one of the following actions (prefixed with report- internally):
| Action path | Description |
|---|---|
create | Create a new report |
delete | Delete an existing report |
update | Update an existing report |
send-test-report | Send a test email immediately |
send-report-now | Send the report immediately and schedule the queue |
preview | Get a preview HTML render of the report |
GET /wp-json/burst/v1/report/logs
Returns aggregated delivery logs for the past 30 days. Requires manage-level access and a valid burst_nonce nonce parameter.
Response:
{
"request_success": true,
"data": {
"logs": [ { ... } ]
}
}
Delivery Status Values
The following status values appear in the burst_report_logs table and are returned in the lastSendStatus field of a report object.
| Status | Description |
|---|---|
processing | Queue has been created and email sending is scheduled |
sending_successful | All recipients received the email |
sending_failed | All emails in the batch failed to send |
partly_sent | Some emails were sent, some failed |
cron_miss | The scheduled cron event did not fire within the expected window |
scheduled | Report is enabled and scheduled but has not sent yet |
ready_to_share | Report is not scheduled; the shareable story link is ready |
concept | Report is scheduled but currently disabled |
Changed in v3.3.0: The status value ready_to_send has been replaced by ready_to_share. The condition that triggers this status has also changed: previously it was returned only for reports with format = story; it is now returned for any report where scheduled = false, regardless of format.
Any consumer of the reports REST response that checks for "lastSendStatus": "ready_to_send" must be updated to check for "ready_to_share" instead.
Hooks and Filters
burst_email_blocks
Filters the list of available content blocks used to populate the classic email report.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$blocks | array | Array of block definitions loaded from includes/Admin/Mailer/config/blocks.php |
Example:
add_filter( 'burst_email_blocks', function( array $blocks ): array {
// Add a custom block definition.
$blocks['my_custom_block'] = [
'title' => __( 'Top Custom Metric', 'my-plugin' ),
'header' => [ __( 'Label', 'my-plugin' ), __( 'Value', 'my-plugin' ) ],
'url' => '#/statistics',
'query_args' => [
'metrics' => [ 'my_metric' ],
'type' => 'my_type',
],
];
return $blocks;
} );
burst_mail_report_limit
Filters the maximum number of rows returned per content block in a classic email report.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$limit | int | Number of rows. Default: 5 |
Example:
add_filter( 'burst_mail_report_limit', function( int $limit ): int {
return 10;
} );
burst_mail_report_results
Filters the raw query results for a content block before they are processed into the email table.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$raw_results | array | Array of result rows from the statistics query |
$qd | Query_Data | The query data object used for the block |
$start_date | int | Unix timestamp of the report period start |
$end_date | int | Unix timestamp of the report period end |
Example:
add_filter( 'burst_mail_report_results', function( array $results, $qd, int $start, int $end ): array {
// Remove rows where the first column is empty.
return array_filter( $results, fn( $row ) => ! empty( reset( $row ) ) );
}, 10, 4 );
burst_mail_reports_blocks
Filters the fully built blocks array before it is passed to the Mailer for rendering in a classic report. This runs after each block's data has been fetched and formatted.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$blocks | array | Associative array of built block data keyed by block ID |
$date_start | int | Unix timestamp of the report period start |
$date_end | int | Unix timestamp of the report period end |
Example:
add_filter( 'burst_mail_reports_blocks', function( array $blocks, int $start, int $end ): array {
// Remove the compare block from all reports.
unset( $blocks['compare'] );
return $blocks;
}, 10, 3 );
burst_email_template
Filters the path to the main HTML email template file.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$path | string | Absolute path to the email template file |
Example:
add_filter( 'burst_email_template', function( string $path ): string {
return get_stylesheet_directory() . '/burst/email.html';
} );
burst_email_block_template
Filters the path to the HTML template used to render each content block within the email.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$path | string | Absolute path to the block template file |
Example:
add_filter( 'burst_email_block_template', function( string $path ): string {
return get_stylesheet_directory() . '/burst/block.html';
} );
burst_email_readmore_template
Filters the path to the HTML template used to render the "read more" section at the bottom of the email.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$path | string | Absolute path to the read-more template file |
Example:
add_filter( 'burst_email_readmore_template', function( string $path ): string {
return get_stylesheet_directory() . '/burst/read-more.html';
} );
burst_report_log_delete_threshold
Filters how many days of report delivery logs are retained before old entries are purged.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$days | int | Number of days to retain logs. Default: 30 |
Example:
add_filter( 'burst_report_log_delete_threshold', function( int $days ): int {
return 90;
} );
burst_create_report_from_onboarding (action)
Fired during onboarding to automatically create a default weekly summary report for a given email address.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$email | string | The recipient email address for the onboarding report |
The default report created by this action has the following settings:
| Setting | Default value |
|---|---|
| Name | "Weekly Summary" |
| Format | classic |
| Frequency | weekly |
| Day of week | monday |
| Send time | 09:00 |
| Date range | last_week |
| Enabled | true |
| Scheduled | true |
burst_send_email_batch (action)
WP-Cron hook that fires to process a single batch of emails for a report queue.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$report_id | int | The report ID |
$queue_id | string | The queue identifier (date string or test queue ID) |
$batch_id | int|null | The batch number (1-based) |
Do not call or schedule burst_send_email_batch directly. Use the report-send-report-now REST action or the admin UI instead. Manually scheduling duplicate events for the same [$report_id, $queue_id, $batch_id] combination is a no-op because the system checks for existing scheduled events and already-processed queue entries before sending.
Email Template Variables
The main email template (email.html) supports the following placeholder tokens:
| Token | Description |
|---|---|
{base} | HTML <base> tag pointing to the site home URL |
{title} | Email heading (HTML allowed) |
{logo} | Light-mode logo image URL |
{logo_dark} | Dark-mode logo image URL |
{message} | Introductory message text (e.g. date range coverage) |
{blocks} | Rendered HTML for all content blocks |
{read_more} | Rendered HTML for the "read more" section |
{sent_by_text} | Footer unsubscribe / sender attribution text |
{domain} | Site home URL |
Each block within the block.html template supports:
| Token | Description |
|---|---|
{title} | Block heading |
{subtitle} | Block subheading (e.g. "vs. previous week") |
{table} | Rendered HTML table rows |
{url} | "Learn more" link URL |
{learn-more} | "Learn more" button text |
The read-more.html template supports:
| Token | Description |
|---|---|
{title} | Section heading |
{message} | Teaser text |
{read_more_url} | Call-to-action button URL |
{read_more_text} | Call-to-action button label |
Report Object Fields (API)
The to_array() method on a Report instance returns the following fields, as used in REST API responses.
Changed in v3.3.0: The Report constructor now accepts a second parameter bool $is_shared_link = false. When true, scheduling and recipient fields are replaced with safe defaults in the returned array, and if the report is disabled to_array() returns an empty array [] instead.
// Instantiate a report for a shared-link context (omits scheduling/recipient data).
$report = new Report( $report_id, true );
$data = $report->to_array(); // Returns [] if report is disabled.
// Standard instantiation (full data, used in admin/REST contexts).
$report = new Report( $report_id );
$data = $report->to_array();
| Field | Type | Description |
|---|---|---|
id | int|null | Report ID |
name | string | Report name |
format | string | Report format (classic or story) |
enabled | bool | Whether the report is active |
content | array | Array of content block definitions |
reportDateRange | string | Date range identifier |
fixedEndDate | string | Fixed end date (Y-m-d) |
frequency | string | Report frequency (omitted for shared-link requests; defaults to Report_Frequency::DEFAULT) |
weekOfMonth | int|null | Week of month for monthly reports (omitted for shared-link requests) |
dayOfWeek | string|null | Day of week for weekly/monthly reports (omitted for shared-link requests) |
sendTime | string | Scheduled send time (HH:MM) (omitted for shared-link requests) |
lastEdit | int | Unix timestamp of last modification (omitted for shared-link requests) |
scheduled | bool | Whether the report sends automatically (omitted for shared-link requests) |
recipients | array | Array of recipient email addresses (omitted for shared-link requests) |
lastSendStatus | string | Status string from the most recent delivery log (omitted for shared-link requests) |
lastSendMessage | string | Human-readable message for lastSendStatus (omitted for shared-link requests) |
Changed in v3.3.0: For shared-link requests (
$is_shared_link = true), the fields marked above are replaced with empty/default values and are not populated from the database. If the report'senabledflag isfalse,to_array()returns[].
Email Delivery Details
- Emails are sent using WordPress's
wp_mail()function withContent-Type: text/html; charset=UTF-8. - Before sending, the recipient domain is validated via DNS MX/A record lookup. Emails to domains with no valid DNS records are skipped and logged as failures.
- Recipients are processed in batches of 10 (default). Each batch is scheduled as a separate
burst_send_email_batchcron event, 5 minutes apart. - Duplicate batch processing is prevented: the system checks both existing cron events and the
burst_report_logstable before scheduling or sending. - The
fixed_end_dateon a scheduled report is automatically updated to yesterday each time the report is sent, anchoring the data range end date. - Test reports (sent via the "Send test" button) are delivered immediately without scheduling a cron event, and their queue IDs use the format
test-Y-m-d-{timestamp}to distinguish them from scheduled sends in the logs.
Report delivery depends on WP-Cron. On sites with infrequent traffic or disabled WP-Cron, scheduled reports may be delayed or missed entirely. Missed sends are recorded in the logs with the cron_miss status. Consider configuring a real system cron job to trigger wp-cron.php on a regular interval.