Redline API Quickstart

Compare HTML, Markdown, plain text, JSON, DOCX, or searchable PDF. Get an HTML redline, structured JSON changes, or both. DOCX also supports optional Word Track Changes. Document diffs compare text and supported styles. Optional rendered pages show source layout; DOCX fonts and pagination can differ from Word. Images are displayed, but not compared. Scanned PDFs need OCR.

Send two strings to the content comparison endpoint. These examples use anonymous access within quota; authenticated API calls add the Bearer header described below. The response contains the detected changes.

Python

Install the requests package, then run:

import requests

response = requests.post(
    "https://api.bookcicle.com/v1/redline/html",
    json={"original": "One year.", "revised": "Two years."},
    timeout=30,
)
response.raise_for_status()
print(response.json())

curl

curl https://api.bookcicle.com/v1/redline/html \
  -H 'Content-Type: application/json' \
  -d '{"original":"One year.","revised":"Two years."}'

JavaScript

const response = await fetch("https://api.bookcicle.com/v1/redline/html", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ original: "One year.", revised: "Two years." })
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
console.log(await response.json());

Base URL

https://api.bookcicle.com/v1

Authentication

Anonymous callers can receive HTML comparisons within the existing anonymous size, rate, and usage limits. Exports require a signed-in user or a valid API key. Anonymous callers cannot create export jobs or obtain export download URLs. A private comparison or viewer token does not authorize exports.

All authenticated requests require a Bearer token in the Authorization header:

Authorization: Bearer bc_live_0123456789abcdef0123456789abcdef

You can generate an API key in the API Keys Dashboard. Never share or commit your secret key.

Send your Bearer token and JSON content type. Signed upload URLs include their own authorization.

Payload signing: For direct document requests larger than 1 MiB of serialized JSON, clients must also send x-amz-content-sha256 containing the lowercase SHA-256 digest of the complete request body. Browsers and client SDKs supply this automatically.

Request headers
HeaderWhen requiredValue / behavior
AuthorizationAuthenticated API calls; required for exportsBearer <API_KEY_OR_ACCESS_TOKEN>. Private comparison GETs can use their returned viewer token; export jobs and download URLs require authentication.
Content-TypeJSON POST requestsapplication/json. Presigned uploads use the content type supplied when requesting the upload.
AcceptDocument streaming requeststext/event-stream for POST /v1/redline/documents; application/json for JSON API responses.
Idempotency-KeyOptional: document comparisonReuse the same key when retrying the same input comparison.
X-Viewer-TokenOptional alternative on private comparison GETsUse the comparison token when a token query parameter is not supplied.

Content Comparison API (Format-Agnostic)

Compare HTML, Markdown, plain text, or JSON in one synchronous request. Additions use <ins>; deletions use <del>.

POST /v1/redline/html
Content-Type: application/json

{
  "original": "<p>The fox jumped over the fence.</p>",
  "revised": "<p>The quick fox jumped over the old fence.</p>",
  "mode": "auto",
  "format": "html",
  "output": ["html"]
}

Choose output: ["html"] (default), ["json"], or ["html", "json"]. These select fields in the response object.

Content comparison request
FieldTypeRequired / defaultMeaning
originalstring or upload referenceRequiredBaseline content, or {"uploadId":"..."} for staged inputs.
revisedstring or upload referenceRequiredRevised content, or {"uploadId":"..."} for staged inputs.
formatstring"auto"html, markdown / md, txt / plain, json, or auto. Markdown becomes canonical HTML; JSON source is canonicalized and key-sorted.
modestring"auto"auto, word (inline words), or block (larger text segments).
outputarray of strings["html"]["html"], ["json"], or ["html", "json"]. Pass "hosted_viewer" in the output array to receive a secure embed token and hosted comparison URL (comparisonId, viewerUrl, embedUrl, and viewerToken; aliases: viewer, embed). These select object fields, not an array response.
hostedViewerTtlstring or integer"24h"Adjustable availability and token validity window for hosted embeds (when "hosted_viewer" is in output). Creator tier supports "24h" (default) or "32h". Creator Pro supports "24h" (default), "32h", "48h", or "72h". Integer hours (24, 32, 48, 72) or seconds (e.g. 86400) are also accepted.

Pass "hosted_viewer" in the output array to receive a secure embed token and hosted comparison URL. See Hosted Viewer & Embeds.

JSON changes refer to the output HTML. See Structured JSON Contract, for the output schema and HTML location mapping.

Content comparison response
FieldTypePresenceMeaning
schemaVersionstringAlwaysredline.html.v1 for HTML-only; redline.comparison.v1 when JSON is requested.
htmlstringHTML requestedHTML redline fragment for rendering; omitted for JSON-only.
cssUrlURL stringAlwaysPublic Redline v1 stylesheet for the current environment. Load once in your rendering document.
jsonobjectJSON requestedFirst page of structured changes; see the JSON tables below.
changesUrlURL stringJSON requestedPrivate pagination endpoint; preserve its token. Relative URLs resolve against the API origin.
requestedMode / effectiveModestringAlwaysRequested and selected text diff modes.
format / engine / fallbackstring / string / booleanAlwaysEffective input format; dom / text engine; whether fallback was used.
unitsintegerAlwaysComparison’s metered Redline units.
usageobjectAlwaysused, allowance, remaining, and plan for the account.
comparisonIdstringStored comparisonIdentifier for stored comparison retrieval.
viewerUrl / embedUrlURL stringStored HTML availableBrowser review and embed pages, including private access tokens. These are viewer pages, not raw HTML download URLs.
viewerTokenstringStored HTML availablePrivate comparison access token; treat as a credential.

Small text comparisons can reuse a caller-scoped result for 60 minutes when inputs and options match. Cache hits do not consume additional units; simultaneous first requests may both be charged. Document retries use Idempotency-Key instead.

Response

{
  "schemaVersion": "redline.html.v1",
  "cssUrl": "https://dev.redline.bookcicle.com/styles/v1/redline.css",
  "html": "<p>The <ins>quick </ins>fox jumped over the <ins>old </ins>fence.</p>",
  "requestedMode": "auto",
  "effectiveMode": "word",
  "format": "html",
  "engine": "dom",
  "fallback": false,
  "units": 1,
  "usage": {
    "used": 142,
    "allowance": 25000,
    "remaining": 24858
  }
}

Structured JSON Contract

Changes are anchored to the comparison’s HTML representation for every input format. The engine collects changes before serializing HTML; text fallback uses its diff segments.

Request ["html", "json"] for rendering and structured changes. JSON-only output still refers to the HTML comparison representation. Pass "hosted_viewer" in the output array to receive a secure embed token and hosted comparison URL.

What locations refer to

Locations describe normalized HTML, not source coordinates. They do not provide PDF page numbers or bounding boxes, Word revision IDs, JSON Pointer paths, byte offsets, or table-cell coordinates.

Structured change fields
FieldType / presenceMeaning
idstring; alwaysDocument-order change ID, such as chg_000001. Stable across pages of the same comparison. They are not HTML element IDs, DOM selectors, or identifiers guaranteed to survive a new comparison.
typestring; alwaysinsert, delete, replace, format, link, block_insert, block_delete. Adjacent deletion/insertion groups can form a replacement.
location.blockIndexinteger; alwaysBlock counter in traversal of the HTML comparison representation. DOM blocks start at 1; 0 can indicate a change outside a recognized block. Not a source paragraph number or character offset.
location.blockTypestring; optionalHTML block type, such as p or h1. Unavailable in text fallback.
location.headingPath / sectionTitlearray of strings / string; optionalSurrounding headings and nearest heading title in the HTML representation. Empty context is omitted.
before / afterobject; conditionalInsertions omit before; deletions omit after.
before.text / after.textstring; on present fragmentsChanged text, usable by agents without parsing HTML.
before.html / after.htmlstring; on present fragmentsHTML of the changed content, not a full source document.
before.format / after.formatobject; optionalSemantic or presentation formatting when available.
before.href / after.hrefstring; optionalLink destination when available; omitted in text fallback.

Use fragment text and heading context to summarize edits. Request HTML too when rendering a redline. Applying changes to the original file requires your own source mapping.

Structured JSON page fields
FieldType / presenceMeaning
schemaVersionstring; always"1"; independent of the outer API schema version.
comparisonIdstring; optionalStored comparison identifier.
format / engine / fallback / attributeModemetadata; alwaysInput format and engine behavior. Locations still refer to HTML representation.
summaryobject; alwaysWhole-comparison counts: changeCount, insertions, deletions, replacements, formatChanges, linkChanges, blockInsertions, blockDeletions.
changesarray; alwaysChanges on the current page.
totalChangesinteger; alwaysNumber of changes in the complete comparison.
offset / limitinteger; alwaysZero-based page offset; default limit 100, maximum 500.
nextOffsetinteger; optionalNext page offset; omitted when there are no further changes.

Example: both outputs (response excerpt)

{
  "cssUrl": "https://dev.redline.bookcicle.com/styles/v1/redline.css",
  "html": "<h1>Terms</h1><p>Pay within <del>30</del><ins>45</ins> days.</p>",
  "json": {
    "schemaVersion": "1",
    "comparisonId": "cmp_example",
    "format": "html",
    "engine": "dom",
    "fallback": false,
    "attributeMode": "semantic",
    "summary": {
      "changeCount": 1,
      "insertions": 0,
      "deletions": 0,
      "replacements": 1,
      "formatChanges": 0,
      "linkChanges": 0,
      "blockInsertions": 0,
      "blockDeletions": 0
    },
    "changes": [{
      "id": "chg_000001",
      "type": "replace",
      "location": {
        "blockIndex": 2,
        "blockType": "p",
        "headingPath": ["Terms"],
        "sectionTitle": "Terms"
      },
      "before": { "text": "30", "html": "30" },
      "after": { "text": "45", "html": "45" }
    }],
    "totalChanges": 1,
    "offset": 0,
    "limit": 100
  },
  "changesUrl": "https://api.bookcicle.com/v1/redline/comparisons/cmp_example/changes?token=EXAMPLE_PRIVATE_TOKEN"
}

Metadata, fallback, and pagination

Text fallback omits heading, block-type, formatting, and link context. Use before.text and after.text.

Follow nextOffset by GETting changesUrl with &offset=<nextOffset>&limit=100. Keep its private token. No nextOffset means the last page; the summary covers the whole comparison.

DOCX/PDF return this schema in content_complete.contentDiff.json, with contentDiff.changesUrl. Word Track Changes uses separate revision IDs and counts.

Documents API (DOCX & PDF Early Access)

Direct small-document input

For DOCX or PDF pairs up to 750 KiB combined file bytes, send both files as base64 in the comparison request. This skips upload authorization and file transfer. Larger pairs use upload IDs. Both sources must use the same input method.

{
  "format": "pdf",
  "original": { "dataBase64": "<base64 original file bytes>", "contentType": "application/pdf" },
  "revised": { "dataBase64": "<base64 revised file bytes>", "contentType": "application/pdf" },
  "mode": "auto",
  "output": ["html", "json"],
  "metrics": true
}

Word exports require authentication. Anonymous callers may request an HTML comparison within quota. If an anonymous request also asks for track_changes, the stream emits track_changes_failed with code export_authentication_required, then continues the HTML comparison and normal completion without an export job. Supply an authenticated Bearer token to create an export or retrieve its download URL. Replaying a cached anonymous comparison does not restore export access.

Upload two DOCX files or two searchable PDFs, then stream progress with SSE. Request HTML, JSON, or both; JSON changes use the same native comparison as the HTML response. Native Word Track Changes is default off and DOCX-only. Output fields and options are listed below.

Document comparison request
FieldTypeRequired / defaultMeaning
formatstringRequireddocx or pdf; both files must use the same format.
original.uploadId / revised.uploadIdstringRequired for uploaded inputsUpload IDs from the upload endpoint. Legacy originalUploadId / revisedUploadId aliases are also accepted.
original.dataBase64 / revised.dataBase64stringRequired for direct inputsBase64 raw file bytes; use both sources together instead of upload IDs. Public limit: 750 KiB combined raw bytes.
original.contentType / revised.contentTypestringOptional for direct inputsDeclared MIME type must match the selected document format.
modestring"auto"auto / word / block for the HTML comparison engine.
attributeModestring"semantic"semantic / presentation for HTML attribute comparison.
outputarray of strings["html"]["html"], ["json"], ["html", "json"]. Pass "hosted_viewer" in the output array to receive a secure embed token and hosted comparison URL (contentDiff.comparisonId, contentDiff.hostedUrl; alias: viewer). Native Word Track Changes is default off; pass "track_changes" in the output array to queue background generation of a .docx file with native Word tracked revisions.
pageRenderingbooleanfalseReturn source-page PDFs after the text diff. Applies to both formats; omission and false skip all page rendering.
metricsbooleanfalseSet true to include server processing metrics in content_complete and complete, including totalMs and downloadMs.
titlestringOptionalDisplay title for stored comparisons.
Document content_complete event
FieldType / presenceMeaning
requestId / formatstring; alwaysRequest identifier and source document format.
unitsinteger; alwaysComparison’s metered units.
contentDiff.htmlstring; HTML requestedHTML rendered from the semantic document diff.
contentDiff.cssUrlURL string; alwaysSame rendering contract as text API cssUrl; current environment’s public v1 CSS.
contentDiff.jsonobject; JSON requestedSame structured JSON page schema as the text API.
contentDiff.changesUrlURL string; JSON requestedPrivate pagination URL; relative paths resolve against the API origin.
contentDiff.engine / fallbackstring / boolean; alwaysComparison engine and fallback status.
contentDiff.comparisonId / hostedUrlstring / URL; stored outputStored comparison identifier and private retrieval URL. hostedUrl retrieves JSON containing HTML; it is not a raw HTML page.
contentDiff.earlyAccessboolean; optionalEarly Access status when supplied.

Why the HTML redline and Word Track Changes can differ

The semantic document diff supplies HTML; Word exports compare the original DOCX pair independently.

Grouping, counts, and revision IDs may differ. mode does not configure the Word Track Changes comparator. Review each output against the source files.

Rendered source pages

Set pageRendering: true for page previews. The stream sends content_complete first, then page_rendering_complete with private PDF URLs, each side’s page count, the renderer and renderMs. URLs expire after 15 minutes; stored artifacts expire after 24 hours.

event: page_rendering_complete
data: {"schemaVersion":"redline.pages.v1","renderer":"<renderer identifier>","renderMs":210,"pages":[{"side":"original","pdfUrl":"<private URL>","pageCount":2,"expiresInSeconds":900},{"side":"revised","pdfUrl":"<private URL>","pageCount":3,"expiresInSeconds":900}]}

PDF previews preserve source bytes, page sizes and rotation. DOCX preview layout depends on available fonts; exact Word pagination is not guaranteed. The UI displays one page per side and highlights unambiguous text changes. The text comparison contains every supported change, including repeated text and style edits.

Review actions: Rendered pages are read-only. Use the interactive HTML review viewer for Accept/Reject, or review native DOCX Track Changes in Word. Review decisions do not update the source-page previews.

page_rendering_failed leaves the successful text diff available. Rendering is bounded to 1,000 pages, 32 MiB per PDF and 30 seconds of DOCX layout per side, within a 55-second request budget. Document comparison screens enable pages; the playground starts off and offers a checkbox.

Stream completion: Require both content_complete and complete; HTTP 200 only opens the stream. error means comparison failure. track_changes_failed and page_rendering_failed affect only their optional outputs.

Metrics: Use content_complete.metrics.totalMs for the semantic result and complete.metrics.totalMs for all requested work. comparisonMs measures server time through the semantic result and stays unchanged in final completion. pageRenderingMs measures optional page preparation and storage, including failed attempts; it is zero before rendering or when disabled. diffMs measures only the diff engine. renderMs in a successful page event measures page preparation and storage. Direct inputs report downloadMs: 0; upload transfer and browser painting are excluded.

Upload and stream example
  1. For the upload path, presign and upload both documents via POST /v1/redline/uploads.
  2. Stream comparison via SSE with upload references:
    POST /v1/redline/documents
    Authorization: Bearer bc_live_...
    Content-Type: application/json
    Accept: text/event-stream
    
    {
      "format": "docx",
      "original": {
        "uploadId": "upl_orig_123"
      },
      "revised": {
        "uploadId": "upl_rev_456"
      },
      "mode": "auto",
      "output": ["html", "track_changes"]
    }
  3. Receive ordered real-time SSE progress events with self-contained job polling URLs:
    event: accepted
    data: {"requestId":"req_abc"}
    
    event: validating
    data: {"format":"docx"}
    
    event: converting
    data: {"side":"original"}
    
    event: converting
    data: {"side":"revised"}
    
    event: comparing
    data: {"mode":"auto"}
    
    event: content_complete
    data: {"requestId":"req_abc","format":"docx","contentDiff":{"html":"...","engine":"document","fallback":false},"units":2}
    
    event: track_changes_queued
    data: {"jobId":"job_789","status":"queued","pollUrl":"https://api.bookcicle.com/v1/redline/jobs/job_789","artifactType":"track_changes_docx"}
    
    event: complete
    data: {"requestId":"req_abc","status":"complete","jobs":[{"type":"track_changes_docx","jobId":"job_789","pollUrl":"https://api.bookcicle.com/v1/redline/jobs/job_789"}],"jobId":"job_789","units":2}

Performance & Expectations

Live DEV measurements cover native DOCX/PDF → HTML/JSON and HTML, Markdown, TXT and JSON snippets. Upload cases use files already uploaded; direct/inline cases submit source bytes. DOCX sizes are nominal fixture labels; PDF counts describe source pages.

Expected Server Processing

FixtureInputExpected Server ProcessingPage RenderingEngine
HTML snippetinline~72 msN/Adom
TXT snippetinline~73 msN/Adom
MD snippetinline~73 msN/Adom
JSON snippetinline~76 msN/Adom
Small DOCX agreement fixtureupload~132 ms~158 msdocument
Small DOCX agreement fixturedirect~71 ms~158 msdocument
DOCX 10 nominal-page fixtureupload~139 ms~264 msdocument
DOCX 10 nominal-page fixturedirect~78 ms~264 msdocument
DOCX 50 nominal-page fixtureupload~245 ms~853 msdocument
DOCX 50 nominal-page fixturedirect~173 ms~853 msdocument
DOCX 250 nominal-page fixtureupload~490 ms~6393 msdocument
DOCX 250 nominal-page fixturedirect~401 ms~6393 msdocument
PDF 2 pagesupload~159 ms~109 msdocument
PDF 2 pagesdirect~76 ms~109 msdocument
PDF 10 pagesupload~177 ms~114 msdocument
PDF 10 pagesdirect~101 ms~114 msdocument
PDF 50 pagesupload~278 ms~116 msdocument
PDF 250 pagesupload~966 ms~209 msdocument
PDF 500 pagesupload~2019 ms~398 msdocument

Typical warm processing estimates for uncached comparisons. Page Rendering is optional server preparation, excludes browser drawing and is additional to comparison time. Rendering estimates apply to the same fixture across input methods.

What the timings include

Published server timings use metrics.totalMs and exclude upload authorization, file transfer, network transit and browser rendering. They include source retrieval, parsing, comparison, HTML/JSON response preparation and normal request work. Baseline rows skip optional page preparation.

Comparisons bypass caches: snippets require Cache-Control: no-store and server confirmation; documents omit idempotency keys. Authentication caching remains enabled. These fixture results are not a promise for every document under 10 pages. Density, cold starts, structure and outputs affect timing.

Measure client completion separately. Its difference from server processing includes transport, buffering and client handling; it is not a measurement of network time alone.

The full report includes larger/denser inputs, concurrent requests and failed qualifications. Failed output gets no timing claim. Benchmark report · Raw evidence · Page-flow validation.

Capability Matrix

Capability Word DOCX Searchable PDF Plain Text / Markdown / JSON
Searchable text & content comparison Yes Yes Yes
Punctuation & word-level diffing Yes Yes Yes
Source document structure No guarantee No guarantee No guarantee
Text inside tables When extracted When extractable As text
Source formatting preservation No guarantee No guarantee No guarantee
Hyperlink destination changes (href) When preserved by conversion Not guaranteed Markdown links
Native Word Track Changes export (.docx) Yes (Async worker) No (HTML diff only) No
Scanned / Image-only PDF (OCR) No (Rejected) No (Rejected as non-text) N/A
Visual / Pixel / Layout comparison No No No
Deterministic large-document fallback Yes Yes Yes

What Redline Compares vs. What It Excludes

Redline draws a principled boundary between semantic document revisions and cosmetic rendering artifacts:

  • Compared: Words, numbers, symbols, punctuation, paragraph boundaries, headings, bullet/numbered lists, blockquotes, table contents, semantic inline tags (<strong>, <em>, <u>, <s>, <sup>, <sub>, <mark>), and hyperlink URLs.
  • Excluded: Font family typography (e.g. Arial vs Calibri), font size, font color, background shading, margin dimensions, page size (Letter vs A4), orientation, multi-column flows, line spacing, dynamic page break shifts, decorative headers/footers, and embedded shapes/drawing canvases.

Deterministic Complexity Fallbacks

When document structural complexity exceeds the DOM processing envelope (>512 KiB HTML, >1,500 block elements, or >12,000 total tags), the engine automatically falls back to a linear text-stream comparison. This preserves complete change detection for text, numbers, punctuation, and paragraphs in tens of milliseconds, keeping complex comparisons within processing limits.

Operational Limits

LimitValue
Direct DOCX/PDF input750 KiB combined. Larger pairs use uploads.
Upload size10 MiB combined, uncompressed. Without signing in: 5 MiB per file.
Billing unit100 KiB (102,400 bytes) of combined content.
Server timeout60 seconds.
ZIP safeguards100:1 maximum expansion ratio; 1,000 entries maximum.

Large File Uploads

For text formats, the inline limit is 800 KiB (819200 bytes) for the complete UTF-8 JSON request, including escaping, field names, and options. Measure the serialized request, not just the document text. Larger comparisons use uploads.

  1. Request POST /v1/redline/uploads for each input with filename, contentType, and its UTF-8 sizeBytes. Text inputs use text/plain; charset=utf-8. Each response returns uploadId and uploadUrl.
  2. PUT the raw input bytes to each uploadUrl, using the same content type. Limits are 5 MiB per anonymous input and 10 MiB per authenticated input. Use the same identity to authorize uploads and compare them.
  3. For HTML, Markdown, JSON, or plain text, call POST /v1/redline/html with upload references. Keep the same mode, format, and output options. DOCX and PDF continue to use POST /v1/redline/documents.
{
  "original": { "uploadId": "upl_12345678-1234-4234-9234-123456789abc" },
  "revised": { "uploadId": "upl_87654321-4321-4321-9321-abcdef123456" },
  "mode": "word",
  "format": "html",
  "output": ["html"]
}

For text formats, check the complete serialized request against the 800 KiB limit. For PDF/DOCX, choose direct input up to 750 KiB combined raw file bytes and uploads above that threshold. Upload IDs belong to the identity that created them; they are not arbitrary storage paths.

Hosted Viewer & Embeds

Redline can generate a hosted, access-controlled text comparison viewer or provide responsive iframe embed codes without needing to build custom review UI in your application.

Pass "hosted_viewer" in the output array to receive a secure embed token and hosted comparison URL:

<iframe
  src="https://redline.bookcicle.com/embed/cmp_9f82d1?token=cmp_tok_abc123&theme=system&collapse=true"
  width="100%"
  height="600"
  loading="lazy"
  style="border:1px solid #e2e8f0; border-radius:8px;"
></iframe>
Supported iframe query parameters
ParameterDescription
theme=system|light|darkForces color scheme.
view=unified|splitSets initial layout.
collapse=true|falseCollapses or expands long unchanged text runs (defaults to true).
controls=falseSuppresses viewer top navigation chrome.
bg=transparentRemoves background fill for seamless container embedding.
review=true|falseEnables interactive track changes review with inline Accept/Reject buttons and progress tracking (Creator & Creator Pro).

Interactive Track Changes Review Mode (Creator & Creator Pro)

Redline enables interactive change-by-change review inside embedded viewers. End-users can accept or reject individual insertions and deletions or bulk-resolve changes using Accept All and Reject All. Every action dispatches bidirectional window.postMessage events to your host application.

Unified Layout Lock: When Review Mode is active (review=true or via redline:startReview), the viewer is automatically locked into Unified layout. The Split layout option is explicitly disabled in the UI and via postMessage commands during active review to preserve synchronous inline Accept/Reject action buttons, change boundaries, and DOM hierarchy. Split mode re-enables once review is completed or exited.

Review Completion & Clean Block Pruning: When resolving changes, rejected insertions (or accepted deletions) that span entire list items (<li>) or paragraphs (<p>) automatically prune empty container nodes so no orphaned bullets or blank lines remain. Once all changes are reviewed one-by-one, Accept All and Reject All are dismissed leaving Finish Review as the sole action, alongside a floating bottom-right completion prompt ensuring users never miss the final export step.

Bidirectional PostMessage Protocol

Listen for review callbacks in your host container:

window.addEventListener("message", (event) => {
  const { type, ...payload } = event.data || {};

  if (type === "redline:changeResolved") {
    // Fired whenever an individual change is accepted or rejected
    console.log(`Change ${payload.changeId} ${payload.action}ed. Remaining: ${payload.remaining} of ${payload.total}`);
  } else if (type === "redline:reviewComplete") {
    // Fired when all changes are resolved or Finish Review is clicked
    console.log("Review complete! Final resolved HTML:", payload.resolvedHtml);
    console.log("Accepted changes:", payload.accepted);
    console.log("Rejected changes:", payload.rejected);
  }
});

Host applications can also programmatically resolve changes and manage viewer layout:

// Start review mode
iframe.contentWindow.postMessage({ type: "redline:startReview" }, "https://redline.bookcicle.com");

// Accept or reject a specific change ID
iframe.contentWindow.postMessage({ type: "redline:acceptChange", changeId: "chg-1" }, "*");
iframe.contentWindow.postMessage({ type: "redline:rejectChange", changeId: "chg-2" }, "*");

// Bulk resolve remaining changes
iframe.contentWindow.postMessage({ type: "redline:acceptAll" }, "*");
iframe.contentWindow.postMessage({ type: "redline:rejectAll" }, "*");

// Query live review state
iframe.contentWindow.postMessage({ type: "redline:requestState" }, "*");

// Change viewer appearance
iframe.contentWindow.postMessage({ type: "redline:setView", view: "split" }, "*");
iframe.contentWindow.postMessage({ type: "redline:setTheme", theme: "dark" }, "*");
iframe.contentWindow.postMessage({ type: "redline:setCollapse", collapsed: false }, "*");

Drop-in Redline Embed SDK (/scripts/v1/redline.js)

New subscriber-created private viewer links allow guest browser review without a reviewer account. The service supplies the review entitlement after validating the viewer link.

Review choices stay in the browser. The completion callback provides resolved HTML for your application to save or process. It does not save a Redline draft or change Word downloads or page previews.

Rather than writing raw <iframe> tags and manual postMessage listeners, include Redline's lightweight SDK script:

<script src="https://redline.bookcicle.com/scripts/v1/redline.js"></script>
<div id="redline-mount" style="min-height: 560px;"></div>

<script>
  const viewer = Redline.embed({
    container: "#redline-mount",
    comparisonId: "cmp_demo_marketing",
    review: true,            // Enable interactive track changes review
    theme: "system",         // "system" | "light" | "dark"
    view: "unified",         // "unified" | "split"
    onChangeResolved: (evt) => {
      console.log(`Change ${evt.changeId} ${evt.action}ed (${evt.remaining} remaining)`);
    },
    onComplete: (evt) => {
      console.log("Final approved document HTML:", evt.resolvedHtml);
      // Persist or commit the approved document to your database
    }
  });

  // Programmatic controls:
  // viewer.accept("chg-1");
  // viewer.reject("chg-2");
  // viewer.acceptAll();
  // viewer.setTheme("dark");
  // viewer.destroy();
</script>

Hosted Embed Availability & Expiration (Adjustable TTL)

Pass "hosted_viewer" in the output array to receive a secure embed token and hosted comparison URL. Hosted viewer tokens and stored comparison files are intentionally ephemeral, designed for interactive diff review, approval gates, and PR previews. You can customize the availability window using the hostedViewerTtl parameter:

Hosted Embed Availability & Lifecycle Limits
PropertySpecificationDetails
token_ttl24h default (up to 72h)The signed embed token (cmp_tok_<timestamp>_<signature>) expires 24 hours after issuance by default, or after your selected TTL. Requests beyond this duration return token_expired.
creator_ttl24h, 32h optionsCreator accounts can configure hostedViewerTtl to "24h" (default) or "32h".
creator_pro_ttl24h, 32h, 48h, 72h optionsCreator Pro accounts can configure hostedViewerTtl to "24h" (default), "32h", "48h", or "72h" for extended multi-day review workflows.
storage_lifecycleMatches token TTLStored HTML diffs and JSON changes remain accessible for the duration of the token TTL, scheduled for automated expiration cleanup thereafter.
expiration_uiInline expired stateExpired comparison links return HTTP 404 and present a clear notice: Hosted viewer tokens expire after the configured TTL. Stored comparisons are scheduled to expire after the retention period.
clock_skew60 seconds toleranceTokens timestamped more than 60 seconds into the future are rejected (token_issued_in_future) to prevent forged timestamps.
demo_comparisonsPermanent / No expirationPublic sample comparisons (demo, sample, cmp_demo*) bypass token verification and never expire, useful for dev testing.

Requesting a Hosted Comparison with Custom TTL

Pass output: ["html", "hosted_viewer"] and optional hostedViewerTtl (e.g. "32h" for Creator, or up to "72h" for Creator Pro):

// Request a hosted comparison with extended 48-hour availability (Creator Pro)
const response = await fetch("https://api.bookcicle.com/v1/redline/html", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.REDLINE_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    original: originalHtml,
    revised: revisedHtml,
    output: ["html", "hosted_viewer"],
    hostedViewerTtl: "48h" // "24h" (default), "32h" (Creator); "48h", "72h" (Creator Pro)
  })
});

const data = await response.json();
// data returns:
// - data.comparisonId: "cmp_a1b2c3d4e5f6..."
// - data.viewerToken:  "cmp_tok_1740000000_abc123..." (HMAC-SHA256, active for 48h)
// - data.embedUrl:     "https://redline.bookcicle.com/embed/cmp_...?token=cmp_tok_..."
// - data.viewerUrl:    "https://redline.bookcicle.com/view/cmp_...?token=cmp_tok_..."

Long-Term Retention & Re-Hosting Strategies

  • Permanent Archival: If your application needs long-term or audit-ready diff retention, store the returned html fragment and structured json changes directly in your own database or object storage. Pair with Redline’s public, versioned stylesheet (https://redline.bookcicle.com/styles/v1/redline.css).
  • Re-issuing Embed Links: To generate a new 24-hour viewer link for a past comparison, simply re-post the original and revised documents or upload IDs with output: ["html", "hosted_viewer"]. Redline’s sub-100ms comparison engine generates a fresh 24-hour token instantly.

CSS Styling Contract

Redline HTML returns presentation-neutral markup with semantic tags. To use our default theme, link the stylesheet:

<link rel="stylesheet" href="https://redline.bookcicle.com/styles/v1/redline.css">

Load the returned cssUrl once in your rendering document. Document SSE uses contentDiff.cssUrl; the field is also present for JSON-only results.

Render the HTML fragment in an isolated preview. HTML fragments do not contain a stylesheet link; hosted viewers already load it.

The stylesheet is public and environment-specific. Viewer and artifact URLs are private; keep their returned tokens.

Or override specific selectors:

ins { background: #dcfce7; color: #166534; text-decoration: none; }
del { background: #fee2e2; color: #991b1b; text-decoration: line-through; }

Units & Quota Calculation

Text usage is metered by UTF-8 input bytes; PDF/DOCX usage counts raw file bytes:

units = ceil((original_bytes + revised_bytes) / 102400)

1 unit = 100 KiB combined raw input bytes; base64 encoding overhead is not billed. Direct PDF/DOCX pairs support 750 KiB combined raw file bytes; text-format inline requests support 800 KiB serialized JSON. Larger inputs use uploads. Document comparisons remain capped at 10 MiB combined; each anonymous file is capped at 5 MiB.

Error Codes

CodeHTTPDescription
export_authentication_required401 on export and job endpointsSupply a valid authenticated Bearer token to export. In a document comparison stream, this code appears in a nonfatal track_changes_failed event; the HTML result and normal completion remain available within quota.
unauthorized401Missing or invalid authentication.
invalid_api_key401Key does not exist or has invalid prefix.
api_key_revoked403Key has been explicitly revoked.
quota_exceeded402Monthly plan allowance depleted.
rate_limited429Requests per minute limit exceeded.
inline_limit_exceeded413JSON request > 819200 bytes. Upload inputs and send upload IDs.
document_too_large413Document exceeds 10 MiB maximum.
document_too_complex422DOM tag or block density guard triggered.

Troubleshooting payload signatures: If a direct request over 1 MiB fails with an authorization or signature mismatch error, ensure the x-amz-content-sha256 header contains the lowercase hex SHA-256 digest computed over the exact UTF-8 request body bytes.

Rate Limits

API requests are rate-limited based on your active plan tier:

PlanRate LimitMax Document Size
Basic30 requests / minute800 KiB JSON
Creator120 requests / minute5 MiB
Creator Pro300 requests / minute10 MiB

When exceeded, endpoints return HTTP 429 Too Many Requests. Rate-limit headers vary by endpoint; anonymous viewer and job requests can be limited before authentication errors are returned. Honor Retry-After when supplied. If absent, use bounded backoff; do not depend on every response containing x-ratelimit-limit, x-ratelimit-remaining, or x-ratelimit-reset.