Documents
Documents live inside containers and have a version lifecycle: DRAFT → ACTIVE → ARCHIVED. Each document always has one active version at most. Signing requests are sent against a specific version.
Version Lifecycle
Section titled “Version Lifecycle”DRAFT ──(publish)──► ACTIVE ──(archive)──► ARCHIVED │ (new-draft) │ ▼ DRAFT (v2)A new document starts with version 1 in DRAFT. Publishing transitions it to ACTIVE. Creating a new draft from an active document increments the version number.
Endpoints
Section titled “Endpoints”| Method | Path | Description |
|---|---|---|
GET | /api/v1/documents?containerId= | List documents in a container |
POST | /api/v1/documents | Create a document |
GET | /api/v1/documents/:id | Get a document with versions |
PUT | /api/v1/documents/:id/draft | Update the latest draft |
POST | /api/v1/documents/:id/publish | Publish the latest draft |
POST | /api/v1/documents/:id/archive | Archive the active version |
POST | /api/v1/documents/:id/new-draft | Create a new draft from active |
List documents GET
Section titled “List documents ”GET /api/v1/documents?containerId={uuid}Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
containerId | UUID | Yes | Filter documents by container |
Response 200:
{ "data": [ { "id": "018e1234-abcd-7000-8000-000000000010", "name": "Terms of Service", "description": null, "container_id": "018e1234-abcd-7000-8000-000000000001", "created_by": "018e1234-abcd-7000-8000-000000000002", "created_at": "2026-02-01T09:00:00.000Z", "updated_at": "2026-02-01T09:00:00.000Z" } ]}Error cases:
| Status | Code | Cause |
|---|---|---|
400 | BAD_REQUEST | containerId missing or not a valid UUID |
401 | UNAUTHORIZED | Missing or invalid API key |
404 | NOT_FOUND | Container not found in this organisation |
Create a document POST
Section titled “Create a document ”POST /api/v1/documentsCreates a document with an initial DRAFT version (no clauses yet).
Request body:
| Field | Type | Required | Constraints |
|---|---|---|---|
containerId | UUID | Yes | Must belong to your organisation |
name | string | Yes | 1–100 characters |
description | string | No | Max 500 characters |
{ "containerId": "018e1234-abcd-7000-8000-000000000001", "name": "Terms of Service", "description": "Website terms of service document"}Response 201: Document object (same shape as list response).
Error cases:
| Status | Code | Cause |
|---|---|---|
400 | BAD_REQUEST | Missing or invalid fields |
401 | UNAUTHORIZED | Missing or invalid API key |
404 | NOT_FOUND | containerId not found in this organisation |
Get a document GET
Section titled “Get a document ”GET /api/v1/documents/:idReturns the document with its versions array.
Response 200:
{ "data": { "id": "018e1234-abcd-7000-8000-000000000010", "name": "Terms of Service", "versions": [ { "id": "018e1234-abcd-7000-8000-000000000020", "version_number": 1, "status": "ACTIVE", "created_at": "2026-02-01T09:00:00.000Z" } ] }}Update the draft PUT
Section titled “Update the draft ”PUT /api/v1/documents/:id/draftReplaces all clauses and settings on the latest DRAFT version. This is a full replacement — send all clauses, not just changed ones.
Request body:
{ "content": { "clauses": [ { "id": "clause-1", "order": 0, "title": "Acceptable Use", "content": "{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"You agree to use the service only for lawful purposes.\"}]}]}", "is_mandatory": true, "default_checked": true, "slug": "acceptable-use" }, { "id": "clause-2", "order": 1, "title": "Marketing Communications", "content": "{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"We may send you promotional emails.\"}]}]}", "is_mandatory": false, "default_checked": false, "slug": "marketing-comms" } ] }, "settings": { "expiry_days": 30, "redirect_url": "https://example.com/thank-you" }}Clause fields:
| Field | Type | Description |
|---|---|---|
id | string | Stable clause identifier (your choice) |
order | number | Display order (0-based) |
title | string | Clause heading |
content | string | TipTap document as JSON string |
is_mandatory | boolean | true = signer must accept |
default_checked | boolean | Pre-checked state shown to signer |
slug | string | URL-friendly identifier |
section | string | Optional section grouping label |
Settings fields:
| Field | Type | Description |
|---|---|---|
expiry_days | number | Days until signing request expires (0 = never) |
redirect_url | string | null | URL to redirect signer after signing |
Error cases:
| Status | Code | Cause |
|---|---|---|
400 | BAD_REQUEST | Invalid clause structure or settings |
404 | NOT_FOUND | Document has no DRAFT version (already published or archived) |
Publish the draft POST
Section titled “Publish the draft ”POST /api/v1/documents/:id/publishTransitions the latest DRAFT version to ACTIVE. If another version is already ACTIVE, it is automatically archived.
Request: No body.
Response 200: Updated version object with status: "ACTIVE".
Error cases:
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No DRAFT version found on this document |
Archive the active version POST
Section titled “Archive the active version ”POST /api/v1/documents/:id/archiveTransitions the ACTIVE version to ARCHIVED. Existing signing requests already sent are unaffected but new ones cannot be created against archived versions.
Request: No body.
Response 200: Updated version object with status: "ARCHIVED".
Error cases:
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No ACTIVE version found on this document |
Create a new draft POST
Section titled “Create a new draft ”POST /api/v1/documents/:id/new-draftCopies the current ACTIVE version’s clauses and settings into a new DRAFT version with an incremented version number. Use this to start revising a published document.
Request: No body.
Response 201: New version object with status: "DRAFT" and incremented version_number.
Error cases:
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No ACTIVE version to copy from |
409 | CONFLICT | A DRAFT version already exists for this document |