# Selective compare

> Compare a range of paragraphs in one document with a range in another, and get the result at once as JSON, Markdown and a PDF.

`POST /v1/compare/selective` compares a range of paragraphs in one document with a range in
another, or two ranges of the same document, such as one clause in two drafts. It answers in the
same response with the comparison as JSON and Markdown, and stores it as a new PDF file. It makes
no Word redline; to redline whole documents, use [Compare](/developers/rest/compare).

| | |
| --- | --- |
| Base URL | `https://api.versionstory.com` |
| Auth | `Authorization: Bearer vs_...`. See [Authentication](/developers/authentication) |

## The flow

1. **Upload** each document with [`POST /v1/files`](/developers/rest/files#post-v1files), or use
   the `file_id` of a file you have already uploaded.
2. **Pick the ranges.** Read each file with
   [`GET /v1/files/{file_id}/content`](/developers/rest/edit#get-v1filesfileidcontent) and note
   the `index` of the first and last paragraph of each range.
3. **Compare** with `POST /v1/compare/selective`. The response carries the comparison and its PDF
   file.

## POST /v1/compare/selective

`application/json`, with an `original` and a `modified` range. Each names its file and its first
and last paragraph, either by `index` or by paragraph `id`; one side can use indexes while the
other uses ids.

| Field | Required | Meaning |
| --- | --- | --- |
| `file_id` | Yes | The file holding the range |
| `start_paragraph_index`, `end_paragraph_index` | One pair | The range's first and last paragraph, as the file's content numbers them |
| `start_paragraph_id`, `end_paragraph_id` | One pair | Instead of the indexes: the paragraphs' ids from the content's JSON |

```bash
curl -sS https://api.versionstory.com/v1/compare/selective \
  -H "Authorization: Bearer vs_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "original": {"file_id": "3f2a9c1e-...", "start_paragraph_index": 12, "end_paragraph_index": 18},
    "modified": {"file_id": "8c41d7b2-...", "start_paragraph_index": 12, "end_paragraph_index": 19}
  }'
```

Ranges are inclusive and follow document order, including paragraphs inside tables. Each
paragraph is read with its tracked changes accepted, and Word's list numbering is compared apart
from the text. Paragraphs are lined up first, with identical paragraphs as anchors and the rest
paired by how similar their words are, and then each pair is compared word by word.

```json
{
  "comparison": {
    "schema_version": 1,
    "original": { "file_id": "3f2a9c1e-...", "file_name": "v1 NDA.docx", "paragraph_count": 7 },
    "modified": { "file_id": "8c41d7b2-...", "file_name": "v2 NDA.docx", "paragraph_count": 8 },
    "summary": { "unchanged": 5, "modified": 2, "inserted": 1, "deleted": 0 },
    "paragraphs": [
      {
        "status": "modified",
        "original_id": "...",
        "modified_id": "...",
        "original_number": "2.",
        "modified_number": "2.",
        "content": [
          { "text": "through no fault" },
          { "type": "insertion", "text": ", action, or omission" },
          { "text": " of the Recipient;" }
        ]
      }
    ]
  },
  "markdown": "...",
  "file": {
    "file_id": "b4056e05-...",
    "file_name": "Selective Compare - v1 NDA to v2 NDA.pdf",
    "status": "ready",
    "downloads": [
      {
        "format": "pdf",
        "url": "https://documents.versionstory.com/...",
        "file_name": "Selective Compare - v1 NDA to v2 NDA.pdf",
        "expires_at": "2026-10-19T19:30:00+00:00"
      }
    ]
  }
}
```

- **`comparison.paragraphs`** lists the lined-up paragraphs in reading order. `status` is
  `unchanged`, `modified`, `inserted` (with only `modified_id`) or `deleted` (with only
  `original_id`). A content segment without `type` is unchanged text: skip the insertions to
  read the original, and the deletions to read the modified.
- **`markdown`** is the same result with changes marked by `<ins>` and `<del>`, each paragraph
  opened by `<p original-unid="..." modified-unid="..."/>`.
- **`file`** is a new file holding the same result as a PDF, the one the Selective Compare panel
  exports in the Version Story app: inserted text blue and underlined, deleted text red and struck
  through, each paragraph on its own line after its list number.

### The PDF file

The PDF is written before the response is sent, so `file` is always `ready`. Its download URL is
valid for one hour; [`GET /v1/files/{file_id}`](/developers/rest/files#get-v1filesfileid) with
its `file_id` mints a fresh one. It is served as `pdf` only, and it cannot be compared, edited or
read as content. For an organization with
[zero-day retention](/developers/reference/data-handling#retention), it is deleted an hour after
it is made, or as soon as you call
[`POST /v1/files/{file_id}/acknowledge`](/developers/rest/files#post-v1filesfileidacknowledge).

Each request makes a new file, so send it once and keep the `file_id`. It counts toward your
usage as a document with the words of the compared text; downloading its PDF, then or later,
does not count again.

## Errors

| Status | Code | Cause |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | A range named both ways or neither, a start after its end, or a file without paragraphs to compare (a redline, a created document, another selective comparison's PDF) |
| 400 | `PARAGRAPH_NOT_FOUND` | A paragraph index or id is not in its file |
| 400 | `RANGE_TOO_LARGE` | The ranges are too large to compare in one request: more than 40,000 pairs of paragraphs between identical ones, or a comparison of more than 300,000 characters. Compare smaller ranges |
| 400 | `UNSUPPORTED_FILE_TYPE` | A workbook or presentation, which has no paragraphs |
| 401 | `INVALID_API_KEY` | Missing, malformed, unknown, revoked, or expired key |
| 404 | `DOCUMENT_NOT_FOUND` | No such file, or it belongs to another organization |
| 409 | `CONFLICT` | A file is still being prepared (retry after `Retry-After`), or its preparation failed (upload it again) |
| 410 | `CONTENT_DELETED` | A file's content was deleted under zero-day retention |

## Related

- [Compare](/developers/rest/compare): a Word redline of two whole documents
- [Files](/developers/rest/files): upload documents and download the comparison's PDF again
- [Data handling](/developers/reference/data-handling): what is kept, and for how long
