---
title: 'CorePlugin & headless'
description: "Server-side document manipulation with CorePlugin and DocumentAgent. Run template processing and headless edits without a browser or the editor UI."
category: "Plugins"
order: 73
seoTitle: 'CorePlugin & headless API'
---

## What is the headless API?

The plugin system spans two npm entry points:

```text
@eigenpal/docx-editor-react/plugin-api   → browser editor plugins (React, needs DOM)
@eigenpal/docx-editor-core/headless      → headless API (Node.js, no DOM needed)
```

The headless API gives you `DocumentAgent`, parsers, serializers, and template processing: everything you need to manipulate DOCX files programmatically in Node.js. No browser, no React, no ProseMirror.

```ts
import { DocumentAgent, parseDocx, processTemplate } from '@eigenpal/docx-editor-core/headless';
```

**CorePlugins** extend the headless API with custom command handlers. They're the server-side equivalent of EditorPlugins.

## When to use the headless API

- **API routes**: fill templates, generate documents server-side
- **CI/CD pipelines**: validate templates, extract variables
- **Node.js scripts**: batch-process DOCX files
- **Server-side agents**: programmatic document manipulation

If you need UI panels, overlays, or ProseMirror decorations, use an [EditorPlugin](/docs/1.x/plugins/editor-plugins) instead.

## DocumentAgent

`DocumentAgent` wraps a `Document` in a chainable, immutable API for headless reads and edits:

```ts
import { readFile } from 'node:fs/promises';
import { DocumentAgent } from '@eigenpal/docx-editor-core/headless';

const buf = await readFile('template.docx');
const agent = await DocumentAgent.fromBuffer(buf);
const filled = await agent.applyVariables({ customer_name: 'Jane Doe', date: '2026-07-01' });
const out = await filled.toBuffer();
```

The full surface (parsing, text extraction, mutation, serialization) is covered in [Headless DOCX processing](/docs/1.x/guides/headless-processing).

### In a Next.js API route

```ts
// app/api/fill-template/route.ts
import { processTemplate } from '@eigenpal/docx-editor-core/headless';

export async function POST(req: Request) {
  const formData = await req.formData();
  const file = formData.get('file') as File;
  const variables = JSON.parse(formData.get('variables') as string);

  const buf = await file.arrayBuffer();
  const filled = processTemplate(buf, variables);

  return new Response(filled, {
    headers: {
      'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    },
  });
}
```

### Template processing utilities

```ts
import {
  processTemplate,
  getTemplateTags,
  validateTemplate,
} from '@eigenpal/docx-editor-core/headless';

// Get all template variables from a DOCX
const tags = getTemplateTags(buf);
// → ['name', 'date', 'total']

// Validate template syntax
const result = validateTemplate(buf);
// → { valid: boolean, errors: TemplateError[], tags: string[] }

// Fill template with string values
const filled = processTemplate(buf, {
  name: 'Jane Doe',
  date: '2026-07-01',
  total: '$9.99',
});
```

For loops and nested data, use `processTemplateAdvanced(buf, data)` from the same entry point.

## CorePlugin interface

CorePlugins extend `DocumentAgent` with custom command handlers:

```ts
interface CorePlugin {
  id: string;
  name: string;
  version?: string;
  description?: string;
  commandHandlers?: Record<string, CommandHandler>;
  mcpTools?: McpToolDefinition[];
  initialize?: () => void | Promise<void>;
  destroy?: () => void | Promise<void>;
  dependencies?: string[];
}
```

### Fields

| Field             | Required | Description                                  |
| ----------------- | -------- | -------------------------------------------- |
| `id`              | Yes      | Unique identifier                            |
| `name`            | Yes      | Human-readable name                          |
| `version`         | No       | Semver version string                        |
| `description`     | No       | Short description                            |
| `commandHandlers` | No       | Map of command type → handler function       |
| `mcpTools`        | No       | MCP tool definitions exposed via the registry |
| `initialize`      | No       | Called once during registration              |
| `destroy`         | No       | Cleanup on unregistration                    |
| `dependencies`    | No       | IDs of plugins that must be registered first |

## Command handlers

A command handler is a pure function that receives a `Document` and a command, then returns a new `Document`:

```ts
type CommandHandler = (doc: Document, command: PluginCommand) => Document;

interface PluginCommand {
  type: string;
  id?: string;
  position?: Position;
  range?: Range;
  [key: string]: unknown;
}
```

Example: a plugin that adds watermark text:

```ts
import type { Document } from '@eigenpal/docx-editor-core';
import type { CorePlugin, PluginCommand } from '@eigenpal/docx-editor-core/core-plugins';

const watermarkPlugin: CorePlugin = {
  id: 'watermark',
  name: 'Watermark',
  commandHandlers: {
    addWatermark(doc: Document, cmd: PluginCommand) {
      const text = (cmd as { text: string }).text;
      // ..transform doc to add a watermark header
      return doc;
    },
  },
};
```

Use it:

```ts
import { pluginRegistry } from '@eigenpal/docx-editor-core/core-plugins';

pluginRegistry.register(watermarkPlugin);

const handler = pluginRegistry.getCommandHandler('addWatermark');
if (handler) {
  const newDoc = handler(doc, { type: 'addWatermark', text: 'DRAFT' });
}
```

## PluginRegistry

The global `pluginRegistry` manages all CorePlugins:

```ts
import { pluginRegistry } from '@eigenpal/docx-editor-core/core-plugins';

// Register
pluginRegistry.register(myPlugin);

// Query
pluginRegistry.has('watermark'); // true
pluginRegistry.getAll(); // CorePlugin[]
pluginRegistry.getCommandTypes(); // ['addWatermark']

// Unregister
pluginRegistry.unregister('watermark');

// Batch registration
import { registerPlugins } from '@eigenpal/docx-editor-core/core-plugins';
registerPlugins([pluginA, pluginB]);
```

## Reference implementation: docxtemplater plugin

The built-in `docxtemplaterPlugin` in `packages/core/src/core-plugins/docxtemplater/` is a full reference:

- **Command handlers**: `insertTemplateVariable`, `replaceWithTemplateVariable`
- **Lazy dependency validation**: `processTemplate` checks for `docxtemplater`/`pizzip` at call time

```ts
import { pluginRegistry, docxtemplaterPlugin } from '@eigenpal/docx-editor-core/core-plugins';

pluginRegistry.register(docxtemplaterPlugin);

// Now DocumentAgent can dispatch insertTemplateVariable commands
```

Note: there is also a separate **EditorPlugin** for template UI (`packages/react/src/plugins/template/`) that handles syntax highlighting and the annotation panel in the browser. The two systems are independent but complement each other; a single feature can span both.

## Next steps

- [EditorPlugin API](/docs/1.x/plugins/editor-plugins): browser-side UI plugins
- [Examples & cookbook](/docs/1.x/plugins/examples): advanced patterns
- [Headless DOCX processing](/docs/1.x/guides/headless-processing): the full DocumentAgent and parser surface
