REST error envelope
Every non-2xx REST response has the same body. code is drawn from a closed vocabulary and
is what your code should branch on; message is human-readable and may change.
{
"error": {
"code": "UNSUPPORTED_FILE_TYPE",
"message": "original must be a .docx, .doc, or .pdf file",
"request_id": "01J..."
}
}Send an X-Request-Id header to correlate your logs with ours. If you don't, one is minted
and echoed back in the response.
Request errors
| Status | Code | Cause | Retry? |
|---|---|---|---|
| 400 | INVALID_REQUEST | Malformed request — missing, empty, or unnamed file field | No, fix the request |
| 400 | UNSUPPORTED_FILE_TYPE | Extension is not .docx, .doc, or .pdf | No |
| 400 | INVALID_COMPARISON_ID | The comparison id is malformed, or is a mrg_ merge id | No |
| 400 | INVALID_MERGE_ID | The merge id is malformed, or is a cmp_ comparison id | No |
| 400 | UNSUPPORTED_FORMAT | A requested format is unknown, or not valid for this operation | No |
| 401 | INVALID_API_KEY | Missing, malformed, unknown, inactive, or expired key | No, check the credential |
| 402 | USAGE_LIMIT_REACHED | The organization's monthly upload allowance is exhausted | No, it's a billing state |
| 403 | API_KEY_ORG_MISMATCH | The key's service account no longer belongs to the key's organization | No, issue a new key |
| 404 | COMPARISON_NOT_FOUND | No such comparison, or the key's user cannot access it | No |
| 404 | MERGE_NOT_FOUND | No such merge, or the key's user cannot access it | No |
| 413 | FILE_TOO_LARGE | A file exceeds the 100 MB limit | No |
| 500 | INTERNAL_ERROR | Something went wrong on our side | Yes, with backoff |
COMPARISON_NOT_FOUND and MERGE_NOT_FOUND intentionally cover both "doesn't exist" and
"not yours", so ids cannot be probed for existence.
INTERNAL_ERROR is the only code worth retrying blindly. Everything else in this table
describes a request or account condition that will produce the same result next time.
Comparison failures
A comparison that cannot be generated is not an HTTP error. The request succeeded — it
correctly reported that the work will not finish — so it returns 200 with
"status": "failed":
{
"comparison_id": "cmp_...",
"status": "failed",
"error": {
"code": "DOCUMENT_PROTECTED",
"upstream_code": "ONE_OR_MORE_DOCUMENTS_ARE_PROTECTED"
}
}error.code | Meaning | What to do |
|---|---|---|
DOCUMENT_PROTECTED | A source is password-protected or permission-restricted | Remove protection and resubmit |
DOCUMENT_UNREADABLE | A source could not be opened — corrupt or malformed | Check the file opens in Word |
DOCUMENT_CONVERSION_FAILED | A .doc, .pdf, or email source could not be converted to .docx | Convert it yourself and resubmit |
SOURCE_UPLOAD_FAILED | A source did not reach storage intact | Resubmit |
PDF_RENDER_FAILED | The redline generated but the PDF rendering did not | Request format=docx instead |
COMPARISON_FAILED | Catch-all — upstream_code carries the specific pipeline code | Inspect upstream_code; contact support if it recurs |
failed is terminal for that comparison. Waiting longer changes nothing; submit a new
comparison to retry.
MCP errors
The MCP server does not use HTTP status codes for tool outcomes. Failures surface two ways.
Tool errors — a call that cannot proceed raises an MCP tool error carrying a message. These are argument and permission problems: too few revisions for a merge, a project the signed-in user cannot access, an exhausted upload allowance. Read the message; it says what is wrong.
Statuses — a call that succeeded but whose artifact isn't ready reports it in the result:
| Status | Meaning | What to do |
|---|---|---|
ready | The artifact exists | Fetch download_manifest_url |
processing | Still generating | Call again with identical arguments |
failed | Terminal | Read failure_code or errors; do not retry |
awaiting_uploads | Sources were never transferred | Complete the PUTs listed in missing_uploads |
A comparison can be partly successful: some formats generate while others fail. Such a
comparison carries both downloads and failed_options with a failure_code. Anything
present in the manifest is valid and usable.
Transfer errors
The upload endpoint returns ordinary HTTP status codes:
| Status | Cause |
|---|---|
| 400 | Empty request body |
| 403 | Missing, invalid, or expired upload authorization |
| 408 | The upload exceeded the 300-second timeout |
| 413 | The body exceeded the 100 MB cap |
| 502 | Storage rejected the write |
| 503 | All 16 concurrent upload slots are busy — honor Retry-After: 1 |
A 403 most often means the upload manifest is more than an hour old. Call the create tool
again with the same version_story_id to stage fresh transfers.
If a transfer fails with a host-allowlist error rather than a status code, the calling
agent's network policy is blocking *.versionstory.com. For Claude, the fix is in
Settings → Capabilities — the illustrated steps are in
Claude Connector Setup.
Pipeline failure codes
failure_code and upstream_code come from the document pipeline and are more specific
than the public codes above. The ones you are most likely to see:
| Code | Meaning |
|---|---|
ONE_OR_MORE_DOCUMENTS_ARE_PROTECTED | A source is password-protected |
ONE_OR_MORE_DOCUMENTS_ARE_UNABLE_TO_BE_OPENED | A source could not be opened |
ONE_OR_MORE_DOCUMENTS_FAILED_TO_CONVERT_TO_DOCX | Conversion to .docx failed |
ONE_OR_MORE_DOCUMENTS_FAILED_TO_UPLOAD_TO_S3 | A source did not reach storage |
FAILED_TO_SAVE_REDLINE_AS_PDF | PDF rendering of the redline failed |
FAILED_TO_GENERATE_COMPARISON | The comparison itself failed |
OUT_OF_MEMORY | The documents exceeded the processing budget |
UNHANDLED_ERROR | Unclassified failure |
OUT_OF_MEMORY on very large or deeply nested documents is worth reporting to support —
it is usually a fixable characteristic of the specific file rather than a hard limit.