Manifest Files
How Manifests Work
Section titled “How Manifests Work”Any WordPress plugin can award gamification points without depending on WB Gamification at runtime. Create a file named wb-gamification.php in your plugin’s root directory. The file returns a plain PHP array. WB Gamification discovers and loads it automatically at plugins_loaded priority 5 — before any hooks fire.
If WB Gamification is not installed, your manifest file is simply never loaded. No dependency errors, no fatal calls.
File Location
Section titled “File Location”your-plugin/├── your-plugin.php (main plugin file)└── wb-gamification.php (gamification manifest)Manifest Structure
Section titled “Manifest Structure”<?php/** * WB Gamification manifest for My Plugin. * * This file is auto-discovered by WB Gamification at plugins_loaded priority 5. * It is safe to ship in the free version — WB Gamification is an optional dependency. */return [ 'plugin' => 'my-plugin', // Used in Registry collision reports. 'version' => '1.0.0', // Your plugin version (informational). 'triggers' => [ // Each array in 'triggers' is a gamification action definition. [ 'id' => 'my_plugin_action', 'label' => 'Did Something', 'description' => 'Awarded when a member does something in My Plugin.', 'hook' => 'my_plugin_action_hook', 'user_callback' => function( $user_id, $data ) { return $user_id; }, 'metadata_callback' => function( $user_id, $data ) { return [ 'item_id' => $data->id ]; }, 'default_points' => 10, 'category' => 'my_plugin', 'icon' => 'dashicons-star-filled', 'repeatable' => true, 'cooldown' => 3600, 'daily_cap' => 5, 'async' => false, 'standalone_only' => false, 'requires_buddypress' => false, ], ],];Trigger Field Reference
Section titled “Trigger Field Reference”| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Unique action identifier. Use plugin_name_action format to avoid collisions |
label |
string | Yes | Human-readable label shown in the admin actions list |
description |
string | No | Longer description shown in tooltips and the setup wizard |
hook |
string | Yes | WordPress action hook name to listen on |
user_callback |
callable | Yes | Receives the hook arguments. Must return the WordPress user ID to award points to |
metadata_callback |
callable | No | Receives the hook arguments. Returns an array merged into event metadata (available in wb_gam_points_for_action filter) |
default_points |
int | Yes | Default points awarded. Admins can override this in the settings UI |
category |
string | No | Category slug for grouping in the admin UI (e.g. buddypress, woocommerce) |
icon |
string | No | Dashicon class (e.g. dashicons-heart) for the admin UI |
repeatable |
bool | No | Whether the action can be awarded more than once. Default true |
cooldown |
int | No | Minimum seconds between repeated awards for the same user. 0 = no cooldown |
daily_cap |
int | No | Maximum awards per calendar day per user. 0 = unlimited |
async |
bool | No | Route through Action Scheduler instead of processing synchronously. Use for high-volume events |
standalone_only |
bool | No | Set true to skip this trigger when BuddyPress is active (because BP’s own hooks cover the same event better) |
requires_buddypress |
bool | No | Set true to only register this trigger when BuddyPress is active |
Complete Working Example
Section titled “Complete Working Example”This example awards points when a member submits a contact form in a fictional forms plugin, with a daily cap and metadata enrichment:
<?phpreturn [ 'plugin' => 'my-forms-plugin', 'version' => '2.1.0', 'triggers' => [ // Award points for submitting any form. [ 'id' => 'my_forms_submission', 'label' => 'Submitted a Form', 'description' => 'Awarded each time a member submits a form.', 'hook' => 'my_forms_submission_complete', 'user_callback' => function( $form_id, $user_id ) { return (int) $user_id; }, 'metadata_callback' => function( $form_id, $user_id ) { return [ 'form_id' => (int) $form_id ]; }, 'default_points' => 5, 'category' => 'forms', 'icon' => 'dashicons-feedback', 'repeatable' => true, 'cooldown' => 0, 'daily_cap' => 3, 'async' => false, ],
// Award a one-time bonus for the first form submission. [ 'id' => 'my_forms_first_submission', 'label' => 'First Form Submission', 'description' => 'One-time bonus for a member\'s very first form submission.', 'hook' => 'my_forms_submission_complete', 'user_callback' => function( $form_id, $user_id ) { return (int) $user_id; }, 'default_points' => 25, 'category' => 'forms', 'repeatable' => false, // Only once per member. 'cooldown' => 0, 'daily_cap' => 0, 'async' => false, ],
// BP-only trigger: award points for form submissions inside a group. [ 'id' => 'my_forms_group_submission', 'label' => 'Submitted a Group Form', 'description' => 'Awarded when a member submits a form inside a BuddyPress group.', 'hook' => 'my_forms_group_submission_complete', 'user_callback' => function( $form_id, $user_id, $group_id ) { return (int) $user_id; }, 'default_points' => 8, 'category' => 'forms', 'repeatable' => true, 'cooldown' => 3600, 'daily_cap' => 5, 'requires_buddypress' => true, // Only registers when BP is active. ], ],];Conditional Triggers: standalone_only and requires_buddypress
Section titled “Conditional Triggers: standalone_only and requires_buddypress”These two flags let you ship one manifest that works correctly in both BuddyPress and non-BuddyPress environments.
Scenario: Your plugin fires my_plugin_post_published. When WordPress is running standalone, you want to award points for it. But when BuddyPress is active, the BuddyPress bp_publish_post integration already covers this event more richly — so you want to skip your version.
[ 'id' => 'my_plugin_post_published_standalone', 'hook' => 'my_plugin_post_published', 'standalone_only' => true, // Skip when BuddyPress is active. // ...],[ 'id' => 'my_plugin_post_published_bp', 'hook' => 'my_plugin_post_published', 'requires_buddypress' => true, // Only register when BuddyPress is active. // ...],Both flags are stripped from the trigger before it is passed to Registry::register_action().
Registering Actions Programmatically
Section titled “Registering Actions Programmatically”You can also call the PHP helper directly instead of using a manifest file. This is useful when your trigger logic is complex enough to warrant a full class:
add_action( 'wb_gam_register', function() { wb_gam_register_action( [ 'id' => 'my_plugin_action', 'label' => 'My Action', 'hook' => 'my_plugin_hook', 'user_callback' => fn( $user_id ) => $user_id, 'default_points' => 10, 'category' => 'my_plugin', 'repeatable' => true, 'cooldown' => 0, 'daily_cap' => 0, ] );} );This fires after Registry::init() at plugins_loaded priority 6.
Validation
Section titled “Validation”The ManifestLoader validates every manifest and trigger at load time:
- Manifest must return an array. If the file does not
return array( ... ), it is skipped. WhenWP_DEBUGis enabled, a message is logged todebug.log. - Each trigger must include
id,hook, anddefault_points. Missing any of these causes the trigger to be skipped with a debug log entry identifying the file and missing key.
This means you can ship a manifest confidently — malformed entries are silently ignored in production and loudly reported during development.
Developer Hooks
Section titled “Developer Hooks”wb_gam_manifest_paths (filter)
Section titled “wb_gam_manifest_paths (filter)”Add custom directories for the manifest scanner. The ManifestLoader scans every directory in this array for *.php files:
add_filter( 'wb_gam_manifest_paths', function ( array $paths ): array { $paths[] = get_stylesheet_directory() . '/gamification/'; return $paths;} );wb_gam_manifests_loaded (action)
Section titled “wb_gam_manifests_loaded (action)”Fires after all manifests have been loaded and validated. Receives the complete array of action definitions:
add_action( 'wb_gam_manifests_loaded', function ( array $actions ): void { error_log( 'WB Gamification loaded ' . count( $actions ) . ' manifest actions.' );} );Quick Start
Section titled “Quick Start”New to manifest files? Follow the Build Your First Integration tutorial to create a working manifest in 5 minutes.

