Skip to main content

Goals reference

Goals let you measure conversions in Burst Statistics. Each goal tracks a specific user action — a click, a page visit, or a WordPress hook execution — and records how often it occurs relative to your chosen conversion metric.


Overview​

A goal is stored as a row in {prefix}_burst_goals. When a visitor completes a goal, a record is written to {prefix}_burst_goal_statistics linking that pageview to the goal. Goals are represented by the Burst\Frontend\Goals\Goal class and managed in bulk via Burst\Frontend\Goals\Goals. Burst supports two processing paths:

  • Client-side — the browser tracking script detects the conversion (click or element view) and includes it in the tracking payload.
  • Server-side — PHP detects the conversion (a WordPress hook fires or a page is visited) and writes the statistic directly.

Goals can be created from the goals admin UI, from integration templates, directly from the block editor, or from the Elementor editor. See Block editor goals and Elementor goals.

Pro - CreatorAvailable in the Creator tier

The Pro version allows unlimited goals. The free version permits up to three active goals at a time (Goal::LIMIT_FREE). Once three active goals exist, Goal::save() returns false for any new insert, and activating a fourth existing goal also fails. Learn more about goal conversion tracking


Goal types​

Goal types are registered through the field definitions and can be extended with the burst_goal_types filter.

TypeTracking pathserver_sideDescription
clicksClient-sidefalseFires when a visitor clicks an element matching the configured CSS selector.
viewsClient-sidefalseFires when a visitor views an element matching the configured CSS selector.
visitsServer-sidetrueFires when a visitor lands on the target page, matched by post ID or by URL.
hookServer-sidetrueFires when a specified WordPress action hook executes.

server_side is derived automatically in Goal::get():

$this->server_side = $this->type === 'hook' || $this->type === 'visits';

Configuration fields​

These fields map directly to the goal editor in the admin UI.

title​

FieldValue
Typehidden (set programmatically)
Defaultfalse (displays as "New goal" when empty)

The human-readable name shown in the goals list.


status​

FieldValue
Typehidden (toggled via the goal editor UI)
Defaultfalse (inactive)
Accepted valuesactive, inactive

Controls whether the goal is currently collecting conversions. Setting status to active resets date_start to the current time.


type​

FieldValue
Typeradio-buttons
Defaultclicks
Accepted valuesclicks, views, visits, hook

The type of event to track. See Goal types for details on each option.


page_or_website​

FieldValue
Typeradio-buttons
Defaultwebsite
Accepted valuespage, website
Shown forclicks, views, hook goal types

Determines whether the goal applies to the entire site or only to a specific page.

OptionDescription
pageTrack only when the conversion happens on a specific page
websiteTrack the conversion anywhere on the site

When website is selected, the stored url value is set to *.


specific_page​

FieldValue
Typeselect-page
Defaultfalse
Shown forvisits type, or when page_or_website is page

The specific page to track. Accepts a relative URL. When page_or_website is website, this field is ignored and url is stored as *.

For page-scoped goals Burst resolves the matching post ID and stores it in page_id, so tracking can match on the post ID even after the page URL changes. See page_id.

The list of selectable pages comes from the posts/get endpoint. See Select page picker for what appears in it and how it is queried.


selector​

FieldValue
Typeselector
Default'' (empty string)
Shown forclicks and views goal types

A CSS selector identifying the element to track. For example: #newsletter-form .submit-btn or .add-to-cart.

The value is sanitized with sanitize_text_field() before storage.


hook​

FieldValue
Typehook
Default'' (empty string)
Shown forhook goal type only

The WordPress action hook name to listen for. For example: woocommerce_payment_complete or my_plugin_form_submitted.

The value is sanitized with sanitize_text_field() before storage.

caution

Hook-type goals rely on the burst_uid cookie being set by a prior client-side pageview. If the hook fires before the visitor has been tracked (for example, during a server-side-only request), the conversion will not be recorded.


conversion_metric​

FieldValue
Typeradio-buttons
Defaultvisitors
Accepted valuesvisitors, sessions, pageviews

Determines the denominator used when calculating the conversion rate.

OptionDescription
visitorsConversions ÷ unique visitors
sessionsConversions ÷ total sessions
pageviewsConversions ÷ total pageviews

Select page picker​

The select-page field is populated by the burst/v1/posts/get endpoint, which returns the pages a page-scoped or visits goal can target.

Rows come from the {prefix}_burst_page_urls dictionary: one canonical row per post ID, joined to the published post in {$wpdb->posts} for its title. Any public post type with a canonical dictionary row is listed, so WooCommerce products and other plugin post types appear alongside posts and pages, each with the URL the tracker actually stores for it.

The endpoint never calls get_permalink() on the dictionary path. Burst's REST optimizer leaves plugin post types unregistered on burst/v1 requests, so get_permalink() would return the wrong URL for exactly those post types the dictionary makes available.

Until the dictionary has been seeded, the endpoint falls back to published posts and pages from {$wpdb->posts}. Only those two core post types are listed on that path, and because core post types stay registered on burst/v1 requests, get_permalink() resolves correctly there.

Search and result limits​

ParameterTypeDefaultDescription
noncestring—Required. Validated against the burst_nonce action.
searchstring''Sanitized with sanitize_text_field(). Matched against the page URL and the post title on the dictionary path; against the post title only on the fallback path.

A search term of more than three characters raises the result cap from 100 to 1000 rows. Results are ordered by post title, ascending.

Pageviews​

Each returned row carries a pageviews total. Burst resolves these for the listed pages only, through an indexed IN lookup on statistics.page_id executed with Statistics_Query — never a GROUP BY over the whole statistics table. Running it through the query executor keeps the timeout cap, result cache and single-flight protection in place.

Return value​

On success the endpoint responds 200 with:

Show code
[
'request_success' => true,
'posts' => [
[
'page_url' => '/shop/vintage-camera/', // path as stored by the tracker
'page_id' => 42,
'post_title' => 'Vintage camera',
'pageviews' => 128,
],
// …
],
'max_post_count' => 100,
]

On failure it returns a WP_Error: rest_forbidden (403) when the user lacks the view_burst_statistics capability, or rest_invalid_nonce (400) when the nonce is missing or expired.

Troubleshooting​

  • A custom post type does not appear in the picker. The dictionary only holds post types that have recorded traffic. Visit the post once so the tracker writes its canonical row, then reload the goal editor.
  • URLs in the picker differ from get_permalink() output. That is intended: the picker shows the URL the tracker stores, which is what visits goals match against.

Goal object​

The Burst\Frontend\Goals\Goal class represents a single goal. Properties are read/write via magic __get / __set.

PropertyTypeDefaultDescription
idint0Database primary key (ID column).
titlestring''Human-readable goal name. Defaults to "New goal" when empty.
typestring'clicks'Goal type slug. See Goal types.
statusstring'inactive''active' or 'inactive'.
server_sideboolfalseSet automatically to true when type is 'hook' or 'visits'. Derived at load time — not stored as a separate column.
urlstring'*'Raw URL stored in the database. '*' means site-wide.
page_or_websitestring'website'Derived from url. 'website' when url === '*'; 'page' otherwise.
specific_pagestring''Relative URL used when page_or_website === 'page'. Derived from url.
conversion_metricstring'visitors'How conversions are counted.
selectorstring''CSS selector for clicks and views goal types.
hookstring''WordPress action name for hook goal type.
block_goalint0Set to 1 when the goal was created from the block editor or the Elementor editor. Stored in the block_goal column.
page_id?intnullPost ID of the target page for page-scoped goals. Resolved from url on save. null for site-wide goals.
is_draftboolfalsetrue when page_id points to a post that is not published. Derived at load time — not stored as a separate column.
date_startint—Unix timestamp when the goal became active. Reset each time status changes to 'active', or on first creation.
date_endint0Unix timestamp when tracking ended. 0 means still running.
date_createdint0Unix timestamp of first insertion.

Deprecated properties​

caution

The following properties are deprecated and may be removed in a future release. Do not use them in new code.

PropertyTypeReplacement
attribute?stringUse selector instead
attribute_value?stringUse selector instead

Tracking lifecycle​

Client-side goals (clicks, views)​

  1. The visitor interacts with an element matching the goal's selector.
  2. The frontend goals script appends the goal ID to the completed_goals array in the tracking payload.
  3. The tracking endpoint receives the payload and records the conversion in {prefix}_burst_goal_statistics.

The tracking endpoint sanitizes the reported goal IDs with Tracking::sanitize_completed_goal_ids(): every value is cast to a positive integer, zero and duplicate IDs are dropped, and the total is capped at 50 IDs per hit. The endpoint is unauthenticated, so the cap bounds the work a single request can trigger — each reported ID becomes a row in one INSERT on {prefix}_burst_goal_statistics.

Server-side goals — hook type​

  1. During the WordPress init action, Goals_Tracker::add_dynamic_hooks() reads every active hook-type goal and registers a WordPress action for each goal's hook property.
  2. When that action fires, Goals_Tracker::handle_hook( $hook_name ) runs:
    • Reads the burst_uid cookie to identify the visitor.
    • If no burst_uid is present, the conversion is not recorded.
    • Retrieves the visitor's most recent statistic row via get_last_user_statistic().
    • If the goal is scoped to a specific page (page_or_website === 'page'), checks that specific_page appears in the stored page_url + parameters string.
    • Calls create_goal_statistic() to write the conversion.
Show code
// Trigger a custom hook-type goal.
// 1. In Burst, create a goal with type = 'hook' and hook = 'my_plugin_form_submitted'.
// 2. Fire the action from your plugin:
do_action( 'my_plugin_form_submitted' );
// Goals_Tracker picks up the action and records the conversion
// for the currently identified visitor.

Server-side goals — visits type​

Burst records a conversion when the tracked pageview matches the goal's target page. Matching is by post ID first: the tracking payload includes the page_id of the current page, and a visits goal stores a page_id for its target page — when both are present and equal, the conversion is recorded. When no page_id is available, Burst falls back to comparing the normalized page URL against the goal's stored url.

goal_is_completed() and get_completed_goals() receive the tracked page's post ID as an additional $page_id argument, which runs on both the REST tracking endpoint and the SHORTINIT beacon (where is_singular() / get_queried_object_id() are unavailable).


Goal conversion queries​

Internally, Goal_Statistics::get_goal_completed_count_sql() builds the SQL used to count goal completions. The query JOINs {prefix}_burst_sessions so that session-level attributes such as device_id are read from the sessions table rather than the statistics table.

device_id (and the related browser_id, platform_id, browser_version_id, first_time_visit, and bounce columns) live on {prefix}_burst_sessions, not {prefix}_burst_statistics. Custom SQL that queries goal conversions must JOIN {prefix}_burst_sessions and reference sessions.device_id rather than statistics.device_id.

get_goal_completed_count_sql() accepts a single goal ID (int) or a list of goal IDs (int[]). When passed a single ID, the count expression follows that goal's conversion_metric and the query constrains on goals.goal_id = %d. When passed an array of IDs, the conversions are counted as distinct visitors (COUNT(DISTINCT(statistics.uid_id))) and the query constrains on goals.goal_id IN (…).

The generated SQL for a single goal takes the following form:

Show code
// Simplified representation of the query produced by get_goal_completed_count_sql()
// for a single goal id.
global $wpdb;

$sql = $wpdb->prepare(
"SELECT COUNT(DISTINCT statistics.uid_id) AS value
FROM {$wpdb->prefix}burst_goal_statistics AS goals
INNER JOIN {$wpdb->prefix}burst_statistics AS statistics
ON statistics.ID = goals.statistic_id
INNER JOIN {$wpdb->prefix}burst_sessions AS sessions
ON statistics.session_id = sessions.ID
WHERE goals.goal_id = %d
AND statistics.time > %s",
$goal_id,
$date_start
);

Visitor identity lives in the integer statistics.uid_id column (a dictionary id into {prefix}_burst_uids); count distinct visitors on statistics.uid_id, not on a raw uid string.

Sessions without a pageview​

{prefix}_burst_sessions carries a has_pageview column, set to 1 as soon as a session records a hit whose page_type is not 404. A session made up of 404 hits only — typically a bot scan — keeps has_pageview = 0.

Session-level goal queries filter on sessions.has_pageview = 1, so those sessions never contribute to goal metrics. This mirrors the hit-level rule that excludes 404 hits from every pageview query, keeping session-grain and hit-grain numbers aligned. Custom SQL that aggregates goal or device data over {prefix}_burst_sessions should apply the same condition:

Show code
// Pageviews per device — sessions JOIN required since device_id lives on burst_sessions.
// has_pageview = 1 keeps 404-only bot sessions out of the result.
$sql = $wpdb->prepare(
"SELECT COUNT(statistics.uid_id) AS value, sessions.device_id
FROM {$wpdb->prefix}burst_statistics AS statistics
INNER JOIN {$wpdb->prefix}burst_sessions AS sessions
ON statistics.session_id = sessions.ID
WHERE statistics.time > %s
AND sessions.has_pageview = 1
GROUP BY sessions.device_id
ORDER BY value DESC
LIMIT 4",
$date_start
);

The device-breakdown query used to determine the best-performing device for a goal applies the same sessions.has_pageview = 1 condition.


Aggregated goal statistics​

Goal_Statistics::get_goals_data() and Goal_Statistics::get_live_goals_count() accept the special goal_id value 'all', which aggregates conversions across every active goal instead of reporting on a single goal.

When goal_id is 'all':

  • The set of active goal IDs is resolved from {prefix}_burst_goals WHERE status = 'active'. If no active goals exist, an empty/zeroed result is returned.
  • Conversions are counted as distinct visitors (COUNT(DISTINCT(statistics.uid_id))) across all active goals, regardless of each goal's individual conversion_metric.
  • date_start is the earliest date_created of the active goals, and no per-goal URL constraint is applied.
  • The returned status is 'active' and goalId is the string 'all'.
Show code
// Aggregated counts across all active goals.
$all = burst_loader()->admin->statistics->goal_statistics->get_goals_data( [
'goal_id' => 'all',
] );

// Aggregated live count (conversions since midnight) across all active goals.
$live = burst_loader()->admin->statistics->goal_statistics->get_live_goals_count( [
'goal_id' => 'all',
] );

The goal_id argument is sanitized in App::sanitize_arg(): the literal string 'all' is preserved, and any other value is coerced to a positive integer with absint().

Live goal counts and the site timezone​

get_live_goals_count() counts conversions since midnight in the site's timezone, as configured under Settings → General → Timezone in WordPress. The boundary is resolved with wp_timezone():

// Midnight in the site's timezone, not UTC midnight.
$today = ( new \DateTimeImmutable( 'today', wp_timezone() ) )->getTimestamp();

WordPress pins the PHP process to UTC, so a plain strtotime( 'today midnight' ) would start the day at UTC midnight and drop the early-morning conversions of sites east of Greenwich. Custom code that reproduces the live count must resolve midnight through wp_timezone() the same way.

get_goals_data return shape​

get_goals_data() returns the following structure. The goalId key is an int for a single goal and the string 'all' for the aggregated view.

Show code
[
'today' => [ 'value' => int, 'tooltip' => string ],
'total' => [ 'value' => int, 'tooltip' => string ],
'topPerformer' => [ 'title' => string, 'value' => int ],
'conversionMetric' => [ 'title' => string, 'value' => int, 'tooltip' => string, 'icon' => string ],
'conversionPercentage' => [ 'title' => string, 'value' => int, 'tooltip' => string ],
'bestDevice' => [ 'title' => string, 'value' => int, 'icon' => mixed ],
'dateCreated' => int,
'dateStart' => int,
'dateEnd' => int,
'status' => string,
'goalId' => int|string,
]

Database tables​

TablePurpose
{prefix}_burst_goalsOne row per goal; stores all goal configuration, including the block_goal and page_id columns.
{prefix}_burst_goal_statisticsMany-to-many join between statistic rows and goal IDs. All rows for a goal are deleted when the goal is deleted.
{prefix}_burst_page_urlsPage URL dictionary. Supplies the canonical URL and post ID pairs for the select page picker.

Tables are created via the burst_install_tables action. See burst_install_tables.

The block_goal (tinyint, default 0) and page_id (int, nullable) columns are added by database upgrades on existing installs. Goal::save() only writes these columns when they are already present in the schema, so goals still save on installs that have not yet run the upgrade.


Block editor goals​

Burst adds a "Track clicks with a Burst Goal" inspector panel to supported blocks in the block editor (available to users with the manage_burst_statistics capability). Enabling it on a block stores two block attributes — burstGoalActive and burstGoalUid — and creates or updates a goal through the goals/upsert_for_block endpoint.

On the frontend, the render_block filter injects a data-burst-goal="<uid>" attribute into the block's first HTML tag, and the goal's stored selector is [data-burst-goal="<uid>"]. Clicks on that element are then tracked as a clicks-type conversion. The uid is always the source of truth for the selector — the client cannot override it.

Block goals are marked with block_goal = 1 and Burst keeps them in sync with post content:

  • On save_post, goals whose page_id matches the post have their url rewritten to the post's current path. Any block goal whose uid no longer appears in the post content is retired through Goals::retire_goal_if_orphaned() (see retire_goal_if_orphaned). Elementor-managed posts are skipped here — they are synchronised from _elementor_data instead (see Elementor goals).
  • A daily cleanup (cleanup_orphaned_block_goals) removes or deactivates block goals whose uid is absent from all non-trash, non-revision post content and from Elementor's _elementor_data post meta, skipping goals created within the last 3 hours to avoid races while editing.

Clickable blocks​

Goals::get_clickable_blocks() returns the list of Gutenberg block names that support click goal tracking in the inspector panel. The list is also passed to the frontend tracking script as clickable_blocks.

$blocks = \Burst\Frontend\Goals\Goals::get_clickable_blocks();
// [ 'core/button', 'core/image', 'core/navigation-link' ]

Extend or restrict the list with the burst_clickable_blocks filter.

Block attribute registration​

Burst registers the burstGoal* block attributes in the PHP block registry through the register_block_type_args filter, so server-side rendered blocks (for example wpforms/form-selector and WooCommerce blocks) and the /wp/v2/block-renderer endpoint validate them instead of throwing Invalid parameter(s): attributes errors.

The registered schema properties are:

AttributeType
burstGoalUidstring
burstGoalActiveboolean
burstGoalIdnumber
burstGoalTypestring

No defaults are declared: only the schema properties matter for validation, and a default would inject these keys into every dynamic block on every render. The block editor script supplies its own client-side defaults. The core/freeform block is skipped.

Goal::get_by_uid​

Look up a block goal by its uid. Returns a Goal instance for the row whose selector equals [data-burst-goal="<uid>"], or null when none matches.

Show code
$goal = \Burst\Frontend\Goals\Goal::get_by_uid( 'abc123' );
if ( $goal !== null ) {
echo $goal->id;
}

Goals::retire_goal_if_orphaned​

Retire a block or element goal whose uid is no longer present in any content: the goal is deleted when it has no statistics, or set to inactive when it does so existing statistics are preserved. This is the single source of truth shared by the block editor save_post cleanup, the daily cleanup_orphaned_block_goals sweep, and the Elementor sync.

// Delete or deactivate an orphaned block/element goal.
$retired = \Burst\Frontend\Goals\Goals::retire_goal_if_orphaned( 42 );
// Returns true when the goal was deleted or deactivated, false otherwise.

Elementor goals​

Burst adds a "Burst Goal" controls section to the Advanced tab of Elementor widgets, sections, containers and columns (available to users with the manage_burst_statistics capability). Elementor goals are stored the same way as block editor goals — marked with block_goal = 1, with a selector of [data-burst-goal="<uid>"] — and share the same retirement logic through Goals::retire_goal_if_orphaned().

On the frontend, the elementor/frontend/before_render action injects a data-burst-goal="<uid>" attribute into the element wrapper, and clicks on (or views of) that element are tracked accordingly. Goals are synchronised to the database when an Elementor document is saved, via both the elementor/editor/after_save action and _elementor_data post-meta updates, so imports and direct meta writes stay in sync.

Controls​

ControlDescription
Track with a Burst GoalToggle that enables goal tracking for the element
Goal trigger typeclicks or views for widgets; views only for sections, containers and columns
Goal titleHuman-readable name; auto-generated from the element type and trigger when left empty
Conversion metricvisitors, sessions or pageviews
Page scopepage (current page) or website (all pages)

When a goal is copied to another post (for example via Duplicate Post, WPML or an importer), the sync generates a fresh uid for the new post's goal rather than reassigning the original.

Clickable widgets​

Elementor::get_clickable_widgets() returns the Elementor widget types considered clickable. When an element is not in this list, its goal is forced to the views trigger type.

$widgets = \Burst\Integrations\Plugins\Elementor\Elementor::get_clickable_widgets();
// [ 'button', 'icon', 'icon-box', 'image', 'image-box', 'call-to-action', ... ]

Extend or restrict the list with the burst_elementor_clickable_widgets filter.


Predefined goals​

Pro - CreatorAvailable in the Creator tier

Integrations can register predefined goal templates. These are surfaced in the goals UI when the corresponding third-party plugin is active. Learn more about goal conversion tracking

Goals::get_predefined_goals() collects goal templates from each registered integration and returns them as a flat list. Templates are keyed by their id while collecting, so if more than one active integration declares the same goal id, the last one wins and the list contains no duplicate IDs.

Show code
/**
* @param bool $skip_active_check Pass true to include goals from inactive integration plugins.
* @return array<int, array{
* id: string,
* type: string,
* description: string,
* status: string,
* server_side: bool,
* url: string,
* hook: string
* }>
*/
$predefined = burst_loader()->frontend->goals->get_predefined_goals();

By default only goals from active integration plugins are returned.

A predefined goal template can be saved with Goal::add_predefined( string $id ):

$goal = new \Burst\Frontend\Goals\Goal();
$new_id = $goal->add_predefined( 'woocommerce-purchase' );
// Returns the new goal ID on success, or 0 on failure.

add_predefined() sets status = 'active', conversion_metric = 'visitors', and url = '*' as defaults before calling save().

Available predefined goals by integration​

IntegrationGoal IDTypeTrigger
Elementorelementor_pro_forms_form_submittedhookelementor_pro/forms/form_submitted
Elementorsubmit_button_clickclicks.elementor-field-type-submit .elementor-button
WooCommercewoocommerce_add_to_carthookwoocommerce_add_to_cart
WooCommercewoocommerce_checkout_order_createdhookwoocommerce_checkout_order_created
WooCommercewoocommerce_payment_completehookwoocommerce_payment_complete
WooCommercewoocommerce_add_to_cart_clickclicks.add_to_cart_button
WooCommercewoocommerce_click_checkout_buttonclicks.wc-block-cart__submit-button
Easy Digital Downloadsedd_complete_purchasehookedd_complete_purchase
Easy Digital Downloadsedd_add_to_cartclicks.edd-add-to-cart
Easy Digital Downloadsedd_go_to_checkoutclicks.edd_go_to_checkout
Easy Digital Downloadsedd_click_purchaseclicks#edd-purchase-button
Admin and Site Enhancements (ASE)ase_contact_form_submit_clickclicks.asenha-cf-submit-button
Admin and Site Enhancements (ASE) Proformbuilder_after_emailhookformbuilder_after_email
Admin and Site Enhancements (ASE) Proase_pro_form_submit_clickclicks.fb-submit-button

Querying goals​

Show code
$goals = burst_loader()->frontend->goals->get_goals( [
'status' => 'active', // 'active', 'inactive', or 'all' (default)
'limit' => 10, // default: 9999
'offset' => 0, // default: 0
'orderby' => 'ID', // any column in burst_goals; default: 'ID'
'order' => 'ASC', // 'ASC' or 'DESC'; default: 'ASC'
'date_start' => strtotime( '-30 days' ), // Unix timestamp; -1 = no filter (default)
'date_end' => time(), // Unix timestamp; default: time()
] );

foreach ( $goals as $goal ) {
echo $goal->id . ': ' . $goal->title . ' (' . $goal->type . ')';
}

date_start and date_end filter on date_created. The orderby value is validated against the actual columns of burst_goals; unknown values fall back to 'ID'.


Saving and deleting goals​

Creating a new goal​

Show code
$goal = new \Burst\Frontend\Goals\Goal();
$goal->title = 'Newsletter signup';
$goal->type = 'clicks';
$goal->selector = '#newsletter-form .submit-btn';
$goal->status = 'active';
$success = $goal->save();
echo $goal->id; // populated after a successful insert

Updating an existing goal​

$goal = new \Burst\Frontend\Goals\Goal( 42 );
$goal->status = 'inactive';
$goal->save();

Deleting a goal​

$goal = new \Burst\Frontend\Goals\Goal( 42 );
$goal->delete(); // removes the goal row AND all burst_goal_statistics rows for this goal

Save behaviour​

  • title, selector, and hook are sanitized with sanitize_text_field().
  • url is sanitized as a relative URL. When page_or_website === 'website' the stored value is forced to '*'.
  • For page-scoped goals Burst resolves page_id from the URL (or resolves the URL from a supplied page_id for published posts) so visits matching can use the post ID.
  • block_goal and page_id are written only when those columns exist in the schema.
  • server_side is derived from the goal type definition — not settable by the caller.
  • date_start is set to time() on first creation or whenever status changes to 'active'.
  • Returns true on success, false on failure (including exceeding the free-tier limit of Goal::LIMIT_FREE (3) active goals). Activating an existing goal when already at the limit also fails.

Goal::can_add_goal() decides whether another active goal is allowed: it returns true when the burst_license_is_valid filter reports a valid Pro license, otherwise true only while fewer than Goal::LIMIT_FREE (3) active goals exist.


REST API endpoints​

All goals endpoints are under the burst/v1 namespace and require a valid nonce.

EndpointMethodPermissionDescription
burst/v1/goals/getGETviewReturns all configured goals, the list of predefined goals, and the current goal_limit and active_goals_count.
burst/v1/goals/addPOSTmanageCreates a new goal.
burst/v1/goals/setPOSTmanageUpdates an existing goal.
burst/v1/goals/upsert_for_blockPOSTmanageCreates or updates a goal from the block editor, keyed by the block uid.
burst/v1/goals/deletePOSTmanageDeletes a goal and its associated statistics.
burst/v1/goals/add_predefinedPOSTmanageAdds a predefined goal from an integration.
burst/v1/posts/getGETviewReturns the pages the goal "select page" picker can choose from. See Select page picker.
burst/v1/data/goalsGETviewReturns goal conversion statistics for the selected date range.
burst/v1/data/live-goalsGETviewReturns live goal conversion counts (since midnight in the site's timezone).

The data/goals and data/live-goals endpoints accept a goal_id argument. Pass a numeric goal ID to report on a single goal, or the literal string all to aggregate conversions across every active goal. See Aggregated goal statistics.

goals/upsert_for_block resolves the target goal by the block uid (via Goal::get_by_uid()), falling back to id / goal_id. A uid is required for any create or update: when the goal cannot be resolved and no uid is supplied, the endpoint responds 400 with Goal UID is required.. The only exception is deleting an already-existing goal resolved by its id. The endpoint enforces the active-goal limit before inserting, builds the selector from the uid, and marks new goals with block_goal = 1. Passing status: delete removes the goal.

Permission levels:

  • view — requires view_burst_statistics capability
  • manage — requires manage_burst_statistics capability (typically administrators)

Hooks and filters​

burst_goal_types​

Filter the available goal type definitions. Controls which types appear in the goal editor and are accepted by Goal::save().

Parameters:

ParameterTypeDescription
$typesarrayAssociative array of goal type definitions keyed by type slug. Each entry may include label, description, icon, and server_side.

Example:

Show code
add_filter( 'burst_goal_types', function( array $types ): array {
$types['video_play'] = [
'label' => __( 'Video play', 'my-plugin' ),
'description' => __( 'Fires when a video starts playing.', 'my-plugin' ),
'server_side' => false,
];
return $types;
} );

burst_allowed_goal_types​

Filters the normalized list of goal type slugs accepted by Burst's sanitization layer.

Use this alongside burst_goal_types when you introduce a custom type and want it to survive request validation.

Parameters:

ParameterTypeDescription
$goal_typesstring[]Allowed goal type slugs.

Example:

Show code
add_filter( 'burst_allowed_goal_types', function( array $goal_types ): array {
$goal_types[] = 'video_play';
return array_values( array_unique( $goal_types ) );
} );

burst_clickable_blocks​

Filters the list of Gutenberg block names that support click goal tracking in the block editor inspector panel. See Clickable blocks.

Parameters:

ParameterTypeDescription
$clickable_blocksstring[]Block type names considered clickable. Defaults to core/button, core/image and core/navigation-link.

Example:

Show code
add_filter( 'burst_clickable_blocks', function( array $clickable_blocks ): array {
$clickable_blocks[] = 'core/buttons';
return array_values( array_unique( $clickable_blocks ) );
} );

burst_elementor_clickable_widgets​

Filters the list of Elementor widget type names that support click goal tracking in the Elementor editor. Widgets not in this list are forced to the views trigger type. See Clickable widgets.

Parameters:

ParameterTypeDescription
$clickable_widgetsstring[]Elementor widget type names considered clickable. Defaults include button, icon, icon-box, image, image-box, call-to-action, animated-headline, price-table, form, nav-menu and search-form.

Example:

Show code
add_filter( 'burst_elementor_clickable_widgets', function( array $clickable_widgets ): array {
$clickable_widgets[] = 'my-custom-cta';
return array_values( array_unique( $clickable_widgets ) );
} );

burst_before_save_goals​

Fires immediately before a goal is written to the database (both insert and update).

Parameters:

ParameterTypeDescription
$goal\Burst\Frontend\Goals\GoalThe Goal object about to be saved. Public properties can be read or mutated.

Example:

Show code
add_action( 'burst_before_save_goals', function( \Burst\Frontend\Goals\Goal $goal ): void {
if ( $goal->type === 'hook' && empty( $goal->hook ) ) {
$goal->hook = 'woocommerce_thankyou';
}
} );

burst_after_save_goals​

Fires after a goal has been successfully saved and the object refreshed from the database. Does not fire when save() returns false.

Parameters:

ParameterTypeDescription
$goal\Burst\Frontend\Goals\GoalThe saved Goal object with current database values, including the new id for inserts.

Example:

Show code
add_action( 'burst_after_save_goals', function( \Burst\Frontend\Goals\Goal $goal ): void {
if ( $goal->status === 'active' ) {
my_crm_sync_goal( $goal->id, $goal->title );
}
} );

burst_install_tables​

Action fired by the Burst installer to trigger database table creation. Goals hooks into this at priority 10 to create {prefix}_burst_goals.

Parameters: none.

Example:

// Force table creation, e.g. after a manual reset.
do_action( 'burst_install_tables' );

burst_goal_fields​

Filters the goal-editor field schema used by the admin app.

Use this to alter the fields shown for goal creation/editing, including adding custom controls or removing unsupported ones.

Parameters:

ParameterTypeDescription
$fieldsarrayGoal field definitions loaded from goal-fields.php.

burst_after_updated_goals​

Fires after goal collections are updated through the REST API, including add, update, and delete flows handled by the admin app.

This is broader than burst_after_save_goals, which fires per-goal on model save.

Parameters: none.

Example:

add_action( 'burst_after_updated_goals', function(): void {
my_plugin_refresh_goal_dependent_cache();
} );

burst_license_is_valid​

Filters whether a valid Pro license is active. Goal::can_add_goal() uses this to decide whether to allow unlimited goals; when no filter returns true, the free-tier limit of Goal::LIMIT_FREE (3) active goals applies.

Parameters:

ParameterTypeDescription
$is_validboolWhether a valid license is active. Defaults to false.

Example:

if ( burst_license_is_valid() ) {
// Pro features are available.
}