version90 DEVELOPERS

REST API / Contracts & documents

Contracts & documents

A workspace is one negotiation. It holds assets (primary document, exhibits…), each a chain of immutable versions. Tokens can create workspaces, upload documents, and read everything — document editing endpoints reject token auth.

GET /api/v1/workspaces #

List negotiations. Rows are enriched with the AI-extracted contract_type and effective_date (latest suggestion, human-confirmed value preferred) and the count of open AI findings.

Auth · Bearer token · any member

Parameter In Type Required Description
status query enum no Filter by lifecycle status (see workspace object)
turn query enum no internal | counterparty
counterparty query string no Substring match
q query string no Substring match on title
sort query enum no created_at | updated_at | title (default updated_at)
order query enum no asc | desc (default desc)
limit query int no ≤ 200 (default 50)
offset query int no Pagination offset
Request
curl "https://app.version90.com/api/v1/workspaces?turn=internal&limit=20" \
  -H "Authorization: Bearer $V90_TOKEN"
200
{
  "total": 12,
  "items": [{
    ...workspace object (below),
    "contract_type": "Master Services Agreement",  // AI-extracted, "" if none
    "effective_date": "2026-08-01",                // AI-extracted, "" if none
    "open_findings": 2
  }]
}
POST /api/v1/uploads/batch #

Upload-first creation — the recommended way in. Each file becomes its own workspace (title provisionally the filename); AI identification then fills the real title, counterparty and type in the background. Files are processed independently: one bad file never fails the batch (HTTP 207).

Auth · Bearer token · member role or above

Parameter In Type Required Description
files form binary[] yes One or more documents (multipart, repeated field)
Request
curl -X POST https://app.version90.com/api/v1/uploads/batch \
  -H "Authorization: Bearer $V90_TOKEN" \
  -F "files=@msa.docx" -F "files=@nda.docx"
207 — per-file results
[
  { "filename": "msa.docx", "ok": true,
    "workspace_id": "1f7a9e42-…",
    "version": { /* version object */ } },
  { "filename": "nda.docx", "ok": false,
    "error": { "code": "empty_file", "message": "…" } }
]

Errors

  • 402 allowance_exhaustedmonthly contract allowance used (production)
POST /api/v1/workspaces #

Create an empty workspace explicitly (the form path). Most integrations should prefer /uploads/batch and let AI identification name things.

Auth · Bearer token · member role or above

Parameter In Type Required Description
title body string yes 1–500 chars
counterparty_name body string no Optional; AI fills it on first upload if empty
internal_entity body string no Your contracting entity
tags body string[] no Free-form labels
Request
curl -X POST https://app.version90.com/api/v1/workspaces \
  -H "Authorization: Bearer $V90_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Vendor MSA — Acme"}'
201 — workspace object
{
  "id": "1f7a9e42-3c2d-4b56-9a01-8e4d7c6b5a30",
  "title": "Master Services Agreement — Meridian Robotics",
  "counterparty_name": "Meridian Robotics, Inc.",
  "internal_entity": "",
  "owner_id": "b3c1d5e7-…",
  "status": "with_counterparty",   // intake | processing | internal_review |
                                   // ready_to_send | with_counterparty |
                                   // counterparty_revision_received | approved |
                                   // awaiting_signature | executed | archived | closed
  "turn": "counterparty",          // "internal" | "counterparty"
  "tags": [],
  "pinned_baseline_version_id": null,
  "created_at": "2026-07-08T14:22:01+00:00",
  "updated_at": "2026-07-11T09:41:37+00:00"
}
GET /api/v1/workspaces/{id} #

One workspace with its documents: every active asset and that asset's full immutable version chain, oldest first.

Auth · Bearer token · any member

Parameter In Type Required Description
id path uuid yes Workspace id
Request
curl "https://app.version90.com/api/v1/workspaces/$ID" \
  -H "Authorization: Bearer $V90_TOKEN"
200
{
  ...workspace object,
  "documents": [{
    "id": "7a6b5c4d-…",
    "asset_type": "primary",     // primary | related | exhibit | executed_copy
    "label": "",
    "current_version_id": "e8d1b2c3-…",
    "versions": [ /* version objects, oldest → newest */ ]
  }]
}
POST /api/v1/workspaces/{id}/documents #

Upload a document into a workspace (a counterparty return, an exhibit…). Creates a new immutable version. Byte-identical re-uploads are rejected with 409 unless you explicitly retain; an Idempotency-Key makes retries safe (the same key replays the original result).

Auth · Bearer token · member role or above

Parameter In Type Required Description
id path uuid yes Workspace id
file form binary yes The document
kind form enum no primary (default) | related | exhibit | executed_copy
note form string no Version note
label form string no Asset label (new assets)
duplicate_action form enum no retain — store a byte-identical duplicate anyway
Idempotency-Key header string no Safe-retry key
Request
curl -X POST "https://app.version90.com/api/v1/workspaces/$ID/documents" \
  -H "Authorization: Bearer $V90_TOKEN" \
  -H "Idempotency-Key: import-2026-07-11-001" \
  -F "file=@MSA_Meridian_v3.docx" -F "kind=primary"
201
{
  "version": { /* version object (state: received → ready after processing) */ },
  "asset": { "id": "7a6b5c4d-…", "asset_type": "primary" },
  "replayed": false,           // true when the Idempotency-Key replayed
  "duplicate_retained": false
}

Errors

  • 409 duplicate_contentbyte-identical file exists — response includes existing_version_id; resend with duplicate_action=retain to keep both
  • 422empty/oversized/unsupported file — {code, message}
GET /api/v1/versions/{id} #

One version's metadata (the version object). For the bytes, use /download.

Auth · Bearer token · any member

Parameter In Type Required Description
id path uuid yes Version id
Request
curl "https://app.version90.com/api/v1/versions/$VID" \
  -H "Authorization: Bearer $V90_TOKEN"
200 — version object
{
  "id": "e8d1b2c3-…",
  "asset_id": "7a6b5c4d-…",
  "parent_version_id": "d0c9b8a7-…",   // null for the first version
  "blob_hash": "9f31…c2ae",            // sha256 — byte-for-byte identity
  "size_bytes": 96214,
  "original_filename": "MSA_Meridian_v3.docx",
  "detected_format": "docx",
  "source_type": "counterparty_return", // upload | internal_edit |
                                        // counterparty_return | ai_suggestion | …
  "created_by_type": "human",
  "created_by_label": "dana@meridianrobotics.com",
  "note": "",
  "state": "ready",                     // received | scanning | ready | rejected
  "error_code": null,
  "error_detail": null,
  "meta": {},
  "created_at": "2026-07-11T09:41:35+00:00"
}
GET /api/v1/versions/{id}/download #

The exact bytes of this version — sha256-addressed, never regenerated. What was uploaded is what you download.

Auth · Bearer token · any member

Parameter In Type Required Description
id path uuid yes Version id
Request
curl -OJ "https://app.version90.com/api/v1/versions/$VID/download" \
  -H "Authorization: Bearer $V90_TOKEN"
200
binary body
Content-Disposition: attachment; filename="MSA_Meridian_v3.docx"
GET /api/v1/versions/{id}/revisions #

Every tracked change in a DOCX version, read-only: type, author, text, and the paragraph anchor it lives in. rids are stable within a version snapshot. (Resolving changes requires an interactive session — tokens read, humans decide.)

Auth · Bearer token · any member

Parameter In Type Required Description
id path uuid yes Version id (must be a ready DOCX)
Request
curl "https://app.version90.com/api/v1/versions/$VID/revisions" \
  -H "Authorization: Bearer $V90_TOKEN"
200
{
  "version_id": "e8d1b2c3-…",
  "revisions": [{
    "rid": "word/document.xml:0",
    "type": "insert",            // insert | delete | move_from | move_to |
                                 // run_format | paragraph_format |
                                 // paragraph_mark_insert | paragraph_mark_delete | …
    "native_id": "1043",
    "author": "Dana Ferro [Meridian Robotics]",
    "date": "2026-07-11T09:12:00Z",
    "text": "sixty (60)",
    "supported": true,           // resolvable in-app
    "anchor_id": "6A3F2B1C"      // paragraph anchor (w14:paraId or positional)
  }]
}

Errors

  • 422version is not a ready DOCX