Download OpenAPI specification:
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.
The Pactly Public API provides programmatic access to contract lifecycle management capabilities, enabling integration with your existing systems. Key features include:
• Contract Management: Create, retrieve, update, and search contracts across your organization • Template Operations: Access and manage contract templates for consistent document generation • Document Generation: Generate contracts from templates with dynamic variable substitution • Text Extraction: Extract and analyze text content from contracts and documents • Workflow Automation: Trigger workflows and track contract lifecycle events
The API follows RESTful principles and returns JSON responses. All endpoints require authentication via API key.
Operations related to contract management including creation, retrieval, updates, and text extraction
List contracts allowed by the API-key owner current ACL, newest first. Scopes can narrow this access.
Results 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.
Filters 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.
| keyword | string Match against contract name and reference |
| type | string Enum: "template" "playbook" "external" Storage origin, not business type. Discover business types with GET /v2/categories. |
| status | string Comma-separated status codes: 1=Draft, 2=In Negotiation, 3=Pending Approval, 4=Pending Signature, 5=Executed, 6=Terminated. e.g. |
| party | string^[0-9a-fA-F]{24}$ Only contracts linked to this party |
| counterparty | string Only contracts linked to a party matching this name |
| partyRole | string Enum: "counterparty" "signer" "counterparty_representative" "our_representative" Narrow a party or counterparty filter to one role on the contract |
| category | string <= 200 characters Category ID or exact label from GET /v2/categories. Ambiguous labels return 400. |
| owner | string <= 200 characters Company-scoped owner ID or exact email. Ambiguous emails return 400. |
| createdAfter | string <date-time> Only contracts created after this date (ISO 8601) |
| createdBefore | string <date-time> Only contracts created before this date (ISO 8601) |
| updatedAfter | string <date-time> Only contracts updated after this date (ISO 8601) |
| updatedBefore | string <date-time> Only contracts updated before this date (ISO 8601) |
| renewalAfter | string <date-time> Renewal anchor on or after this date. This can be an earlier notice deadline; use expirationAfter/Before for expiration. |
| renewalBefore | string <date-time> Renewal anchor on or before this date |
| expirationAfter | string <date-time> Stored expiration date on or after this ISO date; unknown dates do not match. |
| expirationBefore | string <date-time> Stored expiration date on or before this ISO date. |
| renewalState | string Filter by renewal state |
object Filter by contract property value: | |
| sort | string Default: "createdAt" Enum: "createdAt" "updatedAt" "renewalAnchorDate" Sorting by renewalAnchorDate returns only contracts that have one |
| order | string Default: "desc" Enum: "asc" "desc" |
| cursor | string Opaque cursor from a previous response |
| pagesize | integer [ 1 .. 200 ] Default: 50 Page size (capped at 200) |
{- "contracts": [
- {
- "id": "507f1f77bcf86cd799439011",
- "type": "template",
- "name": "Software License Agreement - Acme Corp",
- "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",
- "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"
}
}Supply up to 50 contract IDs from discovery. Returns bounded current-source citations, indexing coverage and continuation. Ranked search is not exhaustive.
| query required | string [ 1 .. 200 ] characters |
| contractIds required | Array of strings [ 1 .. 50 ] items |
| limit | integer [ 1 .. 20 ] Default: 10 |
| cursor | string |
{- "query": "string",
- "contractIds": [
- "string"
], - "limit": 10,
- "cursor": "string"
}{- "searchMode": "string",
- "hits": [
- {
- "contractId": "string",
- "name": "string",
- "type": "string",
- "revision": "string",
- "matchType": "keyword",
- "evidence": [
- {
- "reference": "string",
- "paragraph": 1,
- "startOffset": 0,
- "endOffset": 0,
- "text": "string"
}
], - "coverage": {
- "totalParagraphs": 0,
- "extraction": "string",
- "complete": true,
- "warning": "string"
}
}
], - "coverage": {
- "state": "available",
- "requestedContracts": 0,
- "missingIndexedText": [
- "string"
], - "exhaustive": false,
- "warning": "string"
}, - "pagination": {
- "hasMore": true,
- "cursor": "string"
}
}Generate a new contract from a template.
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 [●].
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.
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.
| templateId required | string^[0-9a-fA-F]{24}$ Template to generate from |
| contractName | string <= 256 characters Default: "Pactly contract" Name for the generated contract |
object 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 [●]. | |
object |
{- "templateId": "string",
- "contractName": "Pactly contract",
- "values": {
- "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": {
- "persist": true,
- "triggerWorkflows": true,
- "reference": "string"
}
}{- "persisted": true,
- "id": "string",
- "name": "string",
- "reference": "string",
- "template": "string",
- "company": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "effectiveDate": "2019-08-24T14:15:22Z",
- "values": { }
}Retrieve detailed information about a specific contract including all properties, metadata, and status information.
| id required | string Route pattern variable |
{- "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
}
], - "category": {
- "id": "507f1f77bcf86cd799439031",
- "label": "Licensing",
- "abbreviation": "LIC"
}, - "parties": [ ],
- "user": "507f1f77bcf86cd799439012",
- "template": "507f1f77bcf86cd799439013"
}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.
| id required | string Route pattern variable |
required | object |
{- "properties": {
- "expiration_date": "2026-12-31"
}
}{- "id": "string",
- "updated": { },
- "expirationDate": 0,
- "renewalAnchorDate": "string",
- "renewalState": "string"
}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.
| id required | string Route pattern variable |
required | integer or string |
{- "status": 1
}{- "id": "string",
- "status": {
- "code": 0,
- "label": "string"
}, - "changed": true
}Extract and return the full text content of a contract. Useful for search, analysis, and AI processing.
| id required | string^[0-9a-fA-F]{24}$ Contract ID (MongoDB ObjectId format) |
| maxCharacters | integer [ 1000 .. 48000 ] Default: 24000 Enable bounded evidence pages with this character budget. |
| textCursor | string <= 1000 characters Continuation cursor from the previous evidence page. |
{- "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."
]
}Retrieve all available contract templates that can be used to create new contracts.
[- {
- "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"
]
}
]
}
]Retrieve detailed information about a specific template including variables and value mappings.
| id required | string^[0-9a-fA-F]{24}$ Template ID (MongoDB ObjectId format) |
{- "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"
]
}
]
}List all emails associated with contracts (for AI training)
| pagesize | integer Default: 100 Number of emails to return |
| cursor | string Cursor for pagination |
| contractType | string Enum: "template" "playbook" "external" Filter by contract type |
| contractId | string Filter by specific contract ID |
| sender | string Filter by sender email |
| dateFrom | string <date> Filter emails from this date |
| dateTo | string <date> Filter emails until this date |
{- "emails": [
- {
- "id": "string",
- "subject": "string",
- "sender": "string",
- "recipient": "string",
- "body": "string",
- "bodyPlainText": "string",
- "contract": { },
- "attachments": [ ]
}
], - "pagination": {
- "pageSize": 0,
- "hasMore": true,
- "cursor": "string",
- "total": 0
}
}Retrieve all contract property definitions available to the company, including system default properties.
{- "properties": [
- {
- "id": "string",
- "key": "string",
- "label": "string",
- "description": "string",
- "type": "string",
- "options": { },
- "group": "string",
- "subGroup": "string",
- "sortOrder": 0,
- "isDefault": true,
- "manualOnly": true
}
]
}Retrieve a single property definition by its ID or unique key.
| idOrKey required | string Property ID (MongoDB ObjectId) or property key |
{- "id": "string",
- "key": "string",
- "label": "string",
- "description": "string",
- "type": "string",
- "options": { },
- "group": "string",
- "subGroup": "string",
- "sortOrder": 0,
- "isDefault": true,
- "manualOnly": true
}Retrieve all contract categories available to the company, including system default categories.
{- "categories": [
- {
- "id": "string",
- "label": "string",
- "abbreviation": "string",
- "description": "string",
- "isDefault": true
}
]
}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.
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.
| keyword | string Match against party names, entity names and abbreviations |
| isEntity | boolean true returns only organisations, false only individuals; omit for both |
| isInternal | boolean true returns only your own entities, false only external counterparties |
| parent | string^[0-9a-fA-F]{24}$ Return the children of this party — the way to walk a corporate group |
| cursor | string Opaque cursor from a previous response |
| pagesize | integer [ 1 .. 200 ] Default: 50 Page size (capped at 200) |
| order | string Default: "desc" Enum: "asc" "desc" Sort direction on creation date |
{- "parties": [
- {
- "id": "507f1f77bcf86cd799439021",
- "name": "Acme Holdings Pte Ltd",
- "isEntity": true,
- "isInternal": false,
- "entityType": "Private Limited",
- "entityRegNo": "196800306E",
- "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"
}
], - "pagination": {
- "pageSize": 0,
- "hasMore": true,
- "cursor": "string"
}
}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.
{- "partyAttributes": [
- {
- "id": "string",
- "key": "string",
- "label": "string",
- "description": "string",
- "type": "string",
- "options": [
- "string"
], - "partyType": "individual"
}
]
}Get a single party by id.
| id required | string^[0-9a-fA-F]{24}$ Party ID (MongoDB ObjectId format) |
{- "party": {
- "id": "507f1f77bcf86cd799439021",
- "name": "Acme Holdings Pte Ltd",
- "isEntity": true,
- "isInternal": false,
- "entityType": "Private Limited",
- "entityRegNo": "196800306E",
- "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"
}
}List the clause library — the pre-approved language an agent should propose instead of drafting its own.
Visibility 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.
| tag | string Filter by tag |
| cursor | string Opaque cursor from a previous response |
| pagesize | integer [ 1 .. 200 ] Default: 50 |
| order | string Default: "desc" Enum: "asc" "desc" |
{- "clauses": [
- {
- "id": "string",
- "title": "string",
- "text": "string",
- "html": "string",
- "tags": [
- "string"
], - "defaultComment": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
], - "pagination": {
- "pageSize": 0,
- "hasMore": true,
- "cursor": "string"
}
}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.
Positions are omitted here; fetch one playbook to get them.
| cursor | string Opaque cursor from a previous response |
| pagesize | integer [ 1 .. 200 ] Default: 50 |
| order | string Default: "desc" Enum: "asc" "desc" |
{- "playbooks": [
- {
- "id": "string",
- "name": "string",
- "description": "string",
- "version": 0,
- "versionNote": "string",
- "status": "string",
- "isDefault": true,
- "positionCount": 0,
- "unconfirmedPositionCount": 0,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
], - "pagination": {
- "pageSize": 0,
- "hasMore": true,
- "cursor": "string"
}
}A playbook with its positions: keywords, fallback language, and what a missing clause means for each position (ifNotFound).
Positions 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.
| id required | string^[0-9a-fA-F]{24}$ Playbook ID (MongoDB ObjectId format) |
{- "playbook": {
- "id": "string",
- "name": "string",
- "description": "string",
- "version": 0,
- "versionNote": "string",
- "status": "string",
- "isDefault": true,
- "positionCount": 0,
- "unconfirmedPositionCount": 0,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "groups": [
- { }
], - "positions": [
- {
- "order": 0,
- "name": "string",
- "description": "string",
- "groupId": "string",
- "keywords": [
- {
- "value": "string",
- "type": "pattern"
}
], - "examples": [
- {
- "type": "string",
- "title": "string",
- "text": "string"
}
], - "fallbacks": [
- {
- "title": "string",
- "text": "string",
- "html": "string"
}
], - "ifNotFound": {
- "compliant": true,
- "status": "string"
}, - "notes": [
- {
- "type": "technical_drafting",
- "text": "string"
}
]
}
]
}
}Report which playbook position keywords appear in a document, and where.
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.
Send 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.
| id required | string^[0-9a-fA-F]{24}$ Playbook ID (MongoDB ObjectId format) |
| paragraphs required | Array of strings <= 5000 items The document text, one entry per paragraph |
{- "paragraphs": [
- "string"
]
}{- "playbook": {
- "id": "string",
- "name": "string",
- "isDefault": true
}, - "positions": [
- {
- "order": 0,
- "name": "string",
- "ifNotFound": {
- "compliant": true,
- "status": "string"
}, - "matches": [
- {
- "paragraphIndex": 0,
- "keywords": [
- {
- "value": "string",
- "type": "string"
}
]
}
]
}
]
}