Build Custom Versioning with EmDash’s Revisions API

EmDash's Revisions API turns versioning into a programmable layer for custom workflows, not just an undo history.

By Central
EmDash's Revisions API enables custom versioning with raw data access, programmatic creation, and webhook triggers.
Highlights
  • EmDash's Revisions API exposes raw revision data including timestamps, author, content diff, and status changes.
  • You can create revisions programmatically without a human editor saving, enabling automated workflows.
  • Custom versioning with EmDash supports compliance, content scheduling, multi-step approvals, and A/B testing in under 200 lines of code.

Versioning in most CMSs is an afterthought — a history log you rarely use and even more rarely automate. EmDash flips that completely. Its Revisions API was built for programmatic control, not just audit trails. Most developers overlook this power, treating revisions as a safety net rather than a foundation for workflows. By the end of this guide, you’ll see revisions as a programmable layer that can power custom approval chains, content scheduling, and collaborative editing — not just a place to undo mistakes.

What Makes EmDash’s Revisions API Different?

WordPress stores revisions as serialized post meta, tightly coupled to the editor. EmDash treats revisions as first-class data objects. Every content type — posts, pages, custom types — automatically generates revision records on each save. But the API exposes far more than a simple list of past versions.

Every revision is data you can use — not just data you can undo.

The Revisions API gives you:

  • Raw access to revision data — timestamps, author, content diff, status changes
  • Programmatic creation — you can generate a revision without a human editor saving
  • Rollback endpoints — revert to any revision in a single call
  • Webhook triggers — every revision event fires a hook that plugins and workers can listen to

These capabilities let you build versioning systems that WordPress could never support natively.

Why Build Custom Versioning?

Out-of-the-box versioning solves “oops, I need to undo that.” Custom versioning solves real business problems:

  • Compliance — prove exactly who changed what and when, with immutable audit trails
  • Content scheduling — create revisions that become active on a future date
  • Multi-step approvals — a draft moves through editorial stages, each stage creating a revision
  • A/B testing — compare two revision branches and deploy the winner

EmDash’s API makes each of these possible in under 200 lines of code.

Prerequisites

Before you start, you need:

  • An EmDash instance (local or deployed) — version 0.1.0 or later
  • An API token with content:writecodecodecode and content:readcodecodecode permissions
  • Node.js 18+ or a runtime that supports fetch

Generate your token in EmDash admin under Settings → API Tokens. Scope it to only what your custom versioning system needs.

Step 1: Understanding the Revision Data Structure

Every content revision in EmDash is stored as a JSON object with this shape:

“`json

{

“id”: “rev_abc123”,

“documentId”: “doc_xyz789”,

“version”: 5,

“data”: { … },

“diff”: { … },

“author”: { “id”: “user_1”, “name”: “Jane” },

“createdAt”: “2026-04-10T14:23:00Z”,

“metadata”: { “reason”: “Updated pricing section” }

}

“`

The datacodecodecode field contains the full content snapshot at that revision. The diffcodecodecode field holds a structured diff (added, removed, changed paths). metadatacodecodecode is a free-form object you can populate when creating a revision programmatically.

Expected outcome: You understand exactly what the API returns and can map it to your own data structures.

Step 2: Fetching Revisions for a Document

To get all revisions for a specific piece of content, use the endpoint:

“`

GET /api/revisions?documentId=doc_xyz789&limit=50

“`

The response is paginated. Include cursorcodecodecode for the next page.

“`javascript

async function getRevisions(documentId, limit = 50) {

const res = await fetch(https://your-site.com/api/revisions?documentId=${documentId}&limit=${limit}codecodecode, {

headers: { Authorization: Bearer ${API_TOKEN}codecodecode }

});

return res.json();

}

“`

You can filter by author, date range, or status change. For example, to find all revisions where the status changed to “published”:

“`

GET /api/revisions?documentId=doc_xyz789&statusChange=published

“`

Expected outcome: You can retrieve any revision set and filter programmatically.

Step 3: Creating a Custom Diff Viewer

The built-in EmDash admin shows a basic diff. But you can build a richer comparison using the diffcodecodecode field. The diff is stored as a JSON patch (RFC 6902). Here’s a minimal renderer:

“`javascript

function renderDiff(diff) {

return diff.map(op => {

if (op.op === ‘replace’) {

return

${op.path}: ${op.value}

codecodecode;

}

if (op.op === ‘add’) {

return

+ ${op.path}: ${op.value}

codecodecode;

}

if (op.op === ‘remove’) {

return

- ${op.path}

codecodecode;

}

return ”;

}).join(‘n’);

}

“`

This gives you a side-by-side or inline diff that you can embed in a custom editorial dashboard, a Slack webhook, or an email notification.

Expected outcome: A reusable diff component that works outside the admin panel.

Step 4: Implementing Programmatic Rollback

Rolling back to a previous revision is a single API call:

“`

POST /api/documents/doc_xyz789/rollback

Body: { “revisionId”: “rev_abc123” }

“`

But you should add safeguards. Here’s a safe rollback function that checks permissions and logs the action:

“`javascript

async function rollbackToRevision(documentId, revisionId, reason) {

// Validate the revision exists

const rev = await getRevision(revisionId);

if (!rev) throw new Error(‘Revision not found’);

// Execute rollback

const res = await fetch(https://your-site.com/api/documents/${documentId}/rollbackcodecodecode, {

method: ‘POST’,

headers: {

Authorization: Bearer ${API_TOKEN}codecodecode,

‘Content-Type’: ‘application/json’

},

body: JSON.stringify({

revisionId,

metadata: { reason, rolledBackBy: currentUser }

})

});

if (!res.ok) throw new Error(‘Rollback failed’);

// The rollback itself creates a new revision, so you can audit it

return res.json();

}

“`

Expected outcome: A production-ready rollback function that leaves an audit trail.

Step 5: Automating Revision Pruning

Revisions accumulate. EmDash doesn’t auto-delete old ones (yet). Build a cleanup worker that runs daily:

“`javascript

// Cloudflare Worker cron trigger: 0 0 *

async function pruneRevisions(env) {

const thirtyDaysAgo = Date.now() – 30 24 60 60 1000;

// Fetch all documents, then for each, delete revisions older than 30 days

const docs = await fetch(‘https://your-site.com/api/documents’, {

headers: { Authorization: Bearer ${env.API_TOKEN}codecodecode }

}).then(r => r.json());

for (const doc of docs) {

const revisions = await getRevisions(doc.id);

const oldRevisions = revisions.filter(r => new Date(r.createdAt) < thirtyDaysAgo);

for (const rev of oldRevisions) {

await fetch(https://your-site.com/api/revisions/${rev.id}codecodecode, {

method: ‘DELETE’,

headers: { Authorization: Bearer ${env.API_TOKEN}codecodecode }

});

}

}

}

“`

Expected outcome: Automated cleanup that prevents database bloat without losing recent history.

Comparison: Versioning Strategies

Approach Storage Rollback Speed Audit Trail External Access AI-Friendly
EmDash Revisions API JSON in D1/SQLite Instant (single API call) Full metadata, author, reason Yes, via API Yes (MCP server reads diff)
Git-based versioning Git repository Minutes (clone + checkout) Commit messages only Requires git access Limited (no structured diff)
Manual database snapshots SQL dump Hours (restore entire DB) None unless logged No No

The EmDash approach wins for speed, granularity, and programmability.

Advanced: Connecting Revisions to AI Agents

EmDash ships with a built-in MCP server. You can expose your custom versioning system as a tool. For example, an agent can:

  • “Roll back the homepage to the version from last Tuesday”
  • “Show me all revisions where Jane made changes to the pricing table”
  • “Create a new revision of the about page with these updated team photos”

The agent reads the revision data through MCP and performs actions via the API. This turns your versioning system into an automated editorial assistant.

When the General Advice Fails

Custom versioning works well for structured content — blog posts, pages, product descriptions. It breaks down for highly dynamic content like real-time collaborative documents or user-generated content streams. In those cases, the revision API’s snapshot model creates too much overhead. You’d be better off with operational transforms (OT) or CRDTs. But for 90% of content management use cases, EmDash’s Revisions API is the right foundation.

Build your custom system. Test it in the playground. And when you deploy, remember: every revision is data you can use — not just data you can undo.

Questions answered
  • What makes EmDash's Revisions API different from WordPress?EmDash treats revisions as first-class data objects, not serialized post meta. Every content type automatically generates revision records on each save.
  • What can you build with custom versioning?You can build compliance audit trails, content scheduling, multi-step approval chains, and A/B testing systems.
  • What are the prerequisites for using the Revisions API?You need an EmDash instance version 0.1.0 or later, an API token with content:write and content:read permissions, and Node.js 18+.
Share This Article