Contracts API
All authenticated endpoints require a valid Cloudflare Access JWT. Public signing endpoints use a token-based URL instead.
Base paths:
- Authenticated:
/api/contracts - Public (no auth):
/api/contracts/sign
Public Signing Endpoints
Section titled “Public Signing Endpoints”GET /api/contracts/sign/:token
Section titled “GET /api/contracts/sign/:token”Returns the contract info and the signer’s assigned fields for the signing page.
Response
{ "contract": { "id": "string", "title": "string", "contract_type": "employment_agreement", "status": "pending" }, "signer": { "id": "string", "name": "string", "email": "string", "role_label": "string", "status": "viewed", "sign_order": 1, "signed_at": null }, "fields": [ { "id": "string", "type": "signature", "label": "Employee Signature", "signer_role_id": "role_1", "page": 1, "x_pct": 0.1, "y_pct": 0.8, "w_pct": 0.3, "h_pct": 0.06, "required": true } ]}GET /api/contracts/sign/:token/pdf
Section titled “GET /api/contracts/sign/:token/pdf”Streams the base PDF bytes from R2.
POST /api/contracts/sign/:token/view
Section titled “POST /api/contracts/sign/:token/view”Records that the signer has viewed the document (viewed_at, activity log). No body required.
POST /api/contracts/sign/:token/submit
Section titled “POST /api/contracts/sign/:token/submit”Submits the signer’s completed field values.
Body
{ "field_values": { "field-id-1": { "type": "typed", "value": "Jane Smith" }, "field-id-2": "2026-03-07", "field-id-3": true }}For signature fields, the value is { type: "typed" | "drawn", value: string } where drawn signatures use a base64 PNG data URL.
Response { "ok": true }
On success: marks the signer as signed, sends the next signer’s email (if any), or triggers contract completion.
POST /api/contracts/sign/:token/decline
Section titled “POST /api/contracts/sign/:token/decline”Declines the signing request.
Body { "reason": "optional string" }
Response { "ok": true }
Templates
Section titled “Templates”GET /api/contracts/templates
Section titled “GET /api/contracts/templates”List active contract templates.
Response { "templates": ContractTemplate[] }
POST /api/contracts/templates
Section titled “POST /api/contracts/templates”Create a new template. Accepts multipart/form-data.
| Field | Type | Description |
|---|---|---|
name | string | Template name |
description | string? | Optional description |
contract_type | string | employment_agreement | msa | nda | npa | other |
signer_roles | JSON string | [{ id, label }] |
fields_schema | JSON string | FieldDef[] |
google_doc_id | string? | Google Drive file ID to export as PDF |
google_doc_url | string? | Google Docs URL to export as PDF |
pdf | File? | Direct PDF upload |
Exactly one of google_doc_id, google_doc_url, or pdf is required.
Response { "id": "string" }
GET /api/contracts/templates/:id
Section titled “GET /api/contracts/templates/:id”Get a single template including fields_schema and signer_roles.
PUT /api/contracts/templates/:id
Section titled “PUT /api/contracts/templates/:id”Update template metadata and/or field layout.
Body
{ "name": "string?", "description": "string?", "contract_type": "string?", "signer_roles": "SignerRole[]?", "fields_schema": "FieldDef[]?"}DELETE /api/contracts/templates/:id
Section titled “DELETE /api/contracts/templates/:id”Soft-deletes the template (is_active = 0). Existing contracts are not affected.
GET /api/contracts/templates/:id/pdf
Section titled “GET /api/contracts/templates/:id/pdf”Streams the template’s base PDF from R2.
Google Drive
Section titled “Google Drive”GET /api/contracts/google-drive/files
Section titled “GET /api/contracts/google-drive/files”Lists Google Docs from Drive. Requires a Google Workspace connection with drive.readonly scope.
Query params
query— optional text search (name contains)folder_id— optional folder filter (defaults tocontracts_drive_folder_idorg setting if set)
Response { "files": DriveFile[], "defaultFolderId": string | null }
Contracts
Section titled “Contracts”GET /api/contracts
Section titled “GET /api/contracts”List contracts. Scoped by access level:
- executive/head: all contracts
- manager: own contracts + contracts linked to direct reports
- lead/employee: contracts linked to own person record
Query params — status, person_id (optional filters)
Response { "contracts": Contract[] }
POST /api/contracts
Section titled “POST /api/contracts”Create a new contract. Accepts multipart/form-data.
| Field | Type | Description |
|---|---|---|
title | string | Contract title |
contract_type | string | Contract type enum |
template_id | string? | Template to base on (omit for one-off) |
fields_schema | JSON string | FieldDef[] (one-off only) |
signer_roles | JSON string | SignerRole[] (one-off only) |
signers | JSON string | SignerInput[] |
person_id | string? | Link to an employee |
google_doc_id | string? | Drive file (one-off) |
google_doc_url | string? | Google Docs URL (one-off) |
pdf | File? | Direct PDF upload (one-off) |
SignerInput
{ "name": "string", "email": "string", "signer_role_id": "role_1", "role_label": "Employee", "sign_order": 1, "return_to_sender": false}Response { "id": "string" }
GET /api/contracts/:id
Section titled “GET /api/contracts/:id”Get full contract detail including signers and activity log.
Response ContractDetail (extends Contract with signers: ContractSigner[] and activity: ContractActivity[])
POST /api/contracts/:id/send
Section titled “POST /api/contracts/:id/send”Send the contract to the first signer. Sets status to pending and emails the first signer.
Response { "ok": true }
POST /api/contracts/:id/void
Section titled “POST /api/contracts/:id/void”Void the contract.
Body { "reason": "optional string" }
Response { "ok": true }
POST /api/contracts/:id/remind
Section titled “POST /api/contracts/:id/remind”Resend the signing email to the current pending signer.
Response { "ok": true }
GET /api/contracts/:id/download
Section titled “GET /api/contracts/:id/download”Streams the signed PDF (if completed) or base PDF.
GET /api/contracts/:id/activity
Section titled “GET /api/contracts/:id/activity”Get the activity log for a contract.
Response { "activity": ContractActivity[] }
FieldDef
Section titled “FieldDef”interface FieldDef { id: string; type: "signature" | "text" | "date" | "checkbox" | "textarea"; label: string; signer_role_id: string; page: number; // 1-indexed x_pct: number; // 0–1 relative to page width (top-left origin) y_pct: number; // 0–1 relative to page height (top-left origin) w_pct: number; h_pct: number; required: boolean; placeholder?: string;}Contract status flow
Section titled “Contract status flow”draft → pending → completed ↓ voided