Contracts are documents a client reads and signs, such as service agreements and terms of engagement.
A contract is made of ordered sections. A section is either freeform text, stored as Quill delta operations, or a fees section: one or more priced packages the client sees and, when the section allows a choice, picks from. Any section may require the client's signature.
GET /api/v2/contractsTemplates are not included; only contracts issued to clients. Each row carries its sections, as in the single-contract response below.
{
"total_count": 3,
"page_count": 1,
"page_size": 20,
"contracts": [
{
"id": 1,
"subject": "Lawn refresh agreement",
"status": "draft",
"date": "2026-09-11",
"client_id": 12,
"project_id": null,
"created_at": "2026-09-11T16:10:59Z",
"updated_at": "2026-09-11T16:10:59Z"
},
...
]
}
GET /api/v2/clients/:client_id/contractsContracts are also a nested resource for Clients
GET /api/v2/contracts/:id
url is the contract in the app. client_view_url is the client's own view, no login needed, and pdf_url is its PDF. Both keyed links can be shared as they stand.
{
"id": 1,
"subject": "Lawn refresh agreement",
"status": "draft",
"date": "2026-09-11",
"client_id": 12,
"url": "https://yourdomain.clientary.com/contracts/1",
"client_view_url": "https://yourdomain.clientary.com/contracts/1/client_view/k-abc123",
"pdf_url": "https://yourdomain.clientary.com/contracts/1/pdf/k-abc123.pdf",
"contract_sections": [
{
"id": 10,
"position": 0,
"section_type": "freeform",
"requires_signature": false,
"content": [
{ "insert": "Scope" },
{ "insert": "\n", "attributes": { "header": 2 } },
{ "insert": "Remove old sod and lay new turf.\n" }
]
},
{
"id": 11,
"position": 1,
"section_type": "fees",
"requires_signature": true,
"content": {
"type": "static",
"title": "Fee",
"instructions": [ { "insert": "\n" } ],
"options": [
{
"title": "",
"description": "",
"line_items": [
{ "item_type": "service", "title": "Lawn refresh", "quantity": 1, "price": 5000, "taxable": false }
]
}
]
}
}
]
}
POST /api/v2/contracts
{
"contract": {
"client_id": 12,
"subject": "Lawn refresh agreement",
"date": "2026-09-11",
"contract_sections_attributes": [
{
"position": 0,
"section_type": "freeform",
"content": [ { "insert": "Remove old sod and lay new turf.\n" } ]
},
{
"position": 1,
"section_type": "fees",
"requires_signature": true,
"content": {
"type": "static",
"title": "Fee",
"instructions": [ { "insert": "\n" } ],
"options": [
{
"line_items": [
{ "item_type": "service", "title": "Lawn refresh", "quantity": 1, "price": 5000, "taxable": false }
]
}
]
}
}
]
}
}
Required Fields: client_id, date, and at least one section (HTTP 422 on failure)
content is an array of Quill delta operations. Plain text is one insert ending in a newline. A wrapped form, { "ops": [...] }, is accepted as well.
content is an object:
type: static shows the packages. single lets the client choose one package; multi lets them choose several. Both choice types need at least two options, each with a title.
title: Heading shown above the packages.
instructions: Quill delta operations shown above the packages; [ { "insert": "\n" } ] for none.
options: The packages. Each has an optional title and description and a required line_items array.
line_items[].item_type: service for a priced line, header for a label with no amount.
line_items[].title: Required.
line_items[].quantity, price: Required for a service line.
line_items[].taxable: Required, true or false.
A package may also carry currency_code, tax, tax_label, tax2, tax2_label, tax2_enabled, and compound_tax.
PUT /api/v2/contracts/:idYou may provide a partial list of fields to update. Sections are updated through contract_sections_attributes: include an id to change an existing section, omit it to add one, or send "_destroy": true with an id to remove one. A contract the client has signed can no longer be changed.
{
"contract": {
"subject": "Lawn refresh agreement, revised",
"contract_sections_attributes": [
{ "id": 10, "_destroy": true },
{ "position": 0, "section_type": "freeform", "content": [ { "insert": "Revised scope.\n" } ] }
]
}
}
DELETE /api/v2/contracts/:idDeletions are permanent and not reversible.