version90 DEVELOPERS

REST API / Playbook

The Playbook

How the organization does contracts: standards (baseline documents the AI compares against), the clauses extracted and approved from them, and knowledge sources (policies the AI cites). Reads are open to any member; uploading standards/knowledge is ingestion (allowed for tokens, admin-level roles); review decisions — approving or rejecting clause candidates — stay in-app. This is what the MCP tool search_playbook reads, and one of the groups in GET /api/v1/search.

GET /api/v1/clauses #

Search the clause library. Filter by free text (name + language substring), clause type, state, or category; newest first, capped at 200. For 'the org's approved language' filter state=approved — candidates are extractions still awaiting human review.

Auth · Bearer token · any member

Parameter In Type Required Description
q query string no Substring over name and language
clause_type query string no Exact type, e.g. limitation_of_liability
state query enum no candidate | approved | deprecated | rejected
category_id query uuid no Only clauses applicable to this category
Request
curl "https://app.version90.com/api/v1/clauses?q=liability&state=approved" \
  -H "Authorization: Bearer $V90_TOKEN"
200 — clause objects
[ /* clause objects — below */ ]

The clause object

{
  "id": "4b5c6d7e-…",
  "name": "Limitation of liability",
  "clause_type": "limitation_of_liability",
  "language": "IN NO EVENT SHALL EITHER PARTY'S AGGREGATE LIABILITY EXCEED…",
  "intent": "Cap aggregate liability at 12 months of fees.",
  "state": "approved",            // candidate | approved | deprecated | rejected
  "applicable_category_ids": [],
  "tags": [],
  "source_kind": "baseline_extraction",   // or "manual"
  "source_baseline_version_id": "9c8d7e6f-…",  // provenance, null for manual
  "source_anchor": "Section 8. Limitation of Liability",
  "approved_at": "2026-06-02T15:11:09+00:00"
}

Provenance travels with every clause: extracted clauses point at the exact baseline version and the heading they came from. GET /api/v1/clauses/{id} additionally returns the append-only revision history of the clause's language and state changes.

GET /api/v1/baselines #

The org's standards, each with its immutable version chain (oldest first) and which version is active. A version's state moves received → ready as clause extraction completes.

Auth · Bearer token · any member

Request
curl https://app.version90.com/api/v1/baselines \
  -H "Authorization: Bearer $V90_TOKEN"
200
[{
  "id": "8e9f0a1b-…",
  "name": "Standard MSA",
  "category_id": null,
  "active_version_id": "9c8d7e6f-…",
  "retired": false,
  "versions": [{
    "id": "9c8d7e6f-…",
    "label": "",
    "state": "ready",              // received | ready | failed
    "original_filename": "standard-msa-2026.docx",
    "file_format": "docx",
    "error_detail": null,
    "is_active": true,
    "created_at": "2026-06-02T14:58:40+00:00"
  }]
}]
POST /api/v1/baselines #

Create a standard (then upload its document as a version below). Programmatic seeding — e.g. migrating a template library — is a two-call loop: create, upload.

Auth · Bearer token · admin-level role (manage baselines)

Parameter In Type Required Description
name body string yes 1–255 chars, e.g. 'Standard NDA'
category_id body uuid no Optional category
Request
curl -X POST https://app.version90.com/api/v1/baselines \
  -H "Authorization: Bearer $V90_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Standard NDA"}'
201 — baseline object (no versions yet)
{ "id": "8e9f0a1b-…", "name": "Standard NDA", "versions": [], … }
POST /api/v1/baselines/{id}/versions #

Upload a standard's document (DOCX or PDF, multipart). Clause extraction starts immediately in the background; extracted clauses land in the library as candidates for a human to review in-app.

Auth · Bearer token · admin-level role (manage baselines)

Parameter In Type Required Description
id path uuid yes Baseline id
file form binary yes The document
make_active form bool no Default true — new version becomes the one reviews compare against
Request
curl -X POST "https://app.version90.com/api/v1/baselines/$BID/versions" \
  -H "Authorization: Bearer $V90_TOKEN" \
  -F "[email protected]"
201
{ "version_id": "9c8d7e6f-…" }

Errors

  • 422empty/unsupported/unsafe file — {code, message}
GET /api/v1/knowledge-sources #

The org's knowledge sources — policies and playbook documents the AI cites when a contract conflicts with them — each with its version chain and active version.

Auth · Bearer token · any member

Request
curl https://app.version90.com/api/v1/knowledge-sources \
  -H "Authorization: Bearer $V90_TOKEN"
200
[{
  "id": "2c3d4e5f-…",
  "name": "Security policy",
  "scope": "organization",         // organization | category
  "category_id": null,
  "active_version_id": "5f6a7b8c-…",
  "retired": false,
  "versions": [{
    "id": "5f6a7b8c-…", "label": "", "state": "ready",
    "original_filename": "security-policy.pdf", "file_format": "pdf",
    "error_detail": null, "is_active": true
  }]
}]
GET /api/v1/categories #

Contract categories (the org's own taxonomy — MSA, NDA, DPA…), used to scope baselines, clauses and knowledge.

Auth · Bearer token · any member

Parameter In Type Required Description
include_retired query bool no Default false
Request
curl https://app.version90.com/api/v1/categories \
  -H "Authorization: Bearer $V90_TOKEN"
200
[{
  "id": "7d8e9f0a-…", "key": "msa", "name": "MSA",
  "description": "", "sort_order": 0, "retired": false
}]

Deliberately undocumented here: approving, rejecting, or deprecating clauses. Deciding what counts as the org's approved language is a review workflow that belongs in the app (Playbook → Clauses), where candidates are read next to the standard they came from.