Skip to main content
This page covers the low-level headless Editor approach. For production AI agent workflows, use the Document Engine AI tools instead — they provide structured tool definitions, provider-ready schemas, and built-in dispatch.
SuperDoc can run headless in Node.js for server-side document processing, AI agent workflows, and batch automation.

Quick example

import { Editor } from 'superdoc/super-editor';
import OpenAI from 'openai';

const openai = new OpenAI();
const completion = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Write a service agreement in HTML.' }],
});

const editor = await Editor.open(templateDocx);
editor.commands.insertContent(completion.choices[0].message.content, {
  contentType: 'html',
});
const docx = await editor.exportDocx();

Content formats

editor.commands.insertContent(value, { contentType: 'html' });      // Recommended for LLMs
editor.commands.insertContent(value, { contentType: 'markdown' });
editor.commands.insertContent(value, { contentType: 'text' });
editor.commands.insertContent(value, { contentType: 'schema' });     // ProseMirror JSON
See Import/Export for format details, limitations, and guidance on choosing a format.

AI redlining with track changes

Use suggesting mode so AI edits appear as tracked changes that users can accept or reject:
const contract = await readFile('./contract.docx');
const editor = await Editor.open(contract, {
  documentMode: 'suggesting',
});

// AI suggestions are tracked — users review them in Word
editor.commands.insertContent(aiRevisions, { contentType: 'html' });
const redlined = await editor.exportDocx();

Programmatic block access

For multi-step workflows — extract block references, send them to an LLM, then apply edits — use the Document API. It returns stable block addresses that work across separate editor sessions:
// Find all paragraphs and their text
const result = editor.doc.find({
  select: { type: 'node', nodeType: 'paragraph' },
  includeNodes: true,
});

// Each item has an address with a best-effort stable nodeId
const blocks = result.items.map((item) => ({
  id: item.address.nodeId,     // usually stable across loads for unchanged DOCX content
  type: item.address.nodeType,
  text: item.node?.text,
}));

// Send blocks to LLM, get back edits, apply them
Block addresses from doc.find() prefer DOCX-native IDs (paraId) for imported blocks. This is the best available cross-session anchor, but no ID is guaranteed to survive all Word round-trips. Don’t read node.attrs.sdBlockId directly — it’s regenerated on every load.

LLM quick reference

Point your AI assistant at these URLs for SuperDoc context:
https://docs.superdoc.dev/llms.txt        // Quick reference
https://docs.superdoc.dev/llms-full.txt   // Complete documentation

Next steps