Developer

Hooks API

WordPress-style actions and filters for inter-resource communication.

Hooks API

Hooks let resources communicate with each other using the WordPress action/filter pattern. Resources can fire actions (broadcast events) and apply filters (transform data through a chain of handlers).

Capability required: hooks:provide (to register handlers and fire actions) and/or hooks:consume (to listen for actions and apply filters)

Actions (Fire-and-Forget)

Actions broadcast a payload to all listeners. The sender doesn’t wait for a response.

Firing an action

// Resource A (capability: hooks:provide)
await rp.hooks.doAction("weather:data_updated", { temp: 72, city: "Seoul" });

Listening for an action

// Resource B (capability: hooks:consume)
const unsubscribe = rp.hooks.onAction("weather:data_updated", (payload) => {
  console.log("Weather changed:", payload);
  // { temp: 72, city: "Seoul" }
});

// Later: stop listening
unsubscribe();

Filters (Data Transformation)

Filters pass a value through a chain of handlers. Each handler can transform the value before passing it to the next.

Registering a filter handler

// Resource A (capability: hooks:provide)
rp.hooks.addFilter("weather:format_temp", (value) => {
  // Convert Fahrenheit to Celsius
  return { ...value, tempC: Math.round((value.tempF - 32) * 5 / 9) };
}, { priority: 10 });

Applying a filter

// Resource B (capability: hooks:consume)
const result = await rp.hooks.applyFilter("weather:format_temp", { tempF: 72 });
console.log(result); // { tempF: 72, tempC: 22 }

Priority

Multiple handlers on the same filter run in priority order (lower number first). Default priority is 10.

// Runs first (priority 5)
rp.hooks.addFilter("myapp:process_data", handler1, { priority: 5 });

// Runs second (priority 10, default)
rp.hooks.addFilter("myapp:process_data", handler2);

// Runs third (priority 20)
rp.hooks.addFilter("myapp:process_data", handler3, { priority: 20 });

Each handler receives the return value of the previous handler.

API Reference

rp.hooks.doAction(hookName, payload?)

Fire an action to all registered listeners.

ParameterTypeDescription
hookNamestringHook name (e.g., "weather:data_updated")
payloadunknown (optional)Data to broadcast

rp.hooks.onAction(hookName, callback)

Listen for an action. Returns an unsubscribe function.

ParameterTypeDescription
hookNamestringHook name to listen for
callback(payload: unknown) => voidCalled when the action fires
Returns() => voidCall to unsubscribe

rp.hooks.addFilter(hookName, handler, opts?)

Register a filter handler.

ParameterTypeDescription
hookNamestringFilter name
handler(value: unknown) => unknownTransform function (can be async)
opts.prioritynumber (optional)Execution order (default: 10, lower runs first)

rp.hooks.applyFilter(hookName, value)

Run a value through all registered filter handlers.

ParameterTypeDescription
hookNamestringFilter name
valueunknownInitial value to transform
ReturnsPromise<unknown>Transformed value

If no handlers are registered, the original value is returned unchanged.

rp.hooks.removeFilter(hookName)

Remove your resource’s filter handler for a hook.

ParameterTypeDescription
hookNamestringFilter name to unregister from

Manifest Configuration

Declare which hooks your resource provides and consumes:

{
  "capabilities": [
    "hooks:provide",
    "hooks:consume"
  ]
}

Use hooks:provide if your resource fires actions or registers filter handlers. Use hooks:consume if your resource listens for actions or applies filters. Most resources that use hooks will need both.

Naming Convention

Hook names use a namespace prefix followed by a colon:

{namespace}:{event_name}

Examples:

  • weather:data_updated — weather resource fires when data changes
  • analytics:page_view — analytics resource fires on page views
  • cms:post_published — CMS resource fires when content is published

Use your resource’s name or ID as the namespace to avoid collisions.

Example: Plugin System

// Theme resource provides a filter for page rendering
rp.hooks.addFilter("theme:render_header", (html) => {
  return html + '<div class="announcement-bar">Sale today!</div>';
});

// Main app applies the filter
const headerHtml = await rp.hooks.applyFilter("theme:render_header", "<header>...</header>");

Backend Limitations

From the Node.js backend, doAction, applyFilter, and removeFilter work normally. However, onAction and addFilter are not yet available from backends — use the frontend SDK for receiving hooks.