{
  "openapi": "3.0.0",
  "info": {
    "title": "Pactly Public API v2 (in development)",
    "description": "V2 is in development. It starts with the same endpoints and behavior as v1. More endpoints and changes to requests and responses are coming before v2 is declared stable. Use v1 for integrations that require a frozen contract. The same API keys work with both versions; select the version in the URL.\n\nThe Pactly Public API provides programmatic access to contract lifecycle management capabilities, enabling integration with your existing systems. Key features include:\n\n• **Contract Management**: Create, retrieve, update, and search contracts across your organization\n• **Template Operations**: Access and manage contract templates for consistent document generation\n• **Document Generation**: Generate contracts from templates with dynamic variable substitution\n• **Text Extraction**: Extract and analyze text content from contracts and documents\n• **Workflow Automation**: Trigger workflows and track contract lifecycle events\n\nThe API follows RESTful principles and returns JSON responses. All endpoints require authentication via API key.",
    "termsOfService": "https://www.pactly.com/terms",
    "contact": {
      "name": "Pactly",
      "url": "https://www.pactly.com",
      "email": "developers@pactly.com"
    },
    "version": "2.0.0-draft",
    "license": {
      "name": "Apache 2.0",
      "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
    }
  },
  "servers": [
    {
      "url": "https://clientapi.pactly.ai"
    }
  ],
  "externalDocs": {
    "url": "https://www.pactly.com/api",
    "description": "Find more information about Pactly API capabilities, authentication, and best practices"
  },
  "tags": [
    {
      "name": "Contracts",
      "description": "Operations related to contract management including creation, retrieval, updates, and text extraction"
    },
    {
      "name": "Templates",
      "description": "Manage contract templates that serve as the foundation for generating new contracts"
    },
    {
      "name": "Emails",
      "description": "Email-related operations"
    },
    {
      "name": "Properties",
      "description": "Contract property definitions that define the metadata fields available on contracts"
    },
    {
      "name": "Categories",
      "description": "Contract categories used to classify and organize contracts"
    },
    {
      "name": "Parties"
    },
    {
      "name": "Clauses"
    },
    {
      "name": "Playbooks"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "components": {
    "schemas": {
      "Contract": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Contract ID"
          },
          "type": {
            "type": "string",
            "enum": [
              "template",
              "playbook",
              "external"
            ],
            "description": "Contract type"
          },
          "name": {
            "type": "string",
            "description": "Contract name"
          },
          "reference": {
            "type": "string",
            "description": "Contract reference number"
          },
          "status": {
            "description": "Company-configured status object, normally {\"label\":\"Executed\"}. If no label is configured, returns a string such as \"STATUS_5\". The numeric code is included in contract list responses, not in this detail response.",
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              },
              {
                "type": "string"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "finalizedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "executedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "executionMethod": {
            "type": "string",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          },
          "deleted": {
            "type": "boolean"
          },
          "aborted": {
            "type": "boolean"
          },
          "final": {
            "type": "boolean"
          },
          "hubRequestStatus": {
            "type": "string"
          },
          "properties": {
            "type": "object",
            "description": "Contract properties with values and metadata",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "value": {
                  "description": "Stored JSON value: a string, number, boolean, array, object, or null. Date properties use epoch milliseconds. Party values with an ID are returned as {id, label}; label can be null when the party cannot be resolved. Use GET /v2/properties for property definitions."
                },
                "meta": {
                  "type": "object",
                  "description": "Property metadata including name, type, and grouping"
                }
              },
              "required": [
                "meta"
              ]
            }
          },
          "metadata": {
            "type": "array",
            "description": "An entry for each stored property, with its current catalogue label and type when available. This supplements the properties object. Missing values are null.",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "nullable": true
                },
                "value": {
                  "description": "The same JSON value as properties[key].value, or null when absent."
                }
              },
              "required": [
                "key",
                "label",
                "type",
                "value"
              ]
            }
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Link to this contract in Pactly"
          },
          "user": {
            "type": "string",
            "nullable": true,
            "description": "ID of the user who owns the contract"
          },
          "requester": {
            "type": "string",
            "nullable": true,
            "description": "ID of the user who requested the contract"
          },
          "group": {
            "type": "string",
            "nullable": true,
            "description": "ID of the group this contract belongs to"
          },
          "category": {
            "type": "object",
            "nullable": true,
            "description": "Contract category, or null when unavailable",
            "properties": {
              "id": {
                "type": "string"
              },
              "label": {
                "type": "string",
                "nullable": true
              },
              "abbreviation": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "form": {
            "type": "string",
            "nullable": true,
            "description": "ID of the associated form"
          },
          "formentry": {
            "type": "string",
            "nullable": true,
            "description": "ID of the form entry"
          },
          "template": {
            "type": "string",
            "nullable": true,
            "description": "ID of the template used (for template contracts)"
          },
          "playbook": {
            "type": "string",
            "nullable": true,
            "description": "ID of the playbook used (for playbook contracts)"
          },
          "setModuleTo": {
            "type": "string",
            "description": "Parent contract ID, present when a template contract belongs to a contract set"
          },
          "parties": {
            "type": "array",
            "description": "Readable, confirmed parties associated with this contract. Excludes deleted parties and links awaiting review. Empty when none qualify.",
            "items": {
              "$ref": "#/components/schemas/ContractParty"
            }
          }
        },
        "example": {
          "id": "507f1f77bcf86cd799439011",
          "type": "template",
          "name": "Software License Agreement - Acme Corp",
          "reference": "SLA-2024-001",
          "status": {
            "label": "Executed"
          },
          "createdAt": "2024-01-15T10:30:00Z",
          "updatedAt": "2024-01-20T14:45:00Z",
          "finalizedAt": "2024-01-20T14:45:00Z",
          "executedAt": "2024-01-20T15:00:00Z",
          "executionMethod": "docusign",
          "archived": false,
          "deleted": false,
          "aborted": false,
          "final": true,
          "hubRequestStatus": "completed",
          "properties": {
            "licenseType": {
              "value": "Enterprise",
              "meta": {
                "name": "License Type",
                "description": "Type of software license",
                "type": "select",
                "group": "License Details"
              }
            },
            "effectiveDate": {
              "value": 1706745600000,
              "meta": {
                "name": "Effective Date",
                "description": "When the license becomes active",
                "type": "date",
                "group": "License Details"
              }
            }
          },
          "metadata": [
            {
              "key": "licenseType",
              "label": "License Type",
              "type": "list",
              "value": "Enterprise"
            },
            {
              "key": "effectiveDate",
              "label": "Effective Date",
              "type": "date",
              "value": 1706745600000
            }
          ],
          "url": "https://app.pactly.ai/contracts/template/507f1f77bcf86cd799439011",
          "category": {
            "id": "507f1f77bcf86cd799439031",
            "label": "Licensing",
            "abbreviation": "LIC"
          },
          "parties": [],
          "user": "507f1f77bcf86cd799439012",
          "template": "507f1f77bcf86cd799439013"
        }
      },
      "ContractParty": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Party ID"
          },
          "name": {
            "type": "string",
            "description": "Display name (entity name for companies, full name for individuals)"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "type": "string",
            "nullable": true
          },
          "isEntity": {
            "type": "boolean",
            "description": "Whether the party is a legal entity or an individual"
          },
          "isInternal": {
            "type": "boolean"
          },
          "entityName": {
            "type": "string",
            "nullable": true,
            "description": "Present for entities only"
          },
          "entityType": {
            "type": "string",
            "nullable": true,
            "description": "Present for entities only"
          },
          "entityRegNo": {
            "type": "string",
            "nullable": true,
            "description": "Present for entities only"
          },
          "firstName": {
            "type": "string",
            "nullable": true,
            "description": "Present for individuals only"
          },
          "lastName": {
            "type": "string",
            "nullable": true,
            "description": "Present for individuals only"
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Present for individuals only"
          },
          "designation": {
            "type": "string",
            "nullable": true,
            "description": "Present for individuals only"
          },
          "customAttributes": {
            "type": "object",
            "additionalProperties": true,
            "description": "Company-defined attributes, included when nonempty"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Included when nonempty"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Roles assigned to this party (e.g. counterparty, signer)"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier of the template"
          },
          "name": {
            "type": "string",
            "description": "Name of the template"
          },
          "version": {
            "type": "string",
            "description": "Version number of the template"
          },
          "description": {
            "type": "string",
            "description": "Description of the template purpose and usage"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Template creation timestamp"
          },
          "variables": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateVariable"
            },
            "description": "Variables this template takes. Post them by id in the \"values\" map when generating a contract."
          },
          "valuemaps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateValuemap"
            },
            "description": "Valuemaps this template takes. Post the chosen label by id in the \"values\" map when generating a contract."
          },
          "parties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateParty"
            },
            "description": "Parties this template prints details for. Post an object of the listed attributes by id in the \"values\" map."
          }
        },
        "example": {
          "id": "507f1f77bcf86cd799439013",
          "name": "Software License Agreement Template",
          "version": "2.1.0",
          "description": "Standard template for software licensing agreements with customizable terms",
          "createdAt": "2023-06-15T08:00:00Z",
          "variables": [
            {
              "id": "companyName",
              "question": "Name of the licensing company",
              "valueType": "string",
              "required": true,
              "default": null,
              "conditional": false
            },
            {
              "id": "seatCount",
              "question": "How many seats are licensed?",
              "valueType": "number",
              "required": true,
              "default": 25,
              "conditional": false
            }
          ],
          "valuemaps": [
            {
              "id": "licenseType",
              "question": "Which tier applies?",
              "allowMultipleSelection": false,
              "choices": [
                "Basic",
                "Professional",
                "Enterprise"
              ]
            }
          ],
          "parties": [
            {
              "id": "party1",
              "label": "Licensee",
              "type": "entity",
              "attributes": [
                "entityName",
                "entityRegNo",
                "country",
                "address"
              ]
            }
          ]
        }
      },
      "TemplateParty": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Key to use in the generation \"values\" map, e.g. \"party1\""
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "What this party is called in the document, e.g. \"Supplier\""
          },
          "type": {
            "type": "string",
            "enum": [
              "entity",
              "individual",
              "unknown"
            ],
            "nullable": true,
            "description": "Whether the template treats this party as a company or a person. \"unknown\" means the template has not decided — send \"type\" alongside the attributes when generating, or the document's entity/individual clauses have nothing to go on."
          },
          "attributes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The attribute names this party accepts. Anything else is rejected."
          }
        }
      },
      "TemplateVariable": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Key to use in the generation \"values\" map"
          },
          "question": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable prompt for this variable"
          },
          "valueType": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean"
            ],
            "description": "Expected type of the value"
          },
          "required": {
            "type": "boolean",
            "description": "Generation fails with a 400 when this is omitted"
          },
          "default": {
            "description": "Value the template author suggests, typed per valueType. Not applied automatically — an omitted variable renders as [●]."
          },
          "conditional": {
            "type": "boolean",
            "description": "True when the variable only toggles a block of text in or out rather than printing a value"
          }
        }
      },
      "GenerateContractRequest": {
        "type": "object",
        "required": [
          "templateId"
        ],
        "properties": {
          "templateId": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Template to generate from"
          },
          "contractName": {
            "type": "string",
            "maxLength": 256,
            "description": "Name for the generated contract",
            "default": "Pactly contract"
          },
          "values": {
            "type": "object",
            "additionalProperties": true,
            "description": "Flat map keyed by the ids from GET /v2/templates/{id}. A variable takes a value of its declared valueType — a string for text (numbers are rejected, not coerced), a real boolean for boolean. A valuemap takes the chosen label — an array of labels when allowMultipleSelection is true, a single label when it is not. A party takes an object of its declared attributes, plus \"type\" (\"entity\" or \"individual\") when the template leaves the party type \"unknown\". Anything omitted renders as [●].",
            "example": {
              "SupplierName": "Acme Pte Ltd",
              "Term": 24,
              "Agency": false,
              "ContractingEntity": "Singapore - Acme Holdings",
              "party1": {
                "entityName": "Acme Pte Ltd",
                "entityRegNo": "196800306E",
                "country": "Singapore",
                "address": "1 Raffles Place, Singapore 048616"
              }
            }
          },
          "options": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "persist": {
                "type": "boolean",
                "default": true,
                "description": "Keep the contract in Pactly. False returns the document and stores nothing."
              },
              "triggerWorkflows": {
                "type": "boolean",
                "description": "Run the templateContractCreated workflows. Defaults to the value of persist; cannot be true when persist is false."
              },
              "reference": {
                "type": "string",
                "maxLength": 128,
                "description": "Your own identifier, rendered wherever the template prints {{__reference}}. Only accepted with \"persist\": false — Pactly allocates its own reference otherwise. Omitted, the tag renders [●]."
              }
            }
          }
        }
      },
      "GeneratedContract": {
        "type": "object",
        "description": "Returned when options.persist is true (the default).",
        "properties": {
          "persisted": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "string",
            "description": "Contract ID"
          },
          "name": {
            "type": "string",
            "description": "Contract name"
          },
          "reference": {
            "type": "string",
            "description": "Reference Pactly allocated for this contract"
          },
          "template": {
            "type": "string",
            "description": "Template ID"
          },
          "company": {
            "type": "string",
            "description": "Company ID"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "effectiveDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "values": {
            "type": "object",
            "description": "Values the contract was generated from"
          }
        }
      },
      "GeneratedDocument": {
        "type": "object",
        "description": "Returned when options.persist is false. Nothing is stored, so there is no id to download later — the document is here.",
        "properties": {
          "persisted": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "name": {
            "type": "string",
            "description": "Contract name"
          },
          "template": {
            "type": "string",
            "description": "Template ID"
          },
          "reference": {
            "type": "string",
            "nullable": true,
            "description": "The reference you supplied, if any"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "document": {
            "type": "string",
            "format": "byte",
            "description": "The .docx, base64-encoded"
          }
        }
      },
      "TemplateValuemap": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Key to use in the generation \"values\" map"
          },
          "question": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable prompt for this valuemap"
          },
          "allowMultipleSelection": {
            "type": "boolean",
            "description": "When true, send an array of labels instead of a single label"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allowed labels. Send the label — Pactly resolves it to the underlying text."
          }
        }
      },
      "ContractListResponse": {
        "type": "object",
        "properties": {
          "contracts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Unique identifier of the contract"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "template",
                    "playbook",
                    "external"
                  ]
                },
                "name": {
                  "type": "string",
                  "description": "Name of the contract"
                },
                "reference": {
                  "type": "string",
                  "description": "Reference number of the contract"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Contract creation timestamp"
                },
                "updatedAt": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Last update timestamp"
                },
                "status": {
                  "type": "object",
                  "description": "Current status. `code` is the stable numeric status; `label` is the display text, which each company can rename. Filter on the code, show the label.",
                  "properties": {
                    "code": {
                      "type": "integer",
                      "nullable": true,
                      "description": "1=Draft, 2=In Negotiation, 3=Pending Approval, 4=Pending Signature, 5=Executed, 6=Terminated"
                    },
                    "label": {
                      "type": "string"
                    }
                  }
                },
                "archived": {
                  "type": "boolean"
                },
                "aborted": {
                  "type": "boolean"
                },
                "renewalAnchorDate": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true,
                  "description": "The date renewal and notice windows are measured from"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "expirationDate": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "endDateBasis": {
                  "type": "string",
                  "nullable": true
                },
                "noticeType": {
                  "type": "string",
                  "nullable": true
                },
                "noticePeriodDays": {
                  "type": "number",
                  "nullable": true
                },
                "autorenew": {
                  "type": "boolean",
                  "nullable": true
                },
                "renewalState": {
                  "type": "string",
                  "nullable": true
                },
                "category": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string",
                      "nullable": true
                    },
                    "abbreviation": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "example": {
          "contracts": [
            {
              "id": "507f1f77bcf86cd799439011",
              "type": "template",
              "name": "Software License Agreement - Acme Corp",
              "url": "https://app.pactly.ai/contracts/template/507f1f77bcf86cd799439011",
              "expirationDate": "2025-02-01T00:00:00Z",
              "endDateBasis": "fixed",
              "noticeType": null,
              "noticePeriodDays": null,
              "autorenew": false,
              "reference": "SLA-2024-001",
              "createdAt": "2024-01-15T10:30:00Z",
              "updatedAt": "2024-01-20T14:45:00Z",
              "status": {
                "code": 5,
                "label": "Executed"
              },
              "archived": false,
              "aborted": false,
              "renewalAnchorDate": "2025-02-01T00:00:00Z",
              "renewalState": "linked",
              "category": {
                "id": "507f1f77bcf86cd799439031",
                "label": "Licensing",
                "abbreviation": "LIC"
              }
            },
            {
              "id": "507f1f77bcf86cd799439014",
              "type": "playbook",
              "name": "Service Agreement - Beta Inc",
              "url": "https://app.pactly.ai/contracts/playbook/507f1f77bcf86cd799439014",
              "expirationDate": null,
              "endDateBasis": null,
              "noticeType": null,
              "noticePeriodDays": null,
              "autorenew": null,
              "reference": "SA-2024-002",
              "createdAt": "2024-01-16T09:00:00Z",
              "updatedAt": "2024-01-18T11:30:00Z",
              "status": {
                "code": 1,
                "label": "Draft"
              },
              "archived": false,
              "aborted": false,
              "renewalAnchorDate": null,
              "renewalState": null,
              "category": null
            }
          ],
          "pagination": {
            "pageSize": 50,
            "hasMore": true,
            "cursor": "eyJjIjoxLCJzIjp7ImsiOiJkIiwidiI6IjIwMjQtMDEtMTZUMDk6MDA6MDBaIn19"
          }
        }
      },
      "EvidenceParagraph": {
        "type": "object",
        "properties": {
          "reference": {
            "type": "string",
            "description": "Contract ID, source revision and one-based paragraph reference."
          },
          "paragraph": {
            "type": "integer",
            "minimum": 1
          },
          "startOffset": {
            "type": "integer"
          },
          "endOffset": {
            "type": "integer"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "EvidenceCoverage": {
        "type": "object",
        "properties": {
          "totalParagraphs": {
            "type": "integer"
          },
          "extraction": {
            "type": "string"
          },
          "complete": {
            "type": "boolean",
            "description": "Source extraction completeness, independent of pagination."
          },
          "warning": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "EvidencePagination": {
        "type": "object",
        "properties": {
          "hasMore": {
            "type": "boolean"
          },
          "cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ContractEvidenceResponse": {
        "type": "object",
        "properties": {
          "contractId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "revision": {
            "type": "string"
          },
          "paragraphs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceParagraph"
            }
          },
          "coverage": {
            "$ref": "#/components/schemas/EvidenceCoverage"
          },
          "pagination": {
            "$ref": "#/components/schemas/EvidencePagination"
          }
        }
      },
      "ContractSearchResponse": {
        "type": "object",
        "properties": {
          "searchMode": {
            "type": "string"
          },
          "hits": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "contractId": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "revision": {
                  "type": "string"
                },
                "matchType": {
                  "type": "string",
                  "enum": [
                    "keyword",
                    "semantic"
                  ]
                },
                "evidence": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/EvidenceParagraph"
                  }
                },
                "coverage": {
                  "$ref": "#/components/schemas/EvidenceCoverage"
                }
              }
            }
          },
          "coverage": {
            "type": "object",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "available",
                  "incomplete",
                  "unavailable"
                ]
              },
              "requestedContracts": {
                "type": "integer"
              },
              "missingIndexedText": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "exhaustive": {
                "type": "boolean",
                "enum": [
                  false
                ]
              },
              "warning": {
                "type": "string"
              }
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/EvidencePagination"
          }
        }
      },
      "ContractTextResponse": {
        "type": "object",
        "properties": {
          "contractId": {
            "type": "string",
            "description": "ID of the contract"
          },
          "name": {
            "type": "string",
            "description": "Name of the contract"
          },
          "text": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Array of text segments from the contract"
          }
        },
        "example": {
          "contractId": "507f1f77bcf86cd799439011",
          "name": "Software License Agreement - Acme Corp",
          "text": [
            "SOFTWARE LICENSE AGREEMENT",
            "This Software License Agreement (\"Agreement\") is entered into as of February 1, 2024",
            "between Acme Corporation (\"Licensor\") and Beta Inc (\"Licensee\").",
            "",
            "1. GRANT OF LICENSE",
            "Subject to the terms and conditions of this Agreement, Licensor hereby grants to Licensee",
            "a non-exclusive, non-transferable Enterprise license to use the Software."
          ]
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Bad Request"
          },
          "message": {
            "type": "string",
            "example": "Invalid contract ID format"
          }
        }
      },
      "Property": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Property ID"
          },
          "key": {
            "type": "string",
            "description": "Unique property key identifier"
          },
          "label": {
            "type": "string",
            "description": "Display label"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true,
            "description": "Property data type (string, number, boolean, date, list, multi-option, etc.)"
          },
          "options": {
            "type": "object",
            "nullable": true,
            "description": "Available options for list/select type properties"
          },
          "group": {
            "type": "string",
            "nullable": true,
            "description": "Property group name"
          },
          "subGroup": {
            "type": "string",
            "nullable": true
          },
          "sortOrder": {
            "type": "integer",
            "description": "Display sort order"
          },
          "isDefault": {
            "type": "boolean",
            "description": "Whether this is a system default property"
          },
          "manualOnly": {
            "type": "boolean",
            "description": "Whether this property can only be set manually (not via AI extraction)"
          }
        }
      },
      "PropertyListResponse": {
        "type": "object",
        "properties": {
          "properties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Property"
            }
          }
        }
      },
      "Category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Category ID"
          },
          "label": {
            "type": "string",
            "description": "Category label"
          },
          "abbreviation": {
            "type": "string",
            "nullable": true,
            "description": "Short abbreviation for the category"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "isDefault": {
            "type": "boolean",
            "description": "Whether this is a system default category"
          }
        }
      },
      "CategoryListResponse": {
        "type": "object",
        "properties": {
          "categories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Category"
            }
          }
        }
      },
      "Pagination": {
        "type": "object",
        "description": "Keyset pagination. Pass `cursor` back to fetch the next page and stop when `hasMore` is false. The cursor is opaque: it encodes the sort position, not just an id, and must not be constructed by hand.",
        "properties": {
          "pageSize": {
            "type": "integer",
            "description": "Rows in this page"
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether another page follows"
          },
          "cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as `cursor` to fetch the next page"
          }
        }
      },
      "Party": {
        "type": "object",
        "description": "A party in the contact book. Parties awaiting confirmation and soft-deleted parties are never returned.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Party ID"
          },
          "name": {
            "type": "string",
            "description": "Display name — the entity name for organisations, the full name for individuals"
          },
          "isEntity": {
            "type": "boolean",
            "description": "True for an organisation, false for an individual"
          },
          "isInternal": {
            "type": "boolean",
            "description": "True for one of your own entities, false for an external counterparty"
          },
          "entityType": {
            "type": "string",
            "nullable": true
          },
          "entityRegNo": {
            "type": "string",
            "nullable": true,
            "description": "Company registration number, entities only"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "abbreviations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Alternative names this party is also known by"
          },
          "parent": {
            "type": "string",
            "nullable": true,
            "description": "Parent party id, for corporate groups. Use `?parent=` to list children."
          },
          "customAttributes": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values keyed by the `key` of a definition from GET /v2/party-attributes"
          },
          "sanctionsStatus": {
            "type": "string",
            "enum": [
              "clear",
              "sanctioned",
              "unknown"
            ],
            "description": "Sanctions screening result. `unknown` means screening has not run or could not complete — it is NOT a clear result and must not be treated as one."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "example": {
          "id": "507f1f77bcf86cd799439021",
          "name": "Acme Holdings Pte Ltd",
          "isEntity": true,
          "isInternal": false,
          "entityType": "Private Limited",
          "entityRegNo": "196800306E",
          "email": "legal@acme.example",
          "country": "SG",
          "tags": [
            "supplier"
          ],
          "abbreviations": [
            "Acme",
            "Acme Holdings"
          ],
          "parent": null,
          "customAttributes": {
            "vendorTier": "strategic"
          },
          "sanctionsStatus": "clear",
          "createdAt": "2024-01-15T10:30:00Z",
          "updatedAt": "2024-01-20T14:45:00Z"
        }
      },
      "PartyListResponse": {
        "type": "object",
        "properties": {
          "parties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Party"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "PartyAttribute": {
        "type": "object",
        "description": "A custom attribute definition for parties — the schema behind a party's `customAttributes` values.",
        "properties": {
          "id": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "description": "The key used in a party's customAttributes"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "description": "Value type, e.g. text or date"
          },
          "options": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "Allowed values, for option-typed attributes"
          },
          "partyType": {
            "type": "string",
            "enum": [
              "individual",
              "entity",
              "both"
            ],
            "description": "Which kind of party this attribute applies to"
          }
        }
      },
      "PartyAttributeListResponse": {
        "type": "object",
        "properties": {
          "partyAttributes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartyAttribute"
            }
          }
        }
      },
      "Clause": {
        "type": "object",
        "description": "A clause from the pre-approved clause library.",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "description": "Plain text of the clause"
          },
          "html": {
            "type": "string",
            "description": "Rich text of the clause"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "defaultComment": {
            "type": "string",
            "description": "Comment normally inserted alongside this clause"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ClauseListResponse": {
        "type": "object",
        "properties": {
          "clauses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Clause"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "Playbook": {
        "type": "object",
        "description": "A playbook: the negotiation positions in force. Only production versions are returned.",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "version": {
            "type": "integer"
          },
          "versionNote": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "isDefault": {
            "type": "boolean",
            "description": "True for a Pactly-provided standard playbook, false for your own"
          },
          "positionCount": {
            "type": "integer"
          },
          "unconfirmedPositionCount": {
            "type": "integer",
            "description": "Positions suggested but not yet accepted by a person. They are excluded from `positions`; this is how many were withheld."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PlaybookListResponse": {
        "type": "object",
        "properties": {
          "playbooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Playbook"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "PlaybookPosition": {
        "type": "object",
        "description": "One negotiation position.",
        "properties": {
          "order": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "groupId": {
            "type": "string",
            "nullable": true
          },
          "keywords": {
            "type": "array",
            "description": "Terms that identify this position in a document",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "pattern",
                    "exact",
                    "negative"
                  ],
                  "description": "pattern is a regular expression; exact is a whole-word match; negative disqualifies a paragraph"
                }
              }
            }
          },
          "examples": {
            "type": "array",
            "description": "Illustrative clauses for this position. `type` marks whether an example is acceptable or one to reject.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "nullable": true
                },
                "title": {
                  "type": "string",
                  "nullable": true
                },
                "text": {
                  "type": "string"
                }
              }
            }
          },
          "fallbacks": {
            "type": "array",
            "description": "Approved language to propose for this position",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string",
                  "nullable": true
                },
                "text": {
                  "type": "string"
                },
                "html": {
                  "type": "string"
                }
              }
            }
          },
          "ifNotFound": {
            "type": "object",
            "description": "What the ABSENCE of this position means — without it an empty match set cannot be interpreted.",
            "properties": {
              "compliant": {
                "type": "boolean"
              },
              "status": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "notes": {
            "type": "array",
            "description": "Drafting guidance only. Internal rationale and escalation routing notes are never published.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "technical_drafting"
                  ]
                },
                "text": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "PlaybookDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Playbook"
          },
          {
            "type": "object",
            "properties": {
              "groups": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "positions": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PlaybookPosition"
                }
              }
            }
          }
        ]
      },
      "PlaybookMatchResponse": {
        "type": "object",
        "description": "Keyword matches per position. Contains no compliance verdict: deciding whether a position is met is a separate AI evaluation.",
        "properties": {
          "playbook": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "isDefault": {
                "type": "boolean"
              }
            }
          },
          "positions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "order": {
                  "type": "integer"
                },
                "name": {
                  "type": "string",
                  "nullable": true
                },
                "ifNotFound": {
                  "type": "object",
                  "properties": {
                    "compliant": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "matches": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "paragraphIndex": {
                        "type": "integer",
                        "description": "Index into the `paragraphs` array you posted — look the text up there rather than expecting it back"
                      },
                      "keywords": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "value": {
                              "type": "string"
                            },
                            "type": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "parameters": {
      "AttributeFilterParam": {
        "in": "query",
        "name": "_*_",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "To filter results based on a particular attribute, specify a query parameter with the same name as the attribute defined on your model. For instance, if our `Purchase` model has an `amount` attribute, we could send `GET /purchase?amount=99.99` to return a list of $99.99 purchases."
      },
      "WhereQueryParam": {
        "in": "query",
        "name": "where",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Instead of filtering based on a specific attribute, you may instead choose to provide a `where` parameter with the WHERE piece of a [Waterline criteria](https://sailsjs.com/documentation/concepts/models-and-orm/query-language), _encoded as a JSON string_. This allows you to take advantage of `contains`, `startsWith`, and other sub-attribute criteria modifiers for more powerful `find()` queries.\n\ne.g. `?where={\"name\":{\"contains\":\"theodore\"}}`"
      },
      "LimitQueryParam": {
        "in": "query",
        "name": "limit",
        "required": false,
        "schema": {
          "type": "integer"
        },
        "description": "The maximum number of records to send back (useful for pagination). Defaults to 30."
      },
      "SkipQueryParam": {
        "in": "query",
        "name": "skip",
        "required": false,
        "schema": {
          "type": "integer"
        },
        "description": "The number of records to skip (useful for pagination)."
      },
      "SortQueryParam": {
        "in": "query",
        "name": "sort",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "The sort order. By default, returned records are sorted by primary key value in ascending order.\n\ne.g. `?sort=lastName%20ASC`"
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key for authentication. Include in all requests as x-api-key header. A key may be limited to scopes chosen when it was created (contracts:read, contracts:write, contracts:generate, parties:read, clauses:read, playbooks:read, emails:read); an endpoint outside the key's scopes returns 403 naming the scope it needs. Keys without scopes retain tenant-wide reads. Scoped keys also follow the key owner's current permissions; writes always require the owner's permission. A key may carry an optional expiry (403 once passed) and can be rotated from account settings: rotation issues a new secret on the same key, so scopes and expiry survive while the old secret stops working immediately."
      }
    }
  },
  "paths": {
    "/v2/test": {
      "get": {
        "summary": "Test endpoint",
        "description": "Test endpoint to test API connectivity and authentication",
        "tags": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Test successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/contract-statuses": {
      "get": {
        "summary": "List account contract status labels",
        "operationId": "listContractStatuses",
        "tags": [
          "Contracts"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Status codes and account labels"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts": {
      "get": {
        "summary": "List contracts",
        "description": "List contracts allowed by the API-key owner current ACL, newest first. Scopes can narrow this access.\n\nResults are cursor-paginated: pass the `cursor` from a response back to fetch the next page and stop when `hasMore` is false. The cursor encodes the sort position and is opaque — do not build one.\n\nFilters combine with AND. `party` and `counterparty` both narrow by the parties linked to a contract: `party` takes a party id from `GET /v2/parties`, `counterparty` matches a name and is the convenience form. Property values filter as `property[<key>]=<value>` using a `key` from `GET /v2/properties`; an unknown key is rejected rather than ignored.",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "keyword",
            "schema": {
              "type": "string"
            },
            "description": "Match against contract name and reference"
          },
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string",
              "enum": [
                "template",
                "playbook",
                "external"
              ]
            },
            "description": "Storage origin, not business type. Discover business types with GET /v2/categories."
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated status codes: 1=Draft, 2=In Negotiation, 3=Pending Approval, 4=Pending Signature, 5=Executed, 6=Terminated. e.g. `status=2,3,4` for everything in flight."
          },
          {
            "in": "query",
            "name": "party",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Only contracts linked to this party"
          },
          {
            "in": "query",
            "name": "counterparty",
            "schema": {
              "type": "string"
            },
            "description": "Only contracts linked to a party matching this name"
          },
          {
            "in": "query",
            "name": "partyRole",
            "schema": {
              "type": "string",
              "enum": [
                "counterparty",
                "signer",
                "counterparty_representative",
                "our_representative"
              ]
            },
            "description": "Narrow a party or counterparty filter to one role on the contract"
          },
          {
            "in": "query",
            "name": "category",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Category ID or exact label from GET /v2/categories. Ambiguous labels return 400."
          },
          {
            "in": "query",
            "name": "owner",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Company-scoped owner ID or exact email. Ambiguous emails return 400."
          },
          {
            "in": "query",
            "name": "createdAfter",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only contracts created after this date (ISO 8601)"
          },
          {
            "in": "query",
            "name": "createdBefore",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only contracts created before this date (ISO 8601)"
          },
          {
            "in": "query",
            "name": "updatedAfter",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only contracts updated after this date (ISO 8601)"
          },
          {
            "in": "query",
            "name": "updatedBefore",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only contracts updated before this date (ISO 8601)"
          },
          {
            "in": "query",
            "name": "renewalAfter",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Renewal anchor on or after this date. This can be an earlier notice deadline; use expirationAfter/Before for expiration."
          },
          {
            "in": "query",
            "name": "renewalBefore",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Renewal anchor on or before this date"
          },
          {
            "in": "query",
            "name": "expirationAfter",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Stored expiration date on or after this ISO date; unknown dates do not match."
          },
          {
            "in": "query",
            "name": "expirationBefore",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Stored expiration date on or before this ISO date."
          },
          {
            "in": "query",
            "name": "renewalState",
            "schema": {
              "type": "string"
            },
            "description": "Filter by renewal state"
          },
          {
            "in": "query",
            "name": "property",
            "style": "deepObject",
            "explode": true,
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "description": "Filter by contract property value: `property[<key>]=<value>`, where `<key>` comes from GET /v2/properties. Values are coerced to the property's declared type; an unknown key or an operator in place of a value is rejected. Party properties take a party ID."
          },
          {
            "in": "query",
            "name": "sort",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "updatedAt",
                "renewalAnchorDate"
              ],
              "default": "createdAt"
            },
            "description": "Sorting by renewalAnchorDate returns only contracts that have one"
          },
          {
            "in": "query",
            "name": "order",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response"
          },
          {
            "in": "query",
            "name": "pagesize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Page size (capped at 200)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of contracts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContractListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/templates": {
      "get": {
        "summary": "List all templates",
        "description": "Retrieve all available contract templates that can be used to create new contracts.",
        "tags": [
          "Templates"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Template"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/emails": {
      "get": {
        "summary": "List contract emails",
        "description": "List all emails associated with contracts (for AI training)",
        "tags": [
          "Emails"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "pagesize",
            "schema": {
              "type": "integer",
              "default": 100
            },
            "description": "Number of emails to return"
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Cursor for pagination"
          },
          {
            "in": "query",
            "name": "contractType",
            "schema": {
              "type": "string",
              "enum": [
                "template",
                "playbook",
                "external"
              ]
            },
            "description": "Filter by contract type"
          },
          {
            "in": "query",
            "name": "contractId",
            "schema": {
              "type": "string"
            },
            "description": "Filter by specific contract ID"
          },
          {
            "in": "query",
            "name": "sender",
            "schema": {
              "type": "string"
            },
            "description": "Filter by sender email"
          },
          {
            "in": "query",
            "name": "dateFrom",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Filter emails from this date"
          },
          {
            "in": "query",
            "name": "dateTo",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Filter emails until this date"
          }
        ],
        "responses": {
          "200": {
            "description": "List of emails with contract context",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "emails": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "subject": {
                            "type": "string"
                          },
                          "sender": {
                            "type": "string"
                          },
                          "recipient": {
                            "type": "string"
                          },
                          "body": {
                            "type": "string"
                          },
                          "bodyPlainText": {
                            "type": "string"
                          },
                          "contract": {
                            "type": "object"
                          },
                          "attachments": {
                            "type": "array"
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "pageSize": {
                          "type": "integer"
                        },
                        "hasMore": {
                          "type": "boolean"
                        },
                        "cursor": {
                          "type": "string"
                        },
                        "total": {
                          "type": "integer",
                          "description": "Number of readable emails matching the requested filters, across all pages. The cursor does not affect this count."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/properties": {
      "get": {
        "summary": "List property definitions",
        "description": "Retrieve all contract property definitions available to the company, including system default properties.",
        "tags": [
          "Properties"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of property definitions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PropertyListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/categories": {
      "get": {
        "summary": "List categories",
        "description": "Retrieve all contract categories available to the company, including system default categories.",
        "tags": [
          "Categories"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of categories",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/parties": {
      "get": {
        "summary": "List parties",
        "description": "List the parties (counterparties, entities and individuals) in the contact book. Excludes parties still awaiting confirmation — an AI-extracted party a person has not accepted is a suggestion, not a counterparty — and soft-deleted parties.\n\n`keyword` matches the normalised search string, so \"ASTAR\" finds \"A*STAR\". Results are cursor-paginated: pass the `cursor` from a response back to fetch the next page, and stop when `hasMore` is false. A cursor is opaque and must not be constructed by hand.",
        "tags": [
          "Parties"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "keyword",
            "schema": {
              "type": "string"
            },
            "description": "Match against party names, entity names and abbreviations"
          },
          {
            "in": "query",
            "name": "isEntity",
            "schema": {
              "type": "boolean"
            },
            "description": "true returns only organisations, false only individuals; omit for both"
          },
          {
            "in": "query",
            "name": "isInternal",
            "schema": {
              "type": "boolean"
            },
            "description": "true returns only your own entities, false only external counterparties"
          },
          {
            "in": "query",
            "name": "parent",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Return the children of this party — the way to walk a corporate group"
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response"
          },
          {
            "in": "query",
            "name": "pagesize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Page size (capped at 200)"
          },
          {
            "in": "query",
            "name": "order",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            },
            "description": "Sort direction on creation date"
          }
        ],
        "responses": {
          "200": {
            "description": "List of parties",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartyListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/party-attributes": {
      "get": {
        "summary": "List party attributes",
        "description": "The custom attribute definitions for parties. A party carries its values in `customAttributes` keyed by the `key` returned here, the same way a contract pairs its property values with `GET /v2/properties`.",
        "tags": [
          "Parties"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of party attribute definitions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartyAttributeListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/clauses": {
      "get": {
        "summary": "List clauses",
        "description": "List the clause library — the pre-approved language an agent should propose instead of drafting its own.\n\nVisibility follows the key owner: an admin key sees the whole library, a grouped user sees their groups, and a key owned by a lite user is refused, because lite users have no Clause Bank.",
        "tags": [
          "Clauses"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "tag",
            "schema": {
              "type": "string"
            },
            "description": "Filter by tag"
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response"
          },
          {
            "in": "query",
            "name": "pagesize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "order",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of clauses",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClauseListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The key belongs to a user without access to the clause library"
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/playbooks": {
      "get": {
        "summary": "List playbooks",
        "description": "List the playbooks in force — your negotiation positions as structured data. Returns your own playbooks and any Pactly-provided default you have not switched off; `isDefault` tells them apart. Only production versions are returned, because a draft is not policy.\n\nPositions are omitted here; fetch one playbook to get them.",
        "tags": [
          "Playbooks"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response"
          },
          {
            "in": "query",
            "name": "pagesize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "order",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of playbooks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybookListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts/search": {
      "post": {
        "summary": "Search text within selected accessible contracts",
        "description": "Supply up to 50 contract IDs from discovery. Returns bounded current-source citations, indexing coverage and continuation. Ranked search is not exhaustive.",
        "operationId": "searchContractText",
        "tags": [
          "Contracts"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Matches, source references, coverage and pagination",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContractSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "A candidate contract is unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query",
                  "contractIds"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "contractIds": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "string"
                    }
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20,
                    "default": 10
                  },
                  "cursor": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts/generate": {
      "post": {
        "summary": "Generate contract",
        "description": "Generate a new contract from a template.\n\n`values` is a flat map keyed by the variable, valuemap and party ids from `GET /v2/templates/{id}`: a scalar for a variable, the chosen label (an array of labels for a multi-selection valuemap) for a valuemap, and an object of the declared attributes for a party. An unknown key, a wrong type, an unknown label or a missing required value is a 400 naming what is wrong. A value that is not supplied renders as `[●]`.\n\n`options.persist` (default `true`) decides whether Pactly keeps the contract. Persisted contracts are returned as a record you then fetch with `GET /v2/contracts/{id}/download`; ephemeral ones (`\"persist\": false`) keep nothing — no contract, no file, no audit event — and the document comes back base64-encoded in the response.\n\n`options.triggerWorkflows` defaults to the value of `persist` and sends real email to real people, so turn it off for a bulk import. It cannot be enabled without persisting: a workflow has no contract to act on.",
        "tags": [
          "Contracts"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Contract generated. The shape follows options.persist: a contract record, or the document itself.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/GeneratedContract"
                    },
                    {
                      "$ref": "#/components/schemas/GeneratedDocument"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid values, unknown keys, or an incoherent combination of options",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Template not found in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Generation budget for this API key exhausted"
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateContractRequest"
              }
            }
          }
        }
      }
    },
    "/v2/contracts/{id}": {
      "get": {
        "summary": "Get contract details",
        "description": "Retrieve detailed information about a specific contract including all properties, metadata, and status information.",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Route pattern variable `id`"
          }
        ],
        "responses": {
          "200": {
            "description": "Contract details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contract"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/templates/{id}": {
      "get": {
        "summary": "Get template details",
        "description": "Retrieve detailed information about a specific template including variables and value mappings.",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Template ID (MongoDB ObjectId format)"
          }
        ],
        "responses": {
          "200": {
            "description": "Template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Template not found in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/properties/{idOrKey}": {
      "get": {
        "summary": "Get property definition",
        "description": "Retrieve a single property definition by its ID or unique key.",
        "tags": [
          "Properties"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "idOrKey",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Property ID (MongoDB ObjectId) or property key"
          }
        ],
        "responses": {
          "200": {
            "description": "Property definition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Property"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/parties/{id}": {
      "get": {
        "summary": "Get party",
        "description": "Get a single party by id.",
        "tags": [
          "Parties"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Party ID (MongoDB ObjectId format)"
          }
        ],
        "responses": {
          "200": {
            "description": "Party",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "party": {
                      "$ref": "#/components/schemas/Party"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/playbooks/{id}": {
      "get": {
        "summary": "Get playbook",
        "description": "A playbook with its positions: keywords, fallback language, and what a missing clause means for each position (`ifNotFound`).\n\nPositions still awaiting human confirmation are excluded — an AI-suggested position is not policy until someone accepts it — and counted in `unconfirmedPositionCount`. Position notes are limited to drafting guidance; internal rationale and escalation routing are not published.",
        "tags": [
          "Playbooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Playbook ID (MongoDB ObjectId format)"
          }
        ],
        "responses": {
          "200": {
            "description": "Playbook with positions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "playbook": {
                      "$ref": "#/components/schemas/PlaybookDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts/{id}/properties": {
      "put": {
        "summary": "Update contract property values",
        "description": "Set up to 20 property values on a contract the key owner can edit, keyed by property key. Keys must exist in the company property catalogue, including global properties (see GET /v2/properties), and can be added to the contract. Conditional list choices use the resulting parent value; blank numeric strings are rejected. Concurrent writes retry or return 409. Dates take YYYY-MM-DD; null clears a value. Runs with the key owner's current permissions and has the same effect as editing the value in Pactly, including the renewal schedule. Requires the contracts:write scope.",
        "operationId": "updateContractProperties",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Route pattern variable `id`"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated values and the resulting renewal state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "updated": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "expirationDate": {
                      "type": "number",
                      "nullable": true
                    },
                    "renewalAnchorDate": {
                      "type": "string",
                      "nullable": true
                    },
                    "renewalState": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The contract kept changing during the update; retry the request"
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "properties"
                ],
                "properties": {
                  "properties": {
                    "type": "object",
                    "additionalProperties": true,
                    "example": {
                      "expiration_date": "2026-12-31"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts/{id}/status": {
      "put": {
        "summary": "Change contract status",
        "description": "Set the status of a contract the key owner can edit, by code or label from GET /v2/contract-statuses. Same as Change status in Pactly: no workflow transitions run. Requires the contracts:write scope and the owner's permission to change status.",
        "operationId": "setContractStatus",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Route pattern variable `id`"
          }
        ],
        "responses": {
          "200": {
            "description": "The status now set on the contract",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "label": {
                          "type": "string"
                        }
                      }
                    },
                    "changed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "oneOf": [
                      {
                        "type": "integer"
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "example": 1
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts/{id}/text": {
      "get": {
        "summary": "Extract contract text",
        "description": "Extract and return the full text content of a contract. Useful for search, analysis, and AI processing.",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "maxCharacters",
            "schema": {
              "type": "integer",
              "minimum": 1000,
              "maximum": 48000,
              "default": 24000
            },
            "description": "Enable bounded evidence pages with this character budget."
          },
          {
            "in": "query",
            "name": "textCursor",
            "schema": {
              "type": "string",
              "maxLength": 1000
            },
            "description": "Continuation cursor from the previous evidence page."
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Contract ID (MongoDB ObjectId format)"
          }
        ],
        "responses": {
          "200": {
            "description": "Contract text",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ContractTextResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ContractEvidenceResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/contracts/{id}/download": {
      "get": {
        "summary": "Download contract",
        "description": "Download a contract file by ID",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Contract ID (MongoDB ObjectId format)"
          }
        ],
        "responses": {
          "200": {
            "description": "Contract file download",
            "content": {
              "application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/playbooks/{id}/match": {
      "post": {
        "summary": "Match a document against a playbook",
        "description": "Report which playbook position keywords appear in a document, and where.\n\n**This is keyword matching, not a compliance review.** It returns no verdict on whether a position is met — deciding that is a separate AI evaluation. Use the matches, each position's fallback language and its `ifNotFound` setting to reach your own conclusion.\n\nSend `paragraphs` as an array of strings, exactly the shape `GET /v2/contracts/{id}/text` returns. Nothing is persisted: no review, no round, no change to the contract.",
        "tags": [
          "Playbooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "Playbook ID (MongoDB ObjectId format)"
          }
        ],
        "responses": {
          "200": {
            "description": "Keyword matches per position",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybookMatchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: invalid, expired or insufficiently scoped API key, or insufficient owner permissions."
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit for this API key exceeded"
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "paragraphs"
                ],
                "properties": {
                  "paragraphs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 5000,
                    "description": "The document text, one entry per paragraph"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}