Skip to main content
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

  1. Open the base document (Doc1) and the target document (Doc2) in separate sessions
  2. Capture a diff snapshot from the target session
  3. Compare the base session against that snapshot
  4. Apply the diff onto the base session as tracked changes
  5. 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