Extension Points

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

LevelMeaning
StableWill not change without a major version bump. Safe for third-party plugins.
BetaAPI is solid but may change with a minor version bump.
ExperimentalMay 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.

HookArgsWhen it firesTypical useStability
server:boot(none)After plugins load, before the Express app is created. Not replayed for plugins activated at runtime.Rarely needed; prefer server:readyStable
server:readyserver: http.ServerAfter the server starts listening on its port. Replayed when a plugin is activated at runtime.Start background tasks, open external connections, start polling loopsStable
server:shutdown(none)During graceful shutdown (SIGTERM/SIGINT)Stop background tasks, close external connectionsStable
server:routes:authrouter: RouterAfter 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:cashierrouter: RouterAfter cashier routes are mounted. Replayed at runtime activation.Add routes under /api/v1/cashier/Stable
server:routes:adminrouter: RouterWhen admin routes are composed. Replayed at runtime activation.Add routes under /api/v1/admin/ (admin auth middleware already applied)Stable
server:routes:apirouter: RouterAfter all API routes are mounted, on a router at the app root. Replayed at runtime activation.Mount top-level routesStable
plugin:activatedproviderType: stringAfter a plugin is activated via Admin PanelReact to another plugin becoming activeBeta
plugin:deactivatingproviderType: stringDuring deactivation via Admin Panel, before the plugin’s hooks and routes are removedClean up the plugin’s own external resourcesBeta
plugin:deactivatedproviderType: stringAfter a plugin is deactivated via Admin PanelClean up state related to another pluginBeta
plugin:auto-disabledproviderType: stringWhen a plugin is auto-disabled due to errorsLogging, alertingBeta
plugin:settings:savedproviderType: string, settings: Record<string, string>After plugin settings are saved via Admin PanelRe-initialize plugin with new configBeta

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.

HookArgsWhen it firesTypical useStability
game:hand:started{ tableId, handNumber, dealerSeat }After a new hand beginsAnalytics, logging, side-bet triggersExperimental
game:hand:completed{ tableId, handNumber, winners: Array<{ playerId, amount }> }After a hand completes and pot is distributedAnalytics, achievements, statisticsExperimental
game:player:seated{ tableId, userId, seatIndex }After a player joins a tableWelcome messages, tracking, notificationsExperimental
game:player:left{ tableId, userId, reason: 'voluntary' | 'forced' }After a player leaves a tableCleanup, tracking, notificationsExperimental

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.

HookValue typeArgsWhen it firesTypical useStability
auth:providersstring[] (starts as ['local'])(none)On GET /api/v1/auth/providersAppend your provider identifier so the frontend knows your auth method is availableStable
cashier:deposit:methodsDepositMethod[](none)On GET /api/v1/cashier/payment-methodsAppend deposit methods so the cashier UI renders your cashier.deposit.method-list fillStable
cashier:withdraw:methodsWithdrawMethod[](none)On GET /api/v1/cashier/payment-methodsAppend withdrawal methods so the cashier UI renders your cashier.withdraw.method-list fillStable
plugin:settings:validate{ valid: boolean, errors: string[] }settings: Record<string, string>, providerType: stringBefore plugin settings are saved to DBValidate 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.

SlotLocationNotesStability
auth.login.below-formLogin page, below the login formAdd alternative login buttons (e.g. Keychain)Stable
auth.register.below-formRegister page, below the register formAdd alternative registration flowsStable
cashier.deposit.method-listCashier page, deposit tab, method listAdd deposit method cards/panelsStable
cashier.withdraw.method-listCashier page, withdrawal tab, method listAdd withdrawal method panelsStable
layout.main.sidebar.endMain layout sidebar, bottom sectionAdd navigation items for authenticated usersStable
layout.main.header.endMain layout header, right sideAdd header elements for authenticated usersStable
layout.admin.sidebar.endAdmin layout sidebar, bottom sectionAdd admin navigation itemsStable
lobby.topLobby page, above the table listAdd banners, announcements, or promotionsStable
plugin.settings.before-formAdmin Panel → Plugin Settings, above the settings formAdd custom UI above the settings formStable
profile.edit.afterProfile page, below edit formAdd wallet connections, external integrationsStable
admin.users.row.flagsAdmin users table, flags columnAdd flag badges for plugin-specific user attributesStable
admin.user-detail.integrationsAdmin user detail drawer, activity sectionAdd per-user integration detailsStable
plugin.settings.after-formAdmin Panel → Plugin Settings, below the settings formAdd help text, status indicators, custom controlsStable

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

HookFired in
server:bootsrc/index.ts
server:readysrc/index.ts
server:shutdownsrc/index.ts
server:routes:authsrc/app.ts
server:routes:cashiersrc/app.ts
server:routes:adminsrc/api/routes/admin/plugin-admin-router.ts
server:routes:apisrc/app.ts
plugin:activatedsrc/core/plugins/plugin-manager.service.ts
plugin:deactivatingsrc/core/plugins/plugin-manager.service.ts
plugin:deactivatedsrc/core/plugins/plugin-manager.service.ts
plugin:auto-disabledsrc/core/plugins/circuit-breaker.ts
plugin:settings:savedsrc/api/routes/admin/plugins.routes.ts
game:hand:startedsrc/game/table/table.ts
game:hand:completedsrc/game/table/table-actions.ts
game:player:seatedsrc/game/table/table-players.ts
game:player:leftsrc/game/table/table-players.ts

Filter Hooks

HookApplied in
auth:providerssrc/api/routes/auth.routes.ts (GET /auth/providers)
cashier:deposit:methodssrc/api/routes/cashier.routes.ts (GET /cashier/payment-methods)
cashier:withdraw:methodssrc/api/routes/cashier.routes.ts (GET /cashier/payment-methods)
plugin:settings:validatesrc/api/routes/admin/plugins.routes.ts

Slots

SlotRendered in
auth.login.below-formclient/src/pages/auth/LoginPage.tsx
auth.register.below-formclient/src/pages/auth/RegisterPage.tsx
cashier.deposit.method-listclient/src/pages/cashier/CashierPage.tsx
cashier.withdraw.method-listclient/src/pages/cashier/CashierPage.tsx
layout.main.sidebar.endclient/src/layouts/MainLayout.tsx
layout.main.header.endclient/src/layouts/MainLayout.tsx
layout.admin.sidebar.endclient/src/layouts/AdminLayout.tsx
lobby.topclient/src/pages/lobby/LobbyPage.tsx
profile.edit.afterclient/src/pages/profile/ProfilePage.tsx
admin.users.row.flagsclient/src/pages/admin/UsersManagementPage.tsx
admin.user-detail.integrationsclient/src/components/admin/UserDetailDrawer.tsx
plugin.settings.before-formclient/src/pages/admin/PluginSettingsPage.tsx
plugin.settings.after-formclient/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.ts auto-disables plugins that error too frequently
  • All hook registrations are tracked per plugin and rolled back on deactivation (rollbackPluginRegistration)

See Also

Try the live demo Pricing

Have questions about Poker Engine? We reply fast.