Complete reference for all named hooks (actions and filters) and UI slots available to plugins.
For a guide on how to use these, see Plugin Development Guide.
Detailed documentation: For full examples and usage guides, see the Plugin Developer Documentation.
Stability Levels
| Level | Meaning |
|---|---|
| Stable | Will not change without a major version bump. Safe for third-party plugins. |
| Beta | API is solid but may change with a minor version bump. |
| Experimental | May be removed or changed at any time. |
Action Hooks
Actions are fire-and-forget events. Register with registry.addAction(hook, callback, priority?).
Callbacks may be async. The engine awaits all callbacks in priority order.
| Hook | Args | When it fires | Typical use | Stability |
|---|---|---|---|---|
server:boot | (none) | After plugins load, before the Express app is created. Not replayed for plugins activated at runtime. | Rarely needed; prefer server:ready | Stable |
server:ready | server: http.Server | After the server starts listening on its port. Replayed when a plugin is activated at runtime. | Start background tasks, open external connections, start polling loops | Stable |
server:shutdown | (none) | During graceful shutdown (SIGTERM/SIGINT) | Stop background tasks, close external connections | Stable |
server:routes:auth | router: Router | After auth routes are mounted. Replayed at runtime activation. | Add routes under /api/v1/auth/ (e.g. router.get('/my-plugin/status', handler)) | Stable |
server:routes:cashier | router: Router | After cashier routes are mounted. Replayed at runtime activation. | Add routes under /api/v1/cashier/ | Stable |
server:routes:admin | router: Router | When admin routes are composed. Replayed at runtime activation. | Add routes under /api/v1/admin/ (admin auth middleware already applied) | Stable |
server:routes:api | router: Router | After all API routes are mounted, on a router at the app root. Replayed at runtime activation. | Mount top-level routes | Stable |
plugin:activated | providerType: string | After a plugin is activated via Admin Panel | React to another plugin becoming active | Beta |
plugin:deactivating | providerType: string | During deactivation via Admin Panel, before the plugin’s hooks and routes are removed | Clean up the plugin’s own external resources | Beta |
plugin:deactivated | providerType: string | After a plugin is deactivated via Admin Panel | Clean up state related to another plugin | Beta |
plugin:auto-disabled | providerType: string | When a plugin is auto-disabled due to errors | Logging, alerting | Beta |
plugin:settings:saved | providerType: string, settings: Record<string, string> | After plugin settings are saved via Admin Panel | Re-initialize plugin with new config | Beta |
Example: server:routes:auth
import type { Router, Request, Response } from 'express';
registry.addAction('server:routes:auth', (router: unknown) => {
(router as Router).get('/my-plugin/status', (_req: Request, res: Response) => {
res.json({ enabled: true });
});
});
Example: server:ready + server:shutdown
let poller: ReturnType<typeof setInterval> | null = null;
registry.addAction('server:ready', async () => {
const config = await registry.config.getPluginConfig('my-plugin');
poller = setInterval(() => poll(config), 60_000);
});
// Register shutdown inside server:ready so it only fires if server:ready ran
registry.addAction('server:ready', async () => {
registry.addAction('server:shutdown', () => {
if (poller) clearInterval(poller);
});
});
Game Lifecycle Hooks
Experimental: These hooks are observation-only and fire-and-forget. They must not block game flow. Their API may change in future versions.
| Hook | Args | When it fires | Typical use | Stability |
|---|---|---|---|---|
game:hand:started | { tableId, handNumber, dealerSeat } | After a new hand begins | Analytics, logging, side-bet triggers | Experimental |
game:hand:completed | { tableId, handNumber, winners: Array<{ playerId, amount }> } | After a hand completes and pot is distributed | Analytics, achievements, statistics | Experimental |
game:player:seated | { tableId, userId, seatIndex } | After a player joins a table | Welcome messages, tracking, notifications | Experimental |
game:player:left | { tableId, userId, reason: 'voluntary' | 'forced' } | After a player leaves a table | Cleanup, tracking, notifications | Experimental |
Filter Hooks
Filters transform a value. Register with registry.addFilter(hook, callback, priority?).
Filters are synchronous — do not return a Promise. All plugin-facing filter hooks (including plugin:settings:validate) use the synchronous path. If a sync filter returns a Promise, the engine logs a warning and the Promise object becomes the next callback’s input.
| Hook | Value type | Args | When it fires | Typical use | Stability |
|---|---|---|---|---|---|
auth:providers | string[] (starts as ['local']) | (none) | On GET /api/v1/auth/providers | Append your provider identifier so the frontend knows your auth method is available | Stable |
cashier:deposit:methods | DepositMethod[] | (none) | On GET /api/v1/cashier/payment-methods | Append deposit methods so the cashier UI renders your cashier.deposit.method-list fill | Stable |
cashier:withdraw:methods | WithdrawMethod[] | (none) | On GET /api/v1/cashier/payment-methods | Append withdrawal methods so the cashier UI renders your cashier.withdraw.method-list fill | Stable |
plugin:settings:validate | { valid: boolean, errors: string[] } | settings: Record<string, string>, providerType: string | Before plugin settings are saved to DB | Validate settings, append to errors to reject the save. Runs only the callbacks of the plugin being saved. | Stable |
DepositMethod shape
The filter starts with the active PaymentSystemConfig objects for the direction. For each entry, the route reads only these fields:
{
type?: string; // provider type; falls back to mapping `id`
name?: string; // shown in cashier UI unless the type has a built-in label
id?: string; // method ID sent to the client
systemId?: string; // used only if no active payment system of this type exists
}
An entry needs type or id. The response keeps one entry per provider type, and drops any entry whose provider type has a PaymentSystem row that is disabled for that direction.
WithdrawMethod shape
Same fields as DepositMethod, filtered against the withdrawal toggle.
Example: auth:providers
registry.addFilter('auth:providers', (providers) => [
...(providers as string[]),
'my-plugin',
]);
Example: cashier:deposit:methods
registry.addFilter('cashier:deposit:methods', (methods) => [
...(methods as object[]),
{
type: 'my-plugin',
name: 'My Payment Method',
},
]);
SlotFill Slots
UI slots are render positions in the frontend where plugins can inject React components.
Register fills from the plugin’s client bundle using window.__pokerEngine.fills.register().
window.__pokerEngine.fills.register(
slot: string,
component: React.ComponentType,
options?: {
priority?: number; // lower = first; default 10
providerType?: string; // used for cleanup on deactivation
}
)
For the complete slot reference including props, see Slot Fills.
| Slot | Location | Notes | Stability |
|---|---|---|---|
auth.login.below-form | Login page, below the login form | Add alternative login buttons (e.g. Keychain) | Stable |
auth.register.below-form | Register page, below the register form | Add alternative registration flows | Stable |
cashier.deposit.method-list | Cashier page, deposit tab, method list | Add deposit method cards/panels | Stable |
cashier.withdraw.method-list | Cashier page, withdrawal tab, method list | Add withdrawal method panels | Stable |
layout.main.sidebar.end | Main layout sidebar, bottom section | Add navigation items for authenticated users | Stable |
layout.main.header.end | Main layout header, right side | Add header elements for authenticated users | Stable |
layout.admin.sidebar.end | Admin layout sidebar, bottom section | Add admin navigation items | Stable |
lobby.top | Lobby page, above the table list | Add banners, announcements, or promotions | Stable |
plugin.settings.before-form | Admin Panel → Plugin Settings, above the settings form | Add custom UI above the settings form | Stable |
profile.edit.after | Profile page, below edit form | Add wallet connections, external integrations | Stable |
admin.users.row.flags | Admin users table, flags column | Add flag badges for plugin-specific user attributes | Stable |
admin.user-detail.integrations | Admin user detail drawer, activity section | Add per-user integration details | Stable |
plugin.settings.after-form | Admin Panel → Plugin Settings, below the settings form | Add help text, status indicators, custom controls | Stable |
Dynamic slots: The engine also supports
plugin.settings.after-form.{providerType}— a per-plugin variant that only renders for a specific plugin’s settings page. Register with the exact providerType suffix (e.g.plugin.settings.after-form.steem).
Source Locations
For developers who want to see where each hook fires or slot renders:
Action Hooks
| Hook | Fired in |
|---|---|
server:boot | src/index.ts |
server:ready | src/index.ts |
server:shutdown | src/index.ts |
server:routes:auth | src/app.ts |
server:routes:cashier | src/app.ts |
server:routes:admin | src/api/routes/admin/plugin-admin-router.ts |
server:routes:api | src/app.ts |
plugin:activated | src/core/plugins/plugin-manager.service.ts |
plugin:deactivating | src/core/plugins/plugin-manager.service.ts |
plugin:deactivated | src/core/plugins/plugin-manager.service.ts |
plugin:auto-disabled | src/core/plugins/circuit-breaker.ts |
plugin:settings:saved | src/api/routes/admin/plugins.routes.ts |
game:hand:started | src/game/table/table.ts |
game:hand:completed | src/game/table/table-actions.ts |
game:player:seated | src/game/table/table-players.ts |
game:player:left | src/game/table/table-players.ts |
Filter Hooks
| Hook | Applied in |
|---|---|
auth:providers | src/api/routes/auth.routes.ts (GET /auth/providers) |
cashier:deposit:methods | src/api/routes/cashier.routes.ts (GET /cashier/payment-methods) |
cashier:withdraw:methods | src/api/routes/cashier.routes.ts (GET /cashier/payment-methods) |
plugin:settings:validate | src/api/routes/admin/plugins.routes.ts |
Slots
| Slot | Rendered in |
|---|---|
auth.login.below-form | client/src/pages/auth/LoginPage.tsx |
auth.register.below-form | client/src/pages/auth/RegisterPage.tsx |
cashier.deposit.method-list | client/src/pages/cashier/CashierPage.tsx |
cashier.withdraw.method-list | client/src/pages/cashier/CashierPage.tsx |
layout.main.sidebar.end | client/src/layouts/MainLayout.tsx |
layout.main.header.end | client/src/layouts/MainLayout.tsx |
layout.admin.sidebar.end | client/src/layouts/AdminLayout.tsx |
lobby.top | client/src/pages/lobby/LobbyPage.tsx |
profile.edit.after | client/src/pages/profile/ProfilePage.tsx |
admin.users.row.flags | client/src/pages/admin/UsersManagementPage.tsx |
admin.user-detail.integrations | client/src/components/admin/UserDetailDrawer.tsx |
plugin.settings.before-form | client/src/pages/admin/PluginSettingsPage.tsx |
plugin.settings.after-form | client/src/pages/admin/PluginSettingsPage.tsx |
plugin.settings.after-form.{providerType} | client/src/pages/admin/PluginSettingsPage.tsx |
Hook System Implementation
The hook system is implemented in src/core/hooks/hook-registry.ts (HookRegistry class).
Key characteristics:
- Actions run in priority order (ascending), awaited sequentially
- Filters run in priority order (ascending), synchronously — each callback’s return value becomes the next callback’s input
- Errors in hooks are caught and logged; the hook continues with remaining callbacks, and a throwing filter leaves the value unchanged
- The circuit breaker in
src/core/plugins/circuit-breaker.tsauto-disables plugins that error too frequently - All hook registrations are tracked per plugin and rolled back on deactivation (
rollbackPluginRegistration)
See Also
- Plugin Development Guide — how to build a plugin
- Slot Fills — UI slot reference