Build a Pro Extension
Since Jetonomy Pro 1.9.0, the extension system that powers all bundled Pro modules (Private Messaging, Polls, Analytics, and the rest) is open to third-party developers. Your plugin can register an extension that gets the exact same lifecycle the bundled ones do: an admin toggle on the Extensions screen, table creation on the migration guard, boot() when enabled, and cleanup on deactivation.
Requires: Jetonomy Pro active (the SDK classes ship with Pro). Your extension is not gated by the customer's Jetonomy Pro license - if you sell your extension separately, gate your own boot().
The contract
Extend the abstract Jetonomy_Pro\Extension and register the instance through the jetonomy_pro_register_extensions filter:
add_filter( 'jetonomy_pro_register_extensions', function ( $extensions ) {
if ( ! class_exists( 'Jetonomy_Pro\\Extension' ) ) {
return $extensions; // Pro not active.
}
require_once __DIR__ . '/class-my-extension.php';
$ext = new \Acme\Jetonomy\My_Extension();
$extensions[ $ext->get_id() ] = $ext;
return $extensions;
} );
namespace Acme\Jetonomy;
class My_Extension extends \Jetonomy_Pro\Extension {
/**
* REQUIRED for external extensions. The base id() derives the id from
* your class NAMESPACE (second-to-last segment, Pascal_Case → kebab-case),
* which almost never matches for third-party code - return it explicitly.
*/
public function id(): string {
return 'acme-kudos';
}
public function meta(): array {
return array(
'id' => 'acme-kudos',
'name' => __( 'Acme Kudos', 'acme-kudos' ),
'version' => '1.0.0',
'description' => __( 'Send kudos to helpful members.', 'acme-kudos' ),
'category' => __( 'Engagement', 'acme-kudos' ),
'requires' => 'starter',
);
}
/** Called only when the extension is enabled on the Extensions screen. */
public function boot(): void {
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
/** Called on the version-change migration guard - dbDelta your tables here. Idempotent. */
public function activate(): void {}
/** Called on Jetonomy Pro deactivation - clear cron, etc. */
public function deactivate(): void {}
}
Lifecycle guarantees
| Moment | What runs |
|---|---|
Pro loads (plugins_loaded:20) |
Your filter callback registers the instance |
| Stored DB version ≠ Pro version | activate() on every registered extension (including yours) |
| Extension enabled + each request | boot() |
| Pro deactivated | deactivate() on enabled extensions |
Enable/disable state lives in the jetonomy_pro_extensions option and is toggled from Jetonomy → Extensions, where your extension appears alongside the bundled ones using your meta() values.
Rules that keep you compatible
- Never reuse a bundled id (
private-messaging,polls,reactions, …) - duplicate ids are ignored, first registration wins. - Prefix everything: options and user meta
acme_kudos_*, tables{$wpdb->prefix}acme_kudos_*, cron hooksacme_kudos_*. Do not use thejetonomy_pro_prefixes - those namespaces belong to bundled modules. - REST permission callbacks: use
$this->rest_auth_mutation( $caps )from the base class for mutation routes - it resolves free'sREST_Authlazily and fails closed (never reference\Jetonomy\API\REST_Authdirectly at registration time). - Background jobs go through
Jetonomy_Pro\Queue(async()/recurring()/cancel()) - Action-Scheduler-first with WP-Cron fallback; cancel your hooks indeactivate(). - Licensing is yours: the customer's Jetonomy Pro license never disables your extension. If yours is paid, check your own license at the top of
boot()and return early. - Frontend surfaces must follow the Frontend Interactivity Standard (
docs/standards/frontend-interactivity.mdin the plugin repo) (declarative store actions,restFetch, re-init onjetonomy:navigated) and use the--jt-*design tokens.
Hooking free instead
If you don't need an admin toggle, tables, or the lifecycle - just behavior - you may not need an extension at all. The free plugin fires 190+ documented hooks (hooks reference) and supports REST, frontend, and adapter extension without Pro. Reach for the extension SDK when you want your feature to feel like a first-class Pro module.