Comparing is the core operation: one base document against one or more revisions, producing one true redline per revision. The comparison engine is deterministic — it produces the exact set of changes between two versions, preserving footnotes, tables, numbering, and formatting.
create_comparison
Stages one or more comparisons from local or attached files and returns upload targets. Every parameter is optional in the schema; the combinations are validated server-side.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
base_file_path | string | — | Path to the base (earlier) document |
base_file_name | string | — | Display name for the base, extension included |
base_file_id | string | — | Reuse a stored document as the base — a find_prior_version match's latest_version.file_id. No base file is uploaded |
base_document_chain_id | string | — | Reuse a document family's latest version as the base |
auto_resolve_base | boolean | true | With a single revision and no base, let the server resolve the base from stored prior versions. Pass false to require an explicit base |
comparison_file_path | string | — | Path to one revised document |
comparison_file_paths | string[] | — | Paths to several revised documents, each compared against the same base |
comparison_file_name | string | — | Display name for a single revision |
comparison_file_names | string[] | — | Display names, in the same order as the paths |
version_story_name | string | — | Name for a new project |
version_story_id | string | — | Add to an existing project instead of creating one |
comparison_groups | object[] | — | Several base-plus-revisions groups in one call. Each holds base_file_path, comparison_file_paths, base_file_name, comparison_file_names. Replaces the top-level base and comparison fields |
The *_name fields are display names; the bytes are identified by path.
Choosing a base
There are four ways to establish the base document, in descending order of explicitness:
- Upload it —
base_file_path. - Reuse a stored file —
base_file_idfrom afind_prior_versionmatch. Only the revision uploads. - Reuse a document family —
base_document_chain_iduses that family's latest version. - Let the server resolve it — pass a single revision with no base at all. The server finds the stored prior version whose family confidently matches the file name. If none matches, the call errors and you must pass a base explicitly.
The response's base field always states which of these happened, via
base.source: uploaded, stored_prior_version, or auto_resolved.
When an uploaded base's file name confidently matches a stored document family, the
comparison is created inside that family's existing project rather than a new one, so
versions of a document accumulate in one place. The response's version_story_name and
version_story_url tell you where it landed.
Response
{
"status": "awaiting_uploads",
"message": "...",
"version_story_id": "uuid",
"version_story_name": "Acme NDA",
"version_story_url": "https://app.versionstory.com/version-story/<id>",
"upload_manifest_url": "https://... (signed, 1 hour)",
"uploads": [
{
"role": "base",
"file_name": "nda_v1.docx",
"content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"http_method": "PUT",
"upload_url": "https://mcp-compare.versionstory.com/transfer",
"authorization": "..."
}
],
"staged_file_names": ["nda_v1.docx", "nda_v2.docx"],
"comparisons": [{ "version_mapping_id": "uuid", "redline_url": "https://..." }],
"base": { "source": "uploaded", "file_name": "nda_v1.docx" }
}uploads is inlined and also fetchable from upload_manifest_url — either is sufficient.
Redline generation starts automatically as the uploads land.
Transferring files
Each upload is one HTTP PUT of the raw file bytes. The upload_url is a Version Story
endpoint, not third-party storage, and the entry's authorization and content_type must
be passed verbatim.
curl -sS --fail -X PUT \
--data-binary @"nda_v1.docx" \
-H "Authorization: <authorization>" \
-H "Content-Type: <content_type>" \
"<upload_url>"Use --data-binary, never -d — the latter corrupts binary content. A 200 means the
transfer succeeded.
Manifest URLs need no headers, and downloads are saved as binary:
curl -s "<manifest_url>"
curl -sS --fail -o "<file_name>" "<download_url>"Transfers are capped at 100 MB per file, with a 300-second timeout and at most 16
concurrent uploads server-wide — a saturated server returns 503 with Retry-After: 1.
create_comparison_from_links
The same operation for documents already reachable at HTTPS URLs. The server fetches them
itself, so there is no upload step and the response comes back processing rather than
awaiting_uploads.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
base_file_url | string | Yes | HTTPS URL of the base document |
comparison_file_url | string | — | HTTPS URL of one revised document |
comparison_file_urls | string[] | — | URLs of several revisions, each compared against the same base |
base_file_name | string | — | Inferred from the URL when omitted |
comparison_file_name | string | — | Display name for a single revision |
comparison_file_names | string[] | — | Display names, in the same order as the URLs |
version_story_name | string | — | Name for a new project |
version_story_id | string | — | Add to an existing project |
slack_team_id | string | — | Slack workspace id (starts with T) for private files.slack.com URLs |
Only HTTPS is accepted, and the resolved address must be publicly routable — loopback, private, and link-local addresses are refused, and the check is repeated on every redirect (at most three). Fetches time out at 30 seconds and are capped at 100 MB. An HTML response is rejected rather than treated as a document.
Private Slack URLs need slack_team_id and are fetched using the caller's organization's
Slack connection; the Version Story Slack app must be installed in that workspace and
invited to the channel.
get_redlines
Waits server-side for redlines and returns a download manifest. Safe to call repeatedly with identical arguments.
| Parameter | Type | Required | Default | Meaning |
|---|---|---|---|---|
version_story_id | string | Yes | — | The project holding the comparisons |
version_mapping_id | string | — | — | One comparison |
version_mapping_ids | string[] | — | — | Several comparisons |
download_options | string[] or string | — | The comparison's previewable redline | Which formats to return |
Formats
| Value | Produces |
|---|---|
pdf_redline | PDF redline — the default for document comparisons |
word_redline | Word document with tracked changes |
pdf_changed_pages_only | PDF containing only the pages that changed |
"all" | Every available format |
Note this differs from the REST API, whose format defaults to docx. Left unset here,
download_options yields one viewable file, pdf_redline.
Response — ready
{
"status": "ready",
"version_story_id": "uuid",
"download_manifest_url": "https://... (signed, 1 hour)",
"comparisons": [
{
"version_mapping_id": "uuid",
"status": "ready",
"downloads": [{ "option": "pdf_redline", "file_name": "Redline v1 to v2.pdf" }],
"pending_options": [],
"text_url": "https://... (signed, 1 hour)",
"text_file_name": "Redline v1 to v2.md"
}
]
}Once the overall status is ready, the per-file download_url values live only in the
manifest — fetch download_manifest_url to get them.
A comparison can report failed for some formats while others succeed; anything listed in
the manifest is still valid. Failed comparisons carry failed_options and a
failure_code, and waiting again will not change the outcome.
Reading the changes
Ready comparisons may also carry text_url and text_file_name: a markdown rendering of
the same redline with the changes marked inline. Use it when you want to analyse the
differences rather than hand a file to a person — it is far more reliable than re-deriving
the changes from the source documents.
The rendering marks changes with these elements:
| Element | Meaning |
|---|---|
<ins author="..." revision="..." date="..."> | Text inserted |
<del author="..." revision="..." date="..."> | Text deleted from the base version |
<moved-from move-id="1"> / <moved-to move-id="1"> | The two halves of one relocation, tied by a shared move-id |
<conflict id="1"> | Adjacent changes from different revisions competing over the same text |
Text outside those elements is unchanged. author is who made the change and revision is
the document version it came from. See Redline format
for the self-describing header every file opens with, worked examples of each element, and
how nesting and escaping behave.
text_url is omitted for spreadsheet comparisons and when the rendering is unavailable.
word_redline carries the same set of tracked changes.
Base corrections
A comparison whose base was auto-resolved may come back with a base_correction field. It
means the document was read after staging and its content disagreed with the base its file
name suggested. The redline already returned is unchanged and still valid; when
corrected_version_mapping_id is present, it names a second comparison against the base the
content points at, which you can fetch with the same tool.
find_prior_version
Searches the user's stored document families by file name, so you can compare against a version already in Version Story instead of asking someone to re-upload it.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
file_name | string | — | The document's file name, extension included |
file_path | string | — | Path to the new document; only the basename is used |
limit | integer | 5 | Maximum matching families to return |
Matching is by name similarity, so the name as the user knows it is enough. Each match is a
document family with a confidence score, its project, and its version history. Pass the
chosen match's latest_version.file_id to create_comparison as base_file_id, or its
document_chain_id as base_document_chain_id. No match means the prior version is not
stored and must be uploaded.
detach_from_prior_versions
Undoes a wrong automatic document-family link — the same template used for a different matter, for example.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
version_story_id | string | Yes | The project holding the comparison |
version_mapping_id | string | Yes | The comparison whose documents were mis-linked |
The comparison and its redline are untouched; only family membership is removed. A stored prior version used as the base keeps its own membership. This is the one destructive tool in the set.