Data sharing
Burst Statistics v3.3.0 includes an anonymous usage data collection system (telemetry) that sends aggregated, non-personal statistics to the Burst Statistics team once per month. This data helps improve the plugin.
No personally identifiable information is collected. All data is anonymised at the point of collection using a one-way site hash.
Overview
The data sharing system is made up of two main components:
| Class | Responsibility |
|---|---|
Burst\Admin\Data_Sharing\Data_Sharing | Schedules and triggers the monthly telemetry send |
Burst\Admin\Data_Sharing\Data_Aggregation | Collects, aggregates, and dispatches the payload |
Data is collected by individual data collectors (one per category) and combined into a single JSON payload sent via HTTP POST to:
https://api.burst-statistics.com/v2/telemetry
Telemetry is never sent from staging environments. See Staging detection for the full list of detected patterns.
Scheduling
The send is triggered by the burst_monthly WordPress cron action. When that fires, a single-event cron job (burst_telemetry_send) is scheduled with a random offset between 24 and 720 hours. This spreads load across the month rather than all sites sending simultaneously.
The random offset is stored persistently in the burst_telemetry_offset option so the same offset is reused if WordPress cron runs multiple times before the event fires.
Relevant WordPress options:
| Option | Description |
|---|---|
burst_telemetry_offset | Random hour offset (24–720) used to stagger the send time |
burst_last_telemetry_send | Unix timestamp of the most recent successful send |
Data collected
Each category is collected by a dedicated Data_Collector subclass. The aggregated payload always includes:
| Field | Type | Description |
|---|---|---|
site_hash | string | SHA-256 hash of site_url + AUTH_KEY. Identifies the site without transmitting the URL. |
is_test | bool | true only during test sends. Test payloads are validated but not stored by the API. |
settings | object | Active plugin settings |
goals | object | Configured conversion goals |
environment | object | WordPress version, PHP version, and similar environment metadata |
email_reports | object | Email report configuration (not personal email addresses) |
metrics | object | Aggregated pageview and event counts for the reporting period |
Database metrics
Database table row counts are collected by Burst\Admin\Data_Sharing\Data_Collectors\Metrics\Database_Metrics. The collector uses the Database_Helper trait and validates each table suffix through validate_table_name() before issuing a COUNT(*) query.
Changed in v3.4.0: Database_Metrics now uses validate_table_name() (via the Database_Helper trait) instead of esc_sql() for table-name sanitisation. Suffixes that are not in the trait's allow-list are rejected before the query runs.
Query performance metrics
Slow query telemetry is collected by Burst\Admin\Data_Sharing\Data_Collectors\Metrics\Query_Stats_Metrics. The collector returns up to the top 10 entries from {$wpdb->prefix}burst_query_stats, ordered by avg_execution_time DESC, including a per-row date_range_days value indicating the date span of the originating query.
Returned fields per query row (v3.4.0+):
| Field | Type | Description |
|---|---|---|
sql_query | string | The recorded SQL query |
avg_execution_time | float | Average execution time in seconds |
max_execution_time | float | Slowest recorded execution time in seconds |
min_execution_time | float | Fastest recorded execution time in seconds |
execution_count | int | Number of times the query has been executed |
date_range_days | int | Date span (in days) of the query that produced the entry. 0 when no date range is associated |
Report logs
Report send statistics are collected by Burst\Admin\Data_Sharing\Data_Collectors\Reports_Data and reported under the email_reports payload. The collector aggregates rows from {$wpdb->prefix}burst_report_logs by queue_id, resolves a single final status per queue, and counts each queue once.
Changed in v3.4.0: Reports_Data has been rewritten to aggregate report logs by queue_id instead of issuing three separate COUNT(*) queries. Each queue is now classified by combining its parent row (batch_id IS NULL) with its child batch rows, and the resolved final status determines whether it counts toward successful_sends, failed_sends, or neither.
Status resolution rules:
- If a parent status exists and is not
PROCESSING, that status wins. - Otherwise, if any child batch is
PARTLY_SENT, the queue isPARTLY_SENT. - Otherwise, if all child batches share a single status, that status wins.
- Otherwise, the queue is
SENDING_FAILED.
Queues whose final status is PROCESSING (or unresolved) are skipped entirely. Queues with queue_id matching test-% are excluded from the aggregation.
A queue is counted as successful only when its final status is SENDING_SUCCESSFUL. The new is_failed_status() helper counts a queue as failed when its final status is one of:
SENDING_FAILEDEMAIL_DOMAIN_ERROREMAIL_ADDRESS_ERRORCRON_MISSPARTLY_SENT
The returned payload shape is unchanged:
| Field | Type | Description |
|---|---|---|
reports_sent_last_month | int | Number of distinct queues resolved to a final non-processing status in the reporting window |
successful_sends | int | Queues whose final status is SENDING_SUCCESSFUL |
failed_sends | int | Queues whose final status is counted as failed by is_failed_status() |
Anonymisation
The site identifier sent in every payload is a one-way SHA-256 hash:
hash( 'sha256', get_site_url() . AUTH_KEY );
The raw site URL is never transmitted. Two sites with the same URL but different AUTH_KEY values produce different hashes.
Kill switch
Before each send, Burst fetches a plain-text control file from:
https://burst.ams3.cdn.digitaloceanspaces.com/feedback/switch.txt
The response body controls whether the send proceeds:
| Response body | Behaviour |
|---|---|
enabled | Send proceeds normally |
disabled | Send is skipped for all sites |
0–100 (integer) | Send proceeds for that percentage of sites (random per-site roll) |
| Any other value / unreachable | Send proceeds (fail-open) |
If the kill switch disables a send, burst_last_telemetry_send is still updated so the site does not retry on the next cron cycle.
Staging detection
No data is sent when the site is identified as a non-production environment. The check uses wp_get_environment_type() first, then inspects the site URL for the following patterns:
| Pattern type | Examples |
|---|---|
Environment type not production | staging, development, local (WP core values) |
| Localhost | localhost, localhost/… |
| Local TLD | *.local |
| Staging subdomain prefix | staging.*, stg.*, test.*, beta.*, acceptance.* |
| Staging subdomain infix | *.dev.*, *.test.*, *.stg.* |
| Managed staging hosts | *.instawp.co |
Caching
Aggregated data is cached in the burst_aggregated_data_v2 transient for one week. This reduces database load on repeated API calls. The cache is bypassed when wp_get_environment_type() is not production.
Changed in v3.4.0: The transient key has been bumped from burst_aggregated_data to burst_aggregated_data_v2 to invalidate stale payloads cached under the previous schema. The old transient is no longer read or written — sites upgrading from v3.3.0 will simply rebuild the cache on the next aggregation cycle.
To clear the cache programmatically:
Show code
$aggregation = new \Burst\Admin\Data_Sharing\Data_Aggregation(
strtotime( '-1 month' ),
time()
);
$aggregation->clear_cache();
API request format
Payloads are sent as JSON POST requests with these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
Accept | application/json |
HTTP_X_BURST_SIGNATURE | Value of the BURST_PUBLIC_KEY constant |
WordPress cron hooks
burst_monthly
Fires once per month via Burst's internal cron management. The data sharing system uses this action to schedule the deferred telemetry send via Data_Sharing::schedule_telemetry().
This action is not exclusive to data sharing — other subsystems also hook into it.
burst_telemetry_send
Single-event cron hook that performs the actual data collection and API send. Scheduled by Data_Sharing::schedule_telemetry() with a random offset after burst_monthly fires.
Parameters: none.
Example — observing the send:
add_action( 'burst_telemetry_send', function() {
// Executes immediately before Burst sends telemetry data.
}, 1 );
Removing burst_telemetry_send prevents the send only for the currently scheduled event. The next burst_monthly cycle will reschedule it.
Data collector filters
burst_data_sharing_license_status
Filters the license-status value included in the telemetry settings payload.
Default value: free.
burst_data_sharing_subscription_tier
Filters the subscription-tier string included in the telemetry settings payload.
Default value: free.
burst_data_sharing_ecommerce_metrics
Filters the ecommerce metrics payload before it is attached to telemetry.
This hook receives the Ecommerce_Metrics collector instance as the second parameter. Return null to omit ecommerce metrics entirely, or return an array payload to include custom ecommerce telemetry.
Example:
Show code
add_filter( 'burst_data_sharing_ecommerce_metrics', function( $metrics, $collector ) {
if ( ! my_plugin_should_share_ecommerce_metrics() ) {
return null;
}
return $collector->collect();
}, 10, 2 );
Disabling data sharing
Data sharing is suppressed automatically on staging and local environments (see Staging detection).
To disable it unconditionally on a production site, prevent the cron hook from running before Burst's own callback:
Show code
// In a must-use plugin or your theme's functions.php.
add_action( 'burst_telemetry_send', function() {
remove_action(
'burst_telemetry_send',
[ burst()->data_sharing, 'send_monthly_telemetry' ]
);
}, 0 );
To prevent the event from ever being scheduled, intercept burst_monthly early:
Show code
add_action( 'burst_monthly', function() {
remove_action(
'burst_monthly',
[ burst()->data_sharing, 'schedule_telemetry' ]
);
}, 1 );
Test sends
Data_Sharing::send_test_telemetry() dispatches a payload with is_test: true. The API validates the payload structure but does not persist the data. An optional custom endpoint can be passed for integration testing.
Show code
$data_sharing = new \Burst\Admin\Data_Sharing\Data_Sharing();
$result = $data_sharing->send_test_telemetry();
if ( $result['success'] ) {
echo 'Accepted. HTTP ' . $result['status_code'];
} else {
echo 'Failed: ' . $result['message'];
}
Return value:
| Key | Type | Description |
|---|---|---|
success | bool | true if the API returned a 2xx status code |
status_code | int | HTTP response code |
message | string | Human-readable status from the API response body |
data | array|null | Full parsed JSON response body |
endpoint | string | URL the request was sent to |
Share link capabilities
Capability model for shared-link viewers
When a visitor arrives via a share link, Burst assigns the burst_viewer role and grants the view_burst_statistics capability. This allows the visitor to access general statistics pages that were included in the shared link's shared_tabs array.
Allowed capabilities for shared-link visitors (v3.3.0+):
Show code
$allowed_caps = [
'view_burst_statistics',
'burst_viewer',
];
Ecommerce tab visibility on shared links
Pro - BusinessAvailable in the Business tier
Ecommerce tab sharing requires Burst Pro. See Revenue & Sales Tracking.
Access to the ecommerce (Sales) tab on a shared link is now controlled exclusively by the ecommerce_tab_is_shared() method on Burst\Admin\Share\Share. It inspects the shared_tabs array stored on the token and returns true only when 'sales' is present.
// Resolved automatically via burst_loader()->admin->share.
$share = burst_loader()->admin->share;
$can_view_sales = $share->ecommerce_tab_is_shared();
Internally, user_can_view_sales() (from the Admin_Helper trait) delegates to this method whenever is_shared_link_request() returns true:
Show code
// From trait-admin-helper.php — simplified illustration.
if ( $this->is_shared_link_request() ) {
return burst_loader()->admin->share->ecommerce_tab_is_shared();
}
is_shared_link_request()
Added in v3.3.0. A protected helper available to any class using the Admin_Helper trait. Returns true when the current request carries either of the two share-token identifiers.
Show code
protected function is_shared_link_request(): bool {
return isset( $_SERVER['HTTP_X_BURST_SHARE_TOKEN'] )
|| isset( $_GET['burst_share_token'] );
}
This helper is used as a guard inside user_can_view_sales() and can be called from any class that uses Admin_Helper to branch logic for anonymous shared-link visitors versus authenticated users.