Skip to content

Files

Upload a document once, then download it as a PDF, as Word, as embeddable HTML or as its listing, with or without its metadata.

View as Markdown

A file is one immutable document held by Version Story, named by a file_id. Uploads, a comparison's redline, an edit's results and created documents are all files, and every endpoint that reads a document takes its file_id. Every response that carries a document carries it as the same File object, the one GET /v1/files/{file_id} answers with.

Base URLhttps://api.versionstory.com
AuthAuthorization: Bearer vs_.... See Authentication

POST /v1/files

multipart/form-data with one file field, document: a .docx, .doc or .pdf to read, edit or show, or a .xlsx or .pptx to compare with another of its kind.

curl -sS https://api.versionstory.com/v1/files \
  -H "Authorization: Bearer vs_live_..." \
  -F "document=@nda.docx"
{ "file_id": "3f2a9c1e-...", "status": "processing" }
  • A .docx answers at once while it is prepared in the background. processing needs no polling of its own: every request that reads the file waits for it.
  • A .doc or .pdf is converted to Word first. The request waits up to 20 seconds and answers ready, failed with an error, or processing if conversion is still running. A redline PDF's insertions and deletions become tracked changes; send the optional field track_redline_changes=false to keep them as formatting. See Redline PDFs.
  • A workbook or presentation is stored as it is and answers ready. It can be compared, never read or edited.

GET /v1/files/{file_id}

Signed downloads for one file. The pdf shows the file's own tracked changes and comments as a redline: it is what to show a person.

ParameterInRequiredDefaultMeaning
file_idpathYes—The file's id
formatqueryNodocxOne or more of pdf, html, docx, md, json. Repeatable or comma-separated. A workbook or presentation is served as it is (xlsx, pptx)
clean_metadataqueryNo—standard or strict: serve every format with its metadata removed. See Clean metadata
clean_metadata_revisionsqueryNothe preset'sWith clean_metadata: keep, accept or reject tracked changes
clean_metadata_commentsqueryNothe preset'sWith clean_metadata: keep or remove comments
clean_metadata_authorsqueryNothe preset'sWith clean_metadata: keep or anonymize the names on changes and comments
searchqueryNo—md only: return only the paragraphs holding a phrase. See Reading the listing
offset, max_charsqueryNo—md only: read the listing in parts. See Reading the listing
formatIs
pdfThe document as a PDF, with its tracked changes and comments shown. The view to show a person
htmlThe document as one self-contained HTML file, to frame where the view must be interactive. See Embed redlines
docxThe Word file
mdThe document's listing as Markdown: its text, each paragraph's locator, and its tracked changes. What edits are written from, and what a model reads. See Edit
jsonThe same listing as structured data, for code. See Document JSON
curl -sS "https://api.versionstory.com/v1/files/3f2a9c1e-...?format=pdf,docx" \
  -H "Authorization: Bearer vs_live_..."
{
  "file_id": "3f2a9c1e-...",
  "file_name": "nda.docx",
  "status": "processing",
  "downloads": [
    {
      "format": "docx",
      "url": "https://documents.versionstory.com/...",
      "file_name": "nda.docx",
      "expires_at": "2026-10-22T18:00:00Z"
    }
  ],
  "pending_formats": ["pdf"]
}

status is the state of the formats this request asked for: processing while one is still being made (the response carries Retry-After; poll again), ready when every one is in downloads, and failed when the document cannot be used or a requested format will never be made. The first request for a PDF or HTML starts making it. A format that failed is named in failed_formats, and a file whose source could not be read at all answers "status": "failed" with an error.

Signed links are valid for one hour, and polling again mints fresh ones. A workbook or presentation has no pdf, html, md or json, and asking for one is 400 UNSUPPORTED_FORMAT.

Reading the listing

The md listing of a long document can be large. Two ways to read only part of it, both for md alone:

  • search, repeated for several phrases (?format=md&search=Closing&search=36%20months). Once the listing is ready, the response adds markdown, holding only the paragraphs that contain a phrase, each after the lines that say where it is (its header, footer, note, table, row and cell). matches gives each phrase with the number of paragraphs holding it. A phrase matches as an edit's text selector does, ignoring case. The paragraphs stop at 48,000 characters, and omitted_paragraphs then counts those left out.
  • offset and max_chars read the listing in parts. markdown holds the part starting at offset (0, or the last response's next_offset) and ending at the last whole line that fits in max_chars. The response adds total_chars and, until the part reaches the end, next_offset.

search takes neither offset nor max_chars; sending them together, or with any format but md, is 400 INVALID_REQUEST.

Clean metadata

A document carries more than its text: who wrote and edited it, when, on which template and from which network folder, its comments and tracked changes, hidden text, and the earlier revisions a PDF keeps. Add clean_metadata to download a copy without it, to send outside the firm.

curl -sS "https://api.versionstory.com/v1/files/3f2a9c1e-...?format=pdf,docx&clean_metadata=standard" \
  -H "Authorization: Bearer vs_live_..."
PresetWhat it removes
standardWhat nobody sees on the page: document properties, people and account ids, revision ids, hidden text, document variables, mail merge sources, custom XML, printer settings, image metadata, and a PDF's XMP, earlier revisions and JavaScript. Tracked changes, comments and authors' names stay
strictEverything standard removes, and the review: tracked changes accepted, comments, PDF notes and speaker notes removed, authors anonymized, white or tiny text, hidden sheets and slides, and macros removed, and PDF form fields and stamps flattened into the page

The three choices override the preset: clean_metadata=standard&clean_metadata_comments=remove keeps tracked changes but removes comments. Each choice needs clean_metadata; one sent without it is 400 INVALID_REQUEST, so a request never gets the uncleaned document by mistake. clean_metadata cleans documents, not listings, so it does not apply to md, json or html.

Each download in the response is the cleaned copy, with a metadata_report:

{
  "format": "docx",
  "url": "https://documents.versionstory.com/...",
  "file_name": "nda.docx",
  "expires_at": "2026-10-22T18:00:00Z",
  "metadata_report": {
    "format": "docx",
    "preset": "standard",
    "items": [
      { "type": "author", "category": "properties", "count": 1, "action": "remove", "examples": [] },
      { "type": "comments", "category": "review", "count": 4, "action": "keep", "examples": [] }
    ],
    "warnings": [
      { "type": "document_numbers", "message": "A header or footer shows a document number or file path. It is visible text, so it was left in place." }
    ]
  }
}

items lists every kind of hidden data found and the action taken: what was removed, reset or converted, and with keep or report, what was left in place. Show the warnings to whoever sends the document; they name what the cleaner cannot decide for you, such as visible document numbers or text under a dark box that only looks redacted. Every type and what each preset does to it is in Metadata types.

A PDF that Version Story made from a Word file (a redline's, or an uploaded document's) draws its comments, their authors and its tracked changes onto the page, where no clean of the PDF can reach them. So its clean cleans the Word file with the same options and renders the PDF again: what the options remove from the Word file is gone from the PDF too. A comparison's redline is cleaned through this endpoint by its file.file_id.

A format whose clean is still running is in pending_formats, as a PDF still converting is; poll until it is ready. One that cannot be cleaned is in failed_formats, and its uncleaned original is never served in its place. Word, Excel, PowerPoint and PDF files are cleaned, and each copy is kept for the options that made it, so asking again is immediate.

POST /v1/files/{file_id}/acknowledge

For an organization with zero-day retention, deletes an uploaded file, a created document or a selective comparison's PDF as soon as you have saved what you need, instead of an hour after it was created. No body.

curl -sS -X POST https://api.versionstory.com/v1/files/3f2a9c1e-.../acknowledge \
  -H "Authorization: Bearer vs_live_..."
{ "file_id": "3f2a9c1e-...", "status": "deletion_queued" }

An edit's files are deleted with the edit: acknowledge those with POST /v1/edit/{edit_id}/acknowledge.

Errors

StatusCodeCause
400INVALID_REQUESTA missing or empty document; an unknown clean_metadata value, a choice sent without it, or clean_metadata with md, json or html; or search, offset or max_chars sent together or with a format other than md
400UNSUPPORTED_FILE_TYPEThe upload is not a .docx, .doc, .pdf, .xlsx or .pptx
400DOCUMENT_UNREADABLEThe upload is not a readable Word file: corrupt, or encrypted
400UNSUPPORTED_FORMATA format is unknown, or not offered for this file
401INVALID_API_KEYMissing, malformed, unknown, revoked, or expired key
402USAGE_LIMIT_REACHEDThe organization's monthly upload allowance is exhausted
404FILE_NOT_FOUNDNo such file, or it belongs to another organization
409CONFLICTAn acknowledgment of a file without zero-day retention, or of an edit's own file
410CONTENT_DELETEDThe file's content was deleted under zero-day retention
413FILE_TOO_LARGEThe upload exceeds the 100 MB limit

Kept for existing integrations

GET /v1/files/{file_id}/content answers the listing in the response body rather than as a download: markdown with format=md, document with format=json. It is deprecated, and marked so in the OpenAPI specification; read the md and json downloads above instead. Its document gives level the meaning the download calls list_level.