Compare two DOCX documents headlessly using the Node.js or Python SDK. The output is a new .docx file with all differences rendered as tracked changes.
Workflow
- Open the base document (
Doc1) and the target document (Doc2) in separate sessions
- Capture a diff snapshot from the target session
- Compare the base session against that snapshot
- Apply the diff onto the base session as tracked changes
- Save the result as a new file (
Doc3)
Tracked diff apply requires a user identity. Set user on the SDK client so tracked changes are attributed correctly.
Node.js
import { SuperDocClient } from '@superdoc-dev/sdk';
const client = new SuperDocClient({
user: { name: 'Review Bot', email: 'bot@example.com' },
});
await client.doc.open({
sessionId: 'base',
doc: './Doc1.docx',
});
await client.doc.open({
sessionId: 'target',
doc: './Doc2.docx',
});
const targetSnapshot = await client.doc.diff.capture({
sessionId: 'target',
});
await client.doc.close({
sessionId: 'target',
});
const diff = await client.doc.diff.compare({
sessionId: 'base',
targetSnapshot,
});
await client.doc.diff.apply({
sessionId: 'base',
diff,
changeMode: 'tracked',
});
await client.doc.save({
sessionId: 'base',
out: './Doc3.docx',
force: true,
});
await client.doc.close({
sessionId: 'base',
});
Python
import asyncio
from superdoc import AsyncSuperDocClient
async def main():
async with AsyncSuperDocClient(
user={"name": "Review Bot", "email": "bot@example.com"}
) as client:
await client.doc.open({
"sessionId": "base",
"doc": "./Doc1.docx",
})
await client.doc.open({
"sessionId": "target",
"doc": "./Doc2.docx",
})
target_snapshot = await client.doc.diff.capture({
"sessionId": "target",
})
await client.doc.close({
"sessionId": "target",
})
diff = await client.doc.diff.compare({
"sessionId": "base",
"targetSnapshot": target_snapshot,
})
await client.doc.diff.apply({
"sessionId": "base",
"diff": diff,
"changeMode": "tracked",
})
await client.doc.save({
"sessionId": "base",
"out": "./Doc3.docx",
"force": True,
})
await client.doc.close({
"sessionId": "base",
})
asyncio.run(main())
What this produces
Doc3 is based on Doc1, not Doc2
- Body edits from
Doc2 are replayed onto Doc1 as tracked changes
- Comments, styles, and numbering changes are replayed directly
- The output stays as a reviewable redline until someone accepts or rejects the tracked changes
Current v1 limits
- Header and footer content is not diffed in v1
- The diff payload is opaque and intended for replay, not semantic inspection
diff.apply checks that the current base document fingerprint still matches the diff’s baseFingerprint
If the base document changes after diff.compare and before diff.apply, re-run the compare step against the current base document state.
See also