# How to Configure hooks.json for Managing Plugin Lifecycle Events

> Learn to configure hooks.json for OpenAI plugins to manage plugin lifecycle events like load, enable, disable, update, and unload for deterministic initialization and cleanup.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-07-04

---

**The [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) manifest lets OpenAI plugins declare lifecycle hooks—JavaScript modules that run automatically during load, enable, disable, update, and unload events—allowing deterministic initialization and cleanup without user intervention.**

This guide explains how to configure [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) to manage plugin lifecycle events in the **openai/plugins** repository. By defining this optional manifest, you can automate setup routines, resource management, and state transitions across the five key lifecycle stages.

## Available Plugin Lifecycle Hooks

The platform recognizes five standard hooks that trigger at specific moments. Configuring these in [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) ensures your plugin responds predictably to state changes.

- **`onLoad`**: Executes immediately when the plugin is discovered, before any user interaction. Use this to validate environment variables, parse configuration, or initialize shared API clients.
- **`onEnable`**: Fires when a user explicitly enables the plugin via the UI or CLI. Ideal for starting background workers, opening WebSocket connections, or scheduling recurring tasks.
- **`onDisable`**: Runs when the plugin is disabled. Implement graceful shutdown logic here—cancel timers, close sockets, and persist ephemeral state.
- **`onUnload`**: Triggered when the plugin is removed from memory (e.g., during hot-reload). Use this to release native resources, delete temporary files, or revoke security tokens.
- **`onUpdate`**: Invoked after the plugin code or assets are updated. Perfect for data migration scripts, cache rehydration, or schema upgrades.

Attempting to declare unsupported hook names will not crash the plugin; the platform ignores unknown entries and logs a warning.

## Structure of the hooks.json Manifest

The [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) file is a plain JSON object where keys are hook names and values are relative file paths to JavaScript or TypeScript modules. Each module must export a single async function named `handler`.

```json
{
  "onLoad": "./hooks/onLoad.js",
  "onEnable": "./hooks/onEnable.js",
  "onDisable": "./hooks/onDisable.js"
}

```

Every hook handler follows this signature:

```javascript
/** @type {(context: HookContext) => Promise<void>} */
export async function handler(context) {
  // Lifecycle logic here
}

```

The `context` object provides:

- **`pluginId`**: The unique identifier for this plugin instance.
- **`config`**: The parsed contents of [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json).
- **`logger`**: A scoped logger that writes to the platform's diagnostic console.
- **`state`**: A shared mutable object for storing data across hooks (e.g., HTTP clients, timers).

If a hook throws an error, the platform logs the failure but continues loading the plugin, preventing a single failed initialization from breaking the entire system.

## Linking hooks.json to plugin.json

To activate the lifecycle system, reference [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) from your main [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) manifest using the `"hooks"` field. This indirection keeps the hook registry separate from the primary configuration.

```json
{
  "name": "My Awesome Plugin",
  "version": "0.1.0",
  "description": "Demonstrates lifecycle hooks",
  "hooks": "./hooks.json"
}

```

As seen in the repository structure for the **Replay.io** and **Figma** plugins (see [`plugins/replayio/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/replayio/.codex-plugin/plugin.json) and [`plugins/figma/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.codex-plugin/plugin.json)), this separation simplifies version control and allows automated tools to generate hook manifests independently of the main spec.

## File Organization and Repository Examples

Hook modules must reside inside the plugin directory. While conventional projects use a `hooks/` subdirectory, any relative path works.

Standard layout:

```

my-plugin/
├── .codex-plugin/
│   └── plugin.json
├── hooks/
│   ├── onLoad.js
│   ├── onEnable.js
│   └── onDisable.js
└── hooks.json

```

Real-world implementations in the **openai/plugins** repository demonstrate this pattern:

- **[`plugins/replayio/hooks.json`](https://github.com/openai/plugins/blob/main/plugins/replayio/hooks.json)**: Defines hooks for the Replay.io integration.
- **[`plugins/figma/hooks.json`](https://github.com/openai/plugins/blob/main/plugins/figma/hooks.json)**: Declares lifecycle handlers for the Figma plugin.

These files illustrate how production plugins centralize lifecycle logic outside their core skill code.

## Implementing Hook Handlers

Below are practical implementations for common lifecycle tasks.

### Initializing Shared Resources (onLoad)

Use `onLoad` to create reusable clients and store them on `context.state` for access in later hooks.

```javascript
export async function handler({ config, logger, state }) {
  logger.info('onLoad: initializing HTTP client');
  const baseUrl = config.apiBaseUrl;
  const apiKey = config.secrets.API_KEY;
  
  state.http = new HttpClient({ 
    baseUrl, 
    headers: { Authorization: `Bearer ${apiKey}` } 
  });
}

```

### Starting Background Jobs (onEnable)

Start polling or scheduled tasks when the plugin becomes active.

```javascript
export async function handler({ state, logger }) {
  logger.info('onEnable: starting background poller');
  
  const poll = async () => {
    try {
      const resp = await state.http.get('/events');
      // Process events...
    } catch (e) {
      logger.error('poll error', e);
    }
  };
  
  state.poller = setInterval(poll, 10000);
}

```

### Graceful Shutdown (onDisable)

Clean up resources to prevent memory leaks and orphaned connections.

```javascript
export async function handler({ state, logger }) {
  logger.info('onDisable: stopping background poller');
  clearInterval(state.poller);
  state.poller = null;
}

```

### Handling Updates (onUpdate)

Migrate data when the plugin version changes.

```javascript
export async function handler({ config, logger }) {
  const storedVersion = await readVersionFromDisk();
  if (storedVersion !== config.version) {
    logger.info(`onUpdate: migrating from ${storedVersion} → ${config.version}`);
    await runMigration(storedVersion, config.version);
    await writeVersionToDisk(config.version);
  }
}

```

## Testing and Debugging Hooks

Verify hook behavior through unit and integration tests.

**Unit Testing**: Import the handler directly and invoke it with a mock `HookContext` object containing spies for `logger` and `state`.

**Integration Testing**: Use the `plugin-creator` skill located at `.agents/skills/plugin-creator/` to scaffold a test harness. Enable and disable the plugin programmatically, asserting that side effects (file writes, network calls) occur as expected.

**Debugging Tips**:

- Write to `context.logger.info()` or `context.logger.error()`—these entries appear in the platform's debug console.
- If hooks never execute, verify that [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) correctly points to [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) and that all file paths in [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) resolve relative to the plugin root.
- Remember that hooks run **once per plugin instance**; hot-reloading triggers `onUpdate` but not `onLoad`.

## Summary

- **[`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json)** is an optional manifest that maps lifecycle events to JavaScript handler modules.
- The platform supports five hooks: `onLoad`, `onEnable`, `onDisable`, `onUnload`, and `onUpdate`.
- Each handler receives a `HookContext` containing `pluginId`, `config`, `logger`, and `state`.
- Reference [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) from [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) using the `"hooks"` field to activate the system.
- Examples in `plugins/replayio/` and `plugins/figma/` demonstrate production-ready configurations.

## Frequently Asked Questions

### What happens if a hook throws an error?

The platform catches the exception, logs it to the diagnostic console, and continues execution. A failing hook does not prevent the plugin from loading or other hooks from running, making them safe for non-critical operations.

### Can I use TypeScript for hook handlers?

Yes. The platform resolves the relative paths in [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) regardless of file extension. Ensure your TypeScript files compile to JavaScript before the plugin loads, or use a runtime transpiler if the environment supports it.

### How do I share data between different hooks?

Store values on the `context.state` object provided to every handler. This object persists across the lifecycle of the plugin instance, allowing you to initialize an HTTP client in `onLoad` and reference it in `onEnable` or `onDisable`.

### Where is the formal specification for the hooks field?

The complete JSON schema is documented in [`.agents/skills/plugin-creator/references/plugin-json-spec.md`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/references/plugin-json-spec.md) within the repository. This file defines the structure of [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json), including the `"hooks"` field that links to your [`hooks.json`](https://github.com/openai/plugins/blob/main/hooks.json) manifest.