openapi: '3.1.1'
info:
  title: DocJacket API
  description: "The DocJacket integration API. One `/api/v1` surface, capability-scoped by credential:\n\n- **Org API key** (`mcp_at_…`) — acts within a single organization. Mint one in DocJacket under **Settings → Advanced → API Keys**. Key scopes: `read` (GETs), `draft` (low-risk writes like completing a task), `actions` (side-effect writes like sending email).\n- **Reseller API key** (`rsk_…`) — cross-tenant provisioning for white-label partners.\n\nSend the key as a bearer token: `Authorization: Bearer <key>`.\n\nErrors use one envelope: `{ \"error\": { \"code\", \"message\", \"fields\"? } }`. Cross-tenant lookups return `404` (never disclosing existence).\n\n**Rate limits.** Per key, per minute, in a fixed window: **300 reads** (GET) and **60 writes** (POST/PUT/PATCH/DELETE). Exceeding a bucket returns `429` with a `Retry-After: 60` header and the standard error envelope (`code: \"rate_limited\"`); retry after the window resets.\n\n**Versioning & deprecation.** The API is **additive by default** — new fields and operations may appear at any time, so clients **must ignore unknown response fields** and not depend on property order. Changes that would break an existing client (removing/renaming a field, tightening a type, changing an error contract) ship only as a **new major version** at a new path (`/api/v2`); `/api/v1` is not broken in place. When a version is deprecated it remains available for at least **6 months** after the successor ships, and the retirement is announced in the release notes. Idempotency-Key is honored on side-effecting writes so retries are safe.\n\nStart with `GET /api/v1/health` (verifies your key), then `GET /api/v1/catalog` (lists every operation and whether your key can call it) and `GET /api/v1/usage` (your key's call volume, top operations, and error rate). The same key also works with AI agents via the DocJacket MCP server at `https://mcp.docjacket.com/mcp`.\n\n**Prefer reading over clicking?** Every operation is also listed on one statically rendered page — no JavaScript required, crawlable and quotable — at [help.docjacket.com/docs/api/reference](https://help.docjacket.com/docs/api/reference), alongside guides for [authentication, requests, webhooks, and the partner tier](https://help.docjacket.com/docs/api). The machine-readable spec is at [api.docjacket.com/openapi.json](https://api.docjacket.com/openapi.json)."
  contact:
    name: DocJacket API support
    url: https://help.docjacket.com/docs/api
    email: support@docjacket.com
  version: v1
servers:
  - url: https://api.docjacket.com
    description: Production
paths:
  /api/v1/orgs:
    get:
      tags:
        - Organizations (Partner)
      summary: List your customer organizations
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: ListOrgs
      parameters:
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
        - name: offset
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 0
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - PartnerApiKey: [ ]
    post:
      tags:
        - Organizations (Partner)
      summary: Provision a customer organization
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: CreateOrg
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrgRequest'
            example:
              name: Peachtree Transaction Services
              owner:
                email: owner@example.com
                firstName: Jordan
                lastName: Rivera
        required: true
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
      security:
        - PartnerApiKey: [ ]
  '/api/v1/orgs/{orgId}':
    get:
      tags:
        - Organizations (Partner)
      summary: Get a customer organization
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: GetOrg
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - PartnerApiKey: [ ]
  '/api/v1/orgs/{orgId}/users':
    post:
      tags:
        - Organizations (Partner)
      summary: Invite a seat into an organization
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: AddSeat
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddSeatRequest'
        required: true
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
      security:
        - PartnerApiKey: [ ]
  '/api/v1/orgs/{orgId}/users/{userId}':
    delete:
      tags:
        - Organizations (Partner)
      summary: Remove a seat from an organization
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: RemoveSeat
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
      security:
        - PartnerApiKey: [ ]
  '/api/v1/orgs/{orgId}/entitlements':
    get:
      tags:
        - Organizations (Partner)
      summary: 'Get an organization''s plan, seats, and AI fair-use pool state'
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: GetOrgEntitlements
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - PartnerApiKey: [ ]
  '/api/v1/orgs/{orgId}/summary':
    get:
      tags:
        - Organizations (Partner)
      summary: 'Get an organization''s activity summary (active deals, tasks due, last activity)'
      description: '**Requires a partner key** (`Authorization: Bearer rsk_…`), not an organization key. Scoped to the organizations your partner account owns — one you do not own returns `404`, never `403`, so ownership is never disclosed. Never infer access to a tenant from an id obtained elsewhere.'
      operationId: GetOrgSummary
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - PartnerApiKey: [ ]
  /api/v1/health:
    get:
      tags:
        - Meta
      summary: Check API key authentication and service health
      description: '**Requires the `read` scope.**'
      operationId: OrgHealth
      responses:
        '200':
          description: OK
          content:
            application/json:
              example:
                ok: true
                organizationId: org-9fc8a1d2e3b4
                scopes:
                  - read
                  - draft
                  - actions
                databaseReachable: true
                databaseError: null
                apiVersion: v1
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/catalog:
    get:
      tags:
        - Meta
      summary: List operations available to your API key
      description: '**Requires the `read` scope.**'
      operationId: OrgCatalog
      responses:
        '200':
          description: OK
          content:
            application/json:
              example:
                data:
                  - operation: list_transactions
                    method: GET
                    path: /api/v1/transactions
                    scope: read
                    callable: true
                  - operation: send_client_update
                    method: POST
                    path: '/api/v1/transactions/{id}/emails/client-update'
                    scope: actions
                    callable: false
                scopes:
                  - read
                  - draft
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}':
    get:
      tags:
        - Transactions
      summary: Get a transaction by ID
      description: '**Requires the `read` scope.**'
      operationId: GetTransaction
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/transactions:
    get:
      tags:
        - Transactions
      summary: List or search transactions
      description: '**Requires the `read` scope.**'
      operationId: ListTransactions
      parameters:
        - name: q
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 1
        - name: pageSize
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Transactions
      summary: 'Apply a completed extraction — create a new transaction, or attach to an existing one via transactionId'
      description: '**Requires the `actions` scope.**'
      operationId: ApplyExtraction
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ApplyExtractionRequest'
            example:
              extractionJobId: 6b1f9c4d-8e2a-4b7c-9f10-3d5a7e2c8b41
              transactionType: Sale
              side: Buyer
              overrides:
                purchasePrice: 485000
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/contacts:
    get:
      tags:
        - Contacts
      summary: List or search contacts
      description: '**Requires the `read` scope.**'
      operationId: ListContacts
      parameters:
        - name: q
          in: query
          schema:
            type: string
        - name: type
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 1
        - name: pageSize
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Contacts
      summary: Create a contact
      description: '**Requires the `actions` scope.**'
      operationId: CreateContact
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateContactRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/key-dates/upcoming:
    get:
      tags:
        - Key Dates
      summary: List upcoming key dates across active transactions
      description: '**Requires the `read` scope.**'
      operationId: UpcomingKeyDates
      parameters:
        - name: horizonDays
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 14
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/key-dates':
    get:
      tags:
        - Transactions
      summary: List key dates for a transaction
      description: '**Requires the `read` scope.**'
      operationId: GetKeyDates
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Transactions
      summary: Add key dates to a transaction (batch)
      description: '**Requires the `draft` scope.**'
      operationId: AddKeyDates
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/AddKeyDatesRequest'
            example:
              keyDates:
                - keyDateType: ClosingDate
                  dateValue: '2026-09-15T00:00:00Z'
                  isCustom: false
                  label: null
                - keyDateType: InspectionDeadline
                  dateValue: '2026-08-22T00:00:00Z'
                  isCustom: false
                  label: null
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/contacts/{contactId}':
    get:
      tags:
        - Contacts
      summary: Get a contact by ID
      description: '**Requires the `read` scope.**'
      operationId: GetContact
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: activityLimit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 10
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/tasks:
    get:
      tags:
        - Tasks
      summary: List open tasks
      description: '**Requires the `read` scope.**'
      operationId: ListTasks
      parameters:
        - name: transactionId
          in: query
          schema:
            format: uuid
        - name: assignedTo
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/tasks/{taskId}/complete':
    post:
      tags:
        - Tasks
      summary: Mark a task complete
      description: '**Requires the `draft` scope.**'
      operationId: CompleteTask
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CompleteTaskRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/activity':
    post:
      tags:
        - Transactions
      summary: Log an activity on a transaction
      description: '**Requires the `draft` scope.**'
      operationId: LogActivity
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/LogActivityRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/checklist':
    post:
      tags:
        - Transactions
      summary: Apply a checklist template to a transaction
      description: '**Requires the `draft` scope.**'
      operationId: ApplyChecklist
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ApplyChecklistRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
    get:
      tags:
        - Transactions
      summary: Get aggregate checklist status for a transaction
      description: '**Requires the `read` scope.**'
      operationId: GetChecklistStatus
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/contact-roles:
    post:
      tags:
        - Contacts
      summary: Create a custom contact role
      description: '**Requires the `actions` scope.**'
      operationId: CreateContactRole
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateContactRoleRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/email-templates:
    post:
      tags:
        - Templates & Forms
      summary: Create an email template
      description: '**Requires the `actions` scope.**'
      operationId: CreateEmailTemplate
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateEmailTemplateRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
    get:
      tags:
        - Templates & Forms
      summary: 'List email templates (org + system, active only)'
      description: '**Requires the `read` scope.**'
      operationId: ListEmailTemplates
      parameters:
        - name: category
          in: query
          schema:
            type: string
        - name: audience
          in: query
          schema:
            type: string
        - name: stage
          in: query
          schema:
            type: string
        - name: side
          in: query
          schema:
            type: string
        - name: search
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/tasks/delete:
    post:
      tags:
        - Tasks
      summary: 'Delete tasks (bulk, permanent)'
      description: '**Requires the `actions` scope.**'
      operationId: DeleteTasks
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/DeleteTasksRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/transactions/resolve:
    get:
      tags:
        - Transactions
      summary: Resolve a property reference to ranked transaction matches
      description: '**Requires the `read` scope.**'
      operationId: ResolveTransactionByProperty
      parameters:
        - name: property
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 3
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/contingencies':
    get:
      tags:
        - Transactions
      summary: List open contingencies for a transaction
      description: '**Requires the `read` scope.**'
      operationId: ListOpenContingencies
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/missing-documents':
    get:
      tags:
        - Transactions
      summary: List documents missing against the purchase baseline
      description: '**Requires the `read` scope.**'
      operationId: GetMissingDocuments
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/intake-status':
    get:
      tags:
        - Transactions
      summary: Get the intake-progress snapshot for a transaction
      description: '**Requires the `read` scope.**'
      operationId: GetIntakeStatus
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/next-actions:
    get:
      tags:
        - Meta
      summary: Get the ranked next-actions feed (tasks + key dates)
      description: '**Requires the `read` scope.**'
      operationId: GetNextActions
      parameters:
        - name: transactionId
          in: query
          schema:
            format: uuid
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/documents:
    get:
      tags:
        - Documents
      summary: List recently-uploaded documents with extraction state
      description: '**Requires the `read` scope.**'
      operationId: ListDocuments
      parameters:
        - name: transactionId
          in: query
          schema:
            format: uuid
        - name: since
          in: query
          schema:
            format: date-time
        - name: hasExtraction
          in: query
          schema: { }
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Documents
      summary: Upload a document (base64) and start extraction
      description: '**Requires the `actions` scope.**'
      operationId: UploadDocumentForExtraction
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/UploadDocumentRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/documents/{documentId}/view-url':
    get:
      tags:
        - Documents
      summary: Get a short-lived presigned URL to view a document
      description: '**Requires the `read` scope.**'
      operationId: GetDocumentViewUrl
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/extractions/{jobId}':
    get:
      tags:
        - Extractions
      summary: Get the state and results of an extraction job
      description: '**Requires the `read` scope.**'
      operationId: GetExtractionResults
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/extractions/{jobId}/source-url':
    get:
      tags:
        - Extractions
      summary: Get a short-lived presigned URL to view an extraction's source PDF
      description: '**Requires the `read` scope.**'
      operationId: GetExtractionSourceUrl
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/documents/classify:
    post:
      tags:
        - Documents
      summary: Classify a document by filename and/or content preview
      description: '**Requires the `read` scope.**'
      operationId: ClassifyDocument
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ClassifyDocumentRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/notifications:
    get:
      tags:
        - Organization
      summary: List the signed-in user's notification inbox
      description: '**Requires the `read` scope.**'
      operationId: ListNotifications
      parameters:
        - name: unreadOnly
          in: query
          schema: { }
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/notifications/unread-count:
    get:
      tags:
        - Organization
      summary: Unread notification count for the signed-in user
      description: '**Requires the `read` scope.**'
      operationId: NotificationUnreadCount
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/conversations:
    get:
      tags:
        - Messaging
      summary: List the organization's message threads
      description: '**Requires the `read` scope.**'
      operationId: ListConversations
      parameters:
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/conversations/{threadId}/messages':
    get:
      tags:
        - Messaging
      summary: List messages in a conversation thread
      description: '**Requires the `read` scope.**'
      operationId: ListConversationMessages
      parameters:
        - name: threadId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Messaging
      summary: Reply with an SMS in an existing conversation thread
      description: '**Requires the `actions` scope.**'
      operationId: SendConversationMessage
      parameters:
        - name: threadId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ReplyMessageRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/email-templates/{idOrSlug}':
    get:
      tags:
        - Templates & Forms
      summary: Get an email template by ID or slug
      description: '**Requires the `read` scope.**'
      operationId: GetEmailTemplate
      parameters:
        - name: idOrSlug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/merge-fields':
    get:
      tags:
        - Transactions
      summary: Get all merge field values for a transaction
      description: '**Requires the `read` scope.**'
      operationId: GetMergeFields
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/render-template':
    post:
      tags:
        - Transactions
      summary: Render an email template against a transaction (preview-only)
      description: '**Requires the `read` scope.**'
      operationId: RenderEmailTemplate
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/RenderTemplateRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/checklist-templates:
    get:
      tags:
        - Templates & Forms
      summary: List timeline/checklist templates (org + system)
      description: '**Requires the `read` scope.**'
      operationId: ListChecklistTemplates
      parameters:
        - name: state
          in: query
          schema:
            type: string
        - name: side
          in: query
          schema:
            type: string
        - name: transactionType
          in: query
          schema:
            type: string
        - name: category
          in: query
          schema:
            type: string
        - name: complexity
          in: query
          schema:
            type: string
        - name: activeOnly
          in: query
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/form-links':
    get:
      tags:
        - Transactions
      summary: List form links sent on a transaction
      description: '**Requires the `read` scope.**'
      operationId: ListFormLinks
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/forms/{formId}':
    get:
      tags:
        - Templates & Forms
      summary: Get a form template with its field list
      description: '**Requires the `read` scope.**'
      operationId: GetFormDefinition
      parameters:
        - name: formId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/form-submissions:
    get:
      tags:
        - Templates & Forms
      summary: List form submissions (org-wide or per transaction)
      description: '**Requires the `read` scope.**'
      operationId: ListFormSubmissions
      parameters:
        - name: transactionId
          in: query
          schema:
            format: uuid
        - name: formDefinitionId
          in: query
          schema:
            format: uuid
        - name: formPurpose
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            default: pending_review
        - name: since
          in: query
          schema:
            format: date-time
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/form-submissions/{submissionId}':
    get:
      tags:
        - Templates & Forms
      summary: Get field-level detail for a form submission
      description: '**Requires the `read` scope.**'
      operationId: GetFormSubmission
      parameters:
        - name: submissionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/portal-link':
    get:
      tags:
        - Transactions
      summary: Get the active client-portal link for a transaction
      description: '**Requires the `read` scope.**'
      operationId: GetPortalLink
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/disclosure-packages/{packageId}':
    get:
      tags:
        - Disclosure Packages
      summary: Get a disclosure package with items and recipients
      description: '**Requires the `read` scope.**'
      operationId: GetDisclosurePackage
      parameters:
        - name: packageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/disclosure-packages/{packageId}/activity':
    get:
      tags:
        - Disclosure Packages
      summary: Get the recipient-activity report for a disclosure package
      description: '**Requires the `read` scope.**'
      operationId: GetDisclosureActivity
      parameters:
        - name: packageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/contacts/search:
    get:
      tags:
        - Contacts
      summary: 'Fuzzy-search contacts by name, email, phone, or company'
      description: '**Requires the `read` scope.**'
      operationId: SearchContacts
      parameters:
        - name: q
          in: query
          schema:
            type: string
        - name: contactType
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 25
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/contacts/by-email:
    get:
      tags:
        - Contacts
      summary: 'Find contacts by exact email, with their transactions'
      description: '**Requires the `read` scope.**'
      operationId: FindContactByEmail
      parameters:
        - name: email
          in: query
          schema:
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/tasks':
    post:
      tags:
        - Transactions
      summary: Create a task on a transaction
      description: '**Requires the `draft` scope.**'
      operationId: CreateTask
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateTaskRequest'
            example:
              title: Order home inspection
              description: Book the inspector and confirm access with the listing agent.
              dueDate: '2026-08-18T00:00:00Z'
              priority: 4
              linkedMilestoneName: InspectionDeadline
              assigneeUserId: null
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/key-dates/{keyDateType}':
    patch:
      tags:
        - Transactions
      summary: Update a key date on a transaction
      description: '**Requires the `draft` scope.**'
      operationId: UpdateKeyDate
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: keyDateType
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/UpdateKeyDateRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/status-summary':
    post:
      tags:
        - Transactions
      summary: Save a status summary on a transaction
      description: '**Requires the `draft` scope.**'
      operationId: SaveStatusSummary
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/StatusSummaryRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/reminders':
    post:
      tags:
        - Transactions
      summary: Schedule a reminder on a transaction
      description: '**Requires the `actions` scope.**'
      operationId: CreateReminder
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateReminderRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/tasks/{taskId}/reschedule':
    post:
      tags:
        - Tasks
      summary: Reschedule (postpone) a task's due date
      description: '**Requires the `draft` scope.**'
      operationId: RescheduleTask
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/RescheduleTaskRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/tasks/{taskId}/assign':
    post:
      tags:
        - Tasks
      summary: Reassign a task to a team member
      description: '**Requires the `draft` scope.**'
      operationId: AssignTask
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/AssignTaskRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/org/users:
    get:
      tags:
        - Organization
      summary: List the organization's assignable team members
      description: '**Requires the `read` scope.**'
      operationId: ListOrgUsers
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/tasks/{taskId}/comments':
    get:
      tags:
        - Tasks
      summary: List a task's comment thread
      description: '**Requires the `read` scope.**'
      operationId: ListTaskComments
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Tasks
      summary: Add a comment to a task
      description: '**Requires the `draft` scope.**'
      operationId: CreateTaskComment
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateTaskCommentRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/tasks/{taskId}/comments/{commentId}':
    delete:
      tags:
        - Tasks
      summary: Delete a task comment (author only)
      description: '**Requires the `draft` scope.**'
      operationId: DeleteTaskComment
      parameters:
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: commentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/contacts':
    post:
      tags:
        - Transactions
      summary: Link an existing contact to a transaction with a role
      description: '**Requires the `actions` scope.**'
      operationId: AddContactToTransaction
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/LinkContactRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/documents/upload-url:
    post:
      tags:
        - Documents
      summary: Get a presigned PUT URL for a document upload
      description: '**Requires the `actions` scope.**'
      operationId: RequestUploadUrl
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/UploadUrlRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/extractions:
    post:
      tags:
        - Extractions
      summary: Start extraction from a presigned upload
      description: '**Requires the `actions` scope.**'
      operationId: KickOffExtraction
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/KickOffExtractionRequest'
            example:
              uploadId: d0f6b1a2-4c3e-4f1b-9a7d-2e5c8b0a1f34
              filename: purchase-agreement.pdf
              state: GA
              documentTypeHint: PurchaseAgreement
              transactionId: null
              side: Buyer
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/documents/{documentId}/extract':
    post:
      tags:
        - Documents
      summary: Start extraction for a document already in DocJacket
      description: '**Requires the `actions` scope.**'
      operationId: ExtractExistingDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ExtractExistingRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/documents/{documentId}':
    patch:
      tags:
        - Documents
      summary: Rename or re-classify a document
      description: '**Requires the `draft` scope.**'
      operationId: UpdateDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/UpdateDocumentRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags:
        - Documents
      summary: Soft-delete a document
      description: '**Requires the `draft` scope.**'
      operationId: DeleteDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/documents/types:
    get:
      tags:
        - Documents
      summary: List the canonical document-type vocabulary
      description: '**Requires the `read` scope.**'
      operationId: ListDocumentTypes
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/documents/reorder':
    post:
      tags:
        - Transactions
      summary: Set the order of a transaction's documents
      description: '**Requires the `draft` scope.**'
      operationId: ReorderDocuments
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ReorderDocumentsRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/documents/{documentId}/versions':
    post:
      tags:
        - Documents
      summary: Replace a document with a new uploaded version
      description: '**Requires the `draft` scope.**'
      operationId: ReplaceDocumentVersion
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ReplaceDocumentVersionRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/documents/{documentId}/split':
    post:
      tags:
        - Documents
      summary: Split a multi-page PDF into separate documents
      description: '**Requires the `draft` scope.**'
      operationId: SplitDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/SplitDocumentRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/draft-message':
    post:
      tags:
        - Messaging
      summary: Generate an AI message draft (never sends)
      description: '**Requires the `draft` scope.**'
      operationId: DraftMessage
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/MessageDraftRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/communications':
    get:
      tags:
        - Messaging
      summary: List every message for a transaction across channels (SMS + email + in-app)
      description: '**Requires the `read` scope.**'
      operationId: ListCommunications
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/emails/client-update':
    post:
      tags:
        - Messaging
      summary: Send a status-update email to a deal party
      description: '**Requires the `actions` scope.**'
      operationId: SendClientUpdateEmail
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ClientUpdateEmailRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/emails/document-request':
    post:
      tags:
        - Messaging
      summary: Send a document-request email to a deal party
      description: '**Requires the `actions` scope.**'
      operationId: SendDocumentRequestEmail
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/DocumentRequestEmailRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/emails/agent-followup':
    post:
      tags:
        - Messaging
      summary: Send a follow-up email to an agent or vendor
      description: '**Requires the `actions` scope.**'
      operationId: SendAgentFollowupEmail
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/AgentFollowupEmailRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/emails/agent':
    post:
      tags:
        - Messaging
      summary: Send an email to an agent on the deal
      description: '**Requires the `actions` scope.**'
      operationId: SendEmailToAgent
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/AgentEmailRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/conversations':
    post:
      tags:
        - Messaging
      summary: Start a conversation by sending the first SMS on a deal
      description: '**Requires the `actions` scope.**'
      operationId: StartConversation
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/StartConversationRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/scheduled-messages':
    post:
      tags:
        - Messaging
      summary: Schedule an email to send later on a deal
      description: '**Requires the `actions` scope.**'
      operationId: ScheduleMessage
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ScheduleMessageRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
    get:
      tags:
        - Messaging
      summary: List active scheduled messages on a deal
      description: '**Requires the `read` scope.**'
      operationId: ListScheduledMessages
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/scheduled-messages/{messageId}':
    delete:
      tags:
        - Messaging
      summary: Cancel a pending scheduled message
      description: '**Requires the `actions` scope.**'
      operationId: CancelScheduledMessage
      parameters:
        - name: messageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/messages':
    post:
      tags:
        - Messaging
      summary: Send an email on a deal (appears in the merged thread as channel=email)
      description: '**Requires the `actions` scope.**'
      operationId: SendMessage
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/SendEmailMessageRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/recipients':
    get:
      tags:
        - Messaging
      summary: List a deal's participants with role and per-channel availability
      description: '**Requires the `read` scope.**'
      operationId: ListRecipients
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: channel
          in: query
          schema:
            type: string
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/conversations/{threadId}/documents':
    post:
      tags:
        - Messaging
      summary: Share a document into an existing conversation via secure SMS link
      description: '**Requires the `actions` scope.**'
      operationId: ShareDocumentToConversation
      parameters:
        - name: threadId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ShareToThreadRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/transactions/{transactionId}/documents/{documentId}/share':
    post:
      tags:
        - Transactions
      summary: Share a document to a party on a deal via secure SMS link
      description: '**Requires the `actions` scope.**'
      operationId: ShareDocumentToParty
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ShareToPartyRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  /api/v1/disclosure-packages:
    post:
      tags:
        - Disclosure Packages
      summary: Create a disclosure package (optionally attach documents and activate)
      description: '**Requires the `actions` scope.**'
      operationId: CreateDisclosurePackage
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreatePackageRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/disclosure-packages/{packageId}/documents':
    put:
      tags:
        - Disclosure Packages
      summary: Replace a disclosure package's document list
      description: '**Requires the `actions` scope.**'
      operationId: UpdateDisclosurePackageDocuments
      parameters:
        - name: packageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ReplaceDocumentsRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/disclosure-packages/{packageId}/activate':
    post:
      tags:
        - Disclosure Packages
      summary: Activate a disclosure package (idempotent)
      description: '**Requires the `actions` scope.**'
      operationId: ActivateDisclosurePackage
      parameters:
        - name: packageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/disclosure-packages/{packageId}/recipients':
    post:
      tags:
        - Disclosure Packages
      summary: Invite a recipient to a disclosure package (sends the share email)
      description: '**Requires the `actions` scope.**'
      operationId: InviteDisclosureRecipient
      parameters:
        - name: packageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: 'Optional. Send a unique value (max 200 characters — a UUID is ideal) to make this call safe to retry. The first request executes and its response is stored; an identical retry replays that stored response verbatim with an `Idempotency-Replayed: true` header instead of performing the side effect again. Reusing a key for a *different* operation is rejected with `422`.'
          schema:
            maxLength: 200
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/InviteRecipientRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
  '/api/v1/disclosure-packages/{packageId}/recipients/{recipientId}':
    delete:
      tags:
        - Disclosure Packages
      summary: Revoke a disclosure recipient's access (idempotent)
      description: '**Requires the `actions` scope.**'
      operationId: RevokeDisclosureRecipient
      parameters:
        - name: packageId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: recipientId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/account/deletion/status:
    get:
      tags:
        - Organization
      summary: 'Whether workspace deletion is scheduled, and whether the caller may act on it'
      description: "Reports whether this workspace is scheduled for deletion, when the purge falls due, and how long the grace period is. Readable by any member with the read scope, because a pending deletion ends the workspace for all of them. The `isOwner` flag tells a client whether to offer the delete and export controls at all, so it can hide them rather than render a button that fails.\n\n**Requires the `read` scope.**"
      operationId: AccountDeletionStatus
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/account/deletion/request:
    post:
      tags:
        - Organization
      summary: Schedule this workspace for deletion after the grace period
      description: "Schedules the workspace for permanent deletion once the grace period elapses, freezes it, and emails a cancel link. Nothing is deleted at the time of the call. Owner only, and `confirmName` must equal the workspace name exactly — a mismatch returns 422 and schedules nothing.\n\n**Requires the `actions` scope.**"
      operationId: RequestAccountDeletion
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/RequestAccountDeletionBody'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/account/deletion/cancel:
    post:
      tags:
        - Organization
      summary: Cancel a scheduled workspace deletion
      description: "Cancels a pending deletion and unfreezes the workspace — the in-app equivalent of the emailed cancel link, for an owner who is still signed in. Owner only. Returns `cancelled: false` when there was nothing scheduled.\n\n**Requires the `actions` scope.**"
      operationId: CancelAccountDeletion
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/account/export/latest:
    get:
      tags:
        - Organization
      summary: Status of the most recent workspace data export
      description: "Returns the most recent export for this workspace with its build progress, or `exists: false` if one has never been requested. A completed export whose artifact has passed its expiry reports as `expired`, since the file is gone. Owner only.\n\n**Requires the `read` scope.**"
      operationId: LatestAccountExport
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/account/export/request:
    post:
      tags:
        - Organization
      summary: Start building a full export of this workspace's data
      description: "Starts building a full archive of this workspace's data; the finished download arrives by email, so there is nothing to fetch from this response. Owner only. One export runs at a time — calling again while one is building returns that one with `alreadyRunning: true` rather than starting a second.\n\n**Requires the `actions` scope.**"
      operationId: RequestAccountExport
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/webhook-events:
    get:
      tags:
        - Webhooks
      summary: List the webhook event types you can subscribe to
      description: '**Requires the `read` scope.**'
      operationId: ListWebhookEvents
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/webhooks:
    get:
      tags:
        - Webhooks
      summary: List your webhook subscriptions
      description: '**Requires the `read` scope.**'
      operationId: ListWebhooks
      parameters:
        - name: includeInactive
          in: query
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
        - Webhooks
      summary: Create a webhook subscription
      description: '**Requires the `actions` scope.**'
      operationId: CreateWebhook
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/CreateWebhookApiRequest'
            example:
              name: Ops pipeline
              url: https://example.com/hooks/docjacket
              events:
                - extraction.completed
                - key_date.updated
                - transaction.status_changed
              description: Feeds our internal deal dashboard.
              headers: null
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/webhooks/{id}':
    get:
      tags:
        - Webhooks
      summary: Get a webhook subscription by ID
      description: '**Requires the `read` scope.**'
      operationId: GetWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    put:
      tags:
        - Webhooks
      summary: Update a webhook subscription
      description: '**Requires the `actions` scope.**'
      operationId: UpdateWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/UpdateWebhookApiRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags:
        - Webhooks
      summary: Delete a webhook subscription
      description: '**Requires the `actions` scope.**'
      operationId: DeleteWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/webhooks/{id}/test':
    post:
      tags:
        - Webhooks
      summary: Send a test delivery to a webhook subscription
      description: '**Requires the `actions` scope.**'
      operationId: TestWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/webhooks/{id}/regenerate-secret':
    post:
      tags:
        - Webhooks
      summary: Rotate a webhook subscription's signing secret
      description: '**Requires the `actions` scope.**'
      operationId: RegenerateWebhookSecret
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/webhooks/{id}/deliveries':
    get:
      tags:
        - Webhooks
      summary: List recent delivery attempts for a webhook subscription
      description: '**Requires the `read` scope.**'
      operationId: ListWebhookDeliveries
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 50
        - name: offset
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 0
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/usage:
    get:
      tags:
        - Meta
      summary: 'Get call-volume, daily breakdown, top operations, and error rate for your API key'
      description: '**Requires the `read` scope.**'
      operationId: GetUsage
      parameters:
        - name: days
          in: query
          schema:
            pattern: '^-?(?:0|[1-9]\d*)$'
            type:
              - integer
              - string
            format: int32
            default: 30
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/devices/register:
    post:
      tags:
        - Organization
      summary: Register or refresh a mobile device for the authenticated user
      description: '**Requires the `actions` scope.**'
      operationId: RegisterDevice
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/RegisterDeviceRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/transaction-statuses:
    get:
      tags:
        - Organization
      summary: List the organization's selectable transaction statuses
      description: '**Requires the `read` scope.**'
      operationId: ListTransactionStatuses
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/status':
    patch:
      tags:
        - Transactions
      summary: Change a transaction's status (runs the same cascade as the web app)
      description: '**Requires the `actions` scope.**'
      operationId: ChangeTransactionStatus
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/ChangeStatusRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/transactions/{transactionId}/reports/communication-log':
    get:
      tags:
        - Messaging
      summary: 'Export a transaction''s communication log (email + SMS) as a PDF, ZIP (with documents), or JSON'
      description: '**Requires the `read` scope.**'
      operationId: ExportCommunicationLog
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: format
          in: query
          schema:
            type: string
        - name: from
          in: query
          schema:
            format: date-time
        - name: to
          in: query
          schema:
            format: date-time
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/transactions/{transactionId}/notes':
    patch:
      tags:
        - Transactions
      summary: Set or clear a transaction's general notes
      description: '**Requires the `draft` scope.**'
      operationId: UpdateTransactionNotes
      parameters:
        - name: transactionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/UpdateNotesRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api/v1/proposals:
    get:
      tags:
        - Proposals
      summary: List pending AI proposals for the organization
      description: '**Requires the `read` scope.**'
      operationId: ListProposals
      parameters:
        - name: status
          in: query
          schema:
            type: string
        - name: transactionId
          in: query
          schema:
            format: uuid
        - name: limit
          in: query
          schema:
            format: int32
        - name: offset
          in: query
          schema:
            format: int32
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/proposals/{id}':
    get:
      tags:
        - Proposals
      summary: Get one AI proposal with its evidence
      description: '**Requires the `read` scope.**'
      operationId: GetProposal
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  '/api/v1/proposals/{id}/approve':
    post:
      tags:
        - Proposals
      summary: Approve an AI proposal
      description: '**Requires the `actions` scope.**'
      operationId: ApproveProposal
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/proposals/{id}/reject':
    post:
      tags:
        - Proposals
      summary: Reject an AI proposal
      description: '**Requires the `actions` scope.**'
      operationId: RejectProposal
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/RejectProposalRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  '/api/v1/proposals/{id}/payload':
    put:
      tags:
        - Proposals
      summary: Edit a proposal's proposed value before approving
      description: '**Requires the `actions` scope.**'
      operationId: EditProposalPayload
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: 'null'
                - $ref: '#/components/schemas/EditProposalPayloadRequest'
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
components:
  schemas:
    AddKeyDatesRequest:
      required:
        - keyDates
      type: object
      properties:
        keyDates:
          type: array
          items:
            $ref: '#/components/schemas/KeyDateInput'
    AddSeatRequest:
      required:
        - email
      type: object
      properties:
        email:
          type: string
        firstName:
          type:
            - 'null'
            - string
        lastName:
          type:
            - 'null'
            - string
        role:
          type:
            - 'null'
            - string
    AgentEmailRequest:
      required:
        - recipientContactId
        - recipientRole
        - intent
        - subject
        - bodyHtml
      type: object
      properties:
        recipientContactId:
          type:
            - 'null'
            - string
          format: uuid
        recipientRole:
          type:
            - 'null'
            - string
        intent:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        bodyHtml:
          type:
            - 'null'
            - string
        bodyPlainText:
          type:
            - 'null'
            - string
    AgentFollowupEmailRequest:
      required:
        - recipientContactId
        - recipientRole
        - topic
        - subject
        - bodyHtml
      type: object
      properties:
        recipientContactId:
          type:
            - 'null'
            - string
          format: uuid
        recipientRole:
          type:
            - 'null'
            - string
        topic:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        bodyHtml:
          type:
            - 'null'
            - string
        bodyPlainText:
          type:
            - 'null'
            - string
        tone:
          type:
            - 'null'
            - string
        sourceContextSummary:
          type:
            - 'null'
            - string
    ApplyChecklistRequest:
      required:
        - templateId
      type: object
      properties:
        templateId:
          type: string
          format: uuid
        replaceExisting:
          type:
            - 'null'
            - boolean
        scope:
          type:
            - 'null'
            - string
    ApplyExtractionRequest:
      required:
        - extractionJobId
      type: object
      properties:
        extractionJobId:
          type:
            - 'null'
            - string
          format: uuid
        transactionType:
          type:
            - 'null'
            - string
        side:
          type:
            - 'null'
            - string
        overrides:
          oneOf:
            - type: 'null'
            - $ref: '#/components/schemas/JsonElement'
        transactionId:
          type:
            - 'null'
            - string
    AssignTaskRequest:
      type: object
      properties:
        assigneeUserId:
          type:
            - 'null'
            - string
          format: uuid
    ChangeStatusRequest:
      type: object
      properties:
        status:
          type:
            - 'null'
            - string
    ClassifyDocumentRequest:
      type: object
      properties:
        filename:
          type:
            - 'null'
            - string
        contentPreview:
          type:
            - 'null'
            - string
    ClientUpdateEmailRequest:
      required:
        - recipientContactId
        - audience
        - focus
        - subject
        - bodyHtml
      type: object
      properties:
        recipientContactId:
          type:
            - 'null'
            - string
          format: uuid
        audience:
          type:
            - 'null'
            - string
        focus:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        bodyHtml:
          type:
            - 'null'
            - string
        bodyPlainText:
          type:
            - 'null'
            - string
        tone:
          type:
            - 'null'
            - string
        attachmentDocumentIds:
          type:
            - 'null'
            - array
          items:
            type: string
    CompleteTaskRequest:
      type: object
      properties:
        completionNotes:
          type:
            - 'null'
            - string
    CreateContactRequest:
      type: object
      properties:
        firstName:
          type:
            - 'null'
            - string
        lastName:
          type:
            - 'null'
            - string
        email:
          type:
            - 'null'
            - string
        phone:
          type:
            - 'null'
            - string
        company:
          type:
            - 'null'
            - string
        licenseNumber:
          type:
            - 'null'
            - string
        contactType:
          type:
            - 'null'
            - string
        kind:
          type:
            - 'null'
            - string
        streetAddress:
          type:
            - 'null'
            - string
        city:
          type:
            - 'null'
            - string
        state:
          type:
            - 'null'
            - string
        zipCode:
          type:
            - 'null'
            - string
        notes:
          type:
            - 'null'
            - string
    CreateContactRoleRequest:
      type: object
      properties:
        name:
          type:
            - 'null'
            - string
        category:
          type:
            - 'null'
            - string
        description:
          type:
            - 'null'
            - string
        allowMultiple:
          type:
            - 'null'
            - boolean
        maxInstances:
          type:
            - 'null'
            - integer
          format: int32
        partyKey:
          type:
            - 'null'
            - string
        ignoreSimilar:
          type:
            - 'null'
            - boolean
    CreateEmailTemplateRequest:
      type: object
      properties:
        name:
          type:
            - 'null'
            - string
        category:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        body:
          type:
            - 'null'
            - string
        description:
          type:
            - 'null'
            - string
        audience:
          type:
            - 'null'
            - string
        stage:
          type:
            - 'null'
            - string
        side:
          type:
            - 'null'
            - string
        tone:
          type:
            - 'null'
            - string
        isActive:
          type:
            - 'null'
            - boolean
    CreateOrgRequest:
      required:
        - name
        - owner
      type: object
      properties:
        name:
          type: string
        owner:
          $ref: '#/components/schemas/OwnerBody'
    CreatePackageRequest:
      type: object
      properties:
        transactionId:
          type:
            - 'null'
            - string
          format: uuid
        name:
          type:
            - 'null'
            - string
        coverMessage:
          type:
            - 'null'
            - string
        documentIds:
          type:
            - 'null'
            - array
          items:
            type: string
            format: uuid
        allowDownloads:
          type:
            - 'null'
            - boolean
        requireAck:
          type:
            - 'null'
            - boolean
        expiresAt:
          type:
            - 'null'
            - string
          format: date-time
    CreateReminderRequest:
      type: object
      properties:
        milestoneId:
          type:
            - 'null'
            - string
          format: uuid
        deadlineType:
          type:
            - 'null'
            - string
        deadlineDate:
          type:
            - 'null'
            - string
          format: date-time
        daysBeforeReminder:
          type:
            - 'null'
            - integer
          format: int32
        recipientEmails:
          type:
            - 'null'
            - string
        recipientRoles:
          type:
            - 'null'
            - string
        emailTemplateId:
          type:
            - 'null'
            - string
          format: uuid
        note:
          type:
            - 'null'
            - string
        reminderTimeOfDay:
          type:
            - 'null'
            - string
    CreateTaskCommentRequest:
      required:
        - content
      type: object
      properties:
        content:
          type: string
    CreateTaskRequest:
      required:
        - title
      type: object
      properties:
        title:
          type:
            - 'null'
            - string
        description:
          type:
            - 'null'
            - string
        dueDate:
          type:
            - 'null'
            - string
          format: date-time
        priority:
          type:
            - 'null'
            - integer
          format: int32
        linkedMilestoneName:
          type:
            - 'null'
            - string
        assigneeUserId:
          type:
            - 'null'
            - string
          format: uuid
    CreateWebhookApiRequest:
      required:
        - name
        - url
        - events
      type: object
      properties:
        name:
          type:
            - 'null'
            - string
        url:
          type:
            - 'null'
            - string
        events:
          type:
            - 'null'
            - array
          items:
            type: string
        description:
          type:
            - 'null'
            - string
        headers:
          type:
            - 'null'
            - object
          additionalProperties:
            type: string
    DeleteTasksRequest:
      required:
        - taskIds
      type: object
      properties:
        taskIds:
          type: array
          items:
            type: string
            format: uuid
    DocumentRequestEmailRequest:
      required:
        - recipientContactId
        - recipientRole
        - documentTypes
        - subject
        - bodyHtml
      type: object
      properties:
        recipientContactId:
          type:
            - 'null'
            - string
          format: uuid
        recipientRole:
          type:
            - 'null'
            - string
        documentTypes:
          type:
            - 'null'
            - array
          items:
            type: string
        subject:
          type:
            - 'null'
            - string
        bodyHtml:
          type:
            - 'null'
            - string
        bodyPlainText:
          type:
            - 'null'
            - string
        tone:
          type:
            - 'null'
            - string
    EditProposalPayloadRequest:
      type: object
      properties:
        parameters:
          oneOf:
            - type: 'null'
            - $ref: '#/components/schemas/JsonElement'
    Error:
      title: Error
      required:
        - error
      type: object
      properties:
        error:
          required:
            - code
            - message
          type: object
          properties:
            code:
              enum:
                - unauthorized
                - insufficient_scope
                - not_found
                - validation_error
                - conflict
                - rate_limited
              type: string
              description: Stable machine-readable error code.
            message:
              type: string
              description: Human-readable explanation. Do not parse or match on this.
            fields:
              type:
                - 'null'
                - object
              additionalProperties:
                type: string
              description: 'Present on `validation_error` only: per-field messages keyed by the offending request property.'
      description: The single error envelope used by every `4xx`/`5xx` response on this API. Inspect `error.code` for branching — it is a stable machine-readable string; `error.message` is human-facing prose and may be reworded without notice.
    ExtractExistingRequest:
      type: object
      properties:
        state:
          type:
            - 'null'
            - string
        documentTypeHint:
          type:
            - 'null'
            - string
        transactionIdOverride:
          type:
            - 'null'
            - string
          format: uuid
        side:
          type:
            - 'null'
            - string
    InviteRecipientRequest:
      type: object
      properties:
        displayLabel:
          type:
            - 'null'
            - string
        email:
          type:
            - 'null'
            - string
        contactId:
          type:
            - 'null'
            - string
          format: uuid
        audienceTier:
          type:
            - 'null'
            - string
        expiresAt:
          type:
            - 'null'
            - string
          format: date-time
    JsonElement: { }
    KeyDateInput:
      required:
        - keyDateType
      type: object
      properties:
        keyDateType:
          type:
            - 'null'
            - string
        dateValue:
          type:
            - 'null'
            - string
          format: date-time
        isCustom:
          type:
            - 'null'
            - boolean
        label:
          type:
            - 'null'
            - string
    KickOffExtractionRequest:
      required:
        - uploadId
      type: object
      properties:
        uploadId:
          type:
            - 'null'
            - string
        filename:
          type:
            - 'null'
            - string
        state:
          type:
            - 'null'
            - string
        documentTypeHint:
          type:
            - 'null'
            - string
        transactionId:
          type:
            - 'null'
            - string
          format: uuid
        side:
          type:
            - 'null'
            - string
    LinkContactRequest:
      type: object
      properties:
        contactId:
          type:
            - 'null'
            - string
          format: uuid
        role:
          type:
            - 'null'
            - string
        isPrimary:
          type:
            - 'null'
            - boolean
    LogActivityRequest:
      required:
        - type
        - summary
      type: object
      properties:
        type:
          type: string
        summary:
          type: string
        details:
          type:
            - 'null'
            - string
    MessageDraftRequest:
      required:
        - intent
      type: object
      properties:
        intent:
          type: string
        recipientRole:
          type:
            - 'null'
            - string
        recipientName:
          type:
            - 'null'
            - string
        additionalContext:
          type:
            - 'null'
            - string
    OwnerBody:
      required:
        - email
      type: object
      properties:
        email:
          type: string
        firstName:
          type:
            - 'null'
            - string
        lastName:
          type:
            - 'null'
            - string
    RegisterDeviceRequest:
      type: object
      properties:
        installationId:
          type:
            - 'null'
            - string
        platform:
          type:
            - 'null'
            - string
        appVersion:
          type:
            - 'null'
            - string
        pushToken:
          type:
            - 'null'
            - string
        capabilities:
          type:
            - 'null'
            - array
          items:
            type: string
        permissions:
          type:
            - 'null'
            - object
          additionalProperties:
            type: string
    RejectProposalRequest:
      type: object
      properties:
        reason:
          type:
            - 'null'
            - string
    RenderTemplateRequest:
      type: object
      properties:
        templateId:
          type:
            - 'null'
            - string
          format: uuid
        slug:
          type:
            - 'null'
            - string
    ReorderDocumentsRequest:
      type: object
      properties:
        orderedDocumentIds:
          type:
            - 'null'
            - array
          items:
            type: string
            format: uuid
    ReplaceDocumentsRequest:
      type: object
      properties:
        documentIds:
          type:
            - 'null'
            - array
          items:
            type: string
            format: uuid
    ReplaceDocumentVersionRequest:
      type: object
      properties:
        uploadId:
          type:
            - 'null'
            - string
        filename:
          type:
            - 'null'
            - string
        sizeBytes:
          type:
            - 'null'
            - integer
          format: int64
        contentType:
          type:
            - 'null'
            - string
    ReplyMessageRequest:
      type: object
      properties:
        messageContent:
          type:
            - 'null'
            - string
        forceOverride:
          type: boolean
          default: false
    RequestAccountDeletionBody:
      type: object
      properties:
        confirmName:
          type:
            - 'null'
            - string
    RescheduleTaskRequest:
      type: object
      properties:
        dueDate:
          type:
            - 'null'
            - string
          format: date-time
    ScheduleMessageRequest:
      type: object
      properties:
        toEmail:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        body:
          type:
            - 'null'
            - string
        htmlBody:
          type:
            - 'null'
            - string
        ccAddresses:
          type:
            - 'null'
            - string
        bccAddresses:
          type:
            - 'null'
            - string
        fromAccountId:
          type:
            - 'null'
            - string
        scheduledAt:
          type:
            - 'null'
            - string
        scheduledTimezone:
          type:
            - 'null'
            - string
        documentIds:
          type:
            - 'null'
            - array
          items:
            type: string
            format: uuid
        documentFileNames:
          type:
            - 'null'
            - object
          additionalProperties:
            type: string
        attachments:
          type:
            - 'null'
            - array
          items:
            $ref: '#/components/schemas/SendMessageAttachment'
    SendEmailMessageRequest:
      type: object
      properties:
        toEmail:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        body:
          type:
            - 'null'
            - string
        htmlBody:
          type:
            - 'null'
            - string
        ccAddresses:
          type:
            - 'null'
            - string
        bccAddresses:
          type:
            - 'null'
            - string
        fromAccountId:
          type:
            - 'null'
            - string
        templateId:
          type:
            - 'null'
            - string
        replyToMessageId:
          type:
            - 'null'
            - string
    SendMessageAttachment:
      required:
        - fileName
        - url
      type: object
      properties:
        fileName:
          type: string
        url:
          type: string
        contentType:
          type:
            - 'null'
            - string
    ShareToPartyRequest:
      type: object
      properties:
        toPhoneNumber:
          type:
            - 'null'
            - string
        contactId:
          type:
            - 'null'
            - string
          format: uuid
        message:
          type:
            - 'null'
            - string
        expirationHours:
          type:
            - 'null'
            - integer
          format: int32
    ShareToThreadRequest:
      type: object
      properties:
        documentId:
          type:
            - 'null'
            - string
          format: uuid
        message:
          type:
            - 'null'
            - string
        expirationHours:
          type:
            - 'null'
            - integer
          format: int32
    SplitDocumentRequest:
      type: object
      properties:
        segments:
          type:
            - 'null'
            - array
          items:
            $ref: '#/components/schemas/SplitSegment'
    SplitSegment:
      type: object
      properties:
        startPage:
          pattern: '^-?(?:0|[1-9]\d*)$'
          type:
            - integer
            - string
          format: int32
        endPage:
          pattern: '^-?(?:0|[1-9]\d*)$'
          type:
            - integer
            - string
          format: int32
        documentType:
          type:
            - 'null'
            - string
    StartConversationRequest:
      type: object
      properties:
        toPhoneNumber:
          type:
            - 'null'
            - string
        messageContent:
          type:
            - 'null'
            - string
        contactId:
          type:
            - 'null'
            - string
        forceOverride:
          type: boolean
          default: false
    StatusSummaryRequest:
      type: object
      properties:
        audience:
          type:
            - 'null'
            - string
        subject:
          type:
            - 'null'
            - string
        bodyMarkdown:
          type:
            - 'null'
            - string
        keyPointBullets:
          type:
            - 'null'
            - array
          items:
            type: string
    UpdateDocumentRequest:
      type: object
      properties:
        name:
          type:
            - 'null'
            - string
        category:
          type:
            - 'null'
            - string
        documentType:
          type:
            - 'null'
            - string
    UpdateKeyDateRequest:
      type: object
      properties:
        newValue:
          type:
            - 'null'
            - string
        source:
          type:
            - 'null'
            - string
        calculationNotes:
          type:
            - 'null'
            - string
    UpdateNotesRequest:
      type: object
      properties:
        notes:
          type:
            - 'null'
            - string
    UpdateWebhookApiRequest:
      required:
        - name
        - url
        - events
      type: object
      properties:
        name:
          type:
            - 'null'
            - string
        url:
          type:
            - 'null'
            - string
        events:
          type:
            - 'null'
            - array
          items:
            type: string
        description:
          type:
            - 'null'
            - string
        headers:
          type:
            - 'null'
            - object
          additionalProperties:
            type: string
        isActive:
          type:
            - 'null'
            - boolean
        circuitBreakerEnabled:
          type:
            - 'null'
            - boolean
        timeoutSeconds:
          type:
            - 'null'
            - integer
          format: int32
    UploadDocumentRequest:
      required:
        - fileBase64
      type: object
      properties:
        fileBase64:
          type:
            - 'null'
            - string
        filename:
          type:
            - 'null'
            - string
        state:
          type:
            - 'null'
            - string
        documentTypeHint:
          type:
            - 'null'
            - string
        transactionId:
          type:
            - 'null'
            - string
          format: uuid
        side:
          type:
            - 'null'
            - string
    UploadUrlRequest:
      required:
        - filename
      type: object
      properties:
        filename:
          type:
            - 'null'
            - string
        contentType:
          type:
            - 'null'
            - string
    WebhookEvent:
      title: WebhookEvent
      required:
        - eventType
        - eventId
        - timestamp
        - organizationId
        - data
      type: object
      properties:
        eventType:
          enum:
            - transaction.created
            - transaction.status_changed
            - transaction.closed
            - document.uploaded
            - document.deleted
            - task.created
            - task.completed
            - extraction.completed
            - key_date.updated
            - key_date.completed
            - contact.created
            - disclosure.recipient_viewed
            - form.submission_received
            - webhook.test
          type: string
          description: Which event this is — one of the types listed in this section.
        eventId:
          type: string
          description: Stable per-delivery id. Retries reuse it — deduplicate on this value.
          format: uuid
        timestamp:
          type: string
          description: UTC send time.
          format: date-time
        organizationId:
          type: string
          description: The organization the event belongs to. Always the organization that owns the subscription — a webhook never carries another tenant's data.
        userId:
          type:
            - 'null'
            - string
          description: 'The acting user, when the event was caused by a person rather than a job.'
          format: uuid
        userName:
          type:
            - 'null'
            - string
          description: 'Display name of the acting user, when there is one.'
        data:
          type: object
          description: Event-specific payload. Additive — ignore unrecognised fields.
      description: The envelope wrapping every webhook delivery. Identical across all event types.
  responses:
    Unauthorized:
      description: 'The `Authorization` header is missing, malformed, or the key has been revoked. Send `Authorization: Bearer <key>`. (`code: "unauthorized"`)'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: 'The key is valid but lacks the scope this operation requires, or it is the wrong credential tier — an organization key (`mcp_at_…`) on a partner route, or a partner key (`rsk_…`) anywhere else. (`code: "insufficient_scope"`)'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: 'No such resource **for this credential**. A record that exists but belongs to another organization also returns `404`, never `403` — existence is never disclosed across tenants. Do not treat `404` as evidence that an ID is unused. (`code: "not_found"`)'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: 'The request was understood but a field failed validation; see `error.fields`. (`code: "validation_error"`)'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: 'The request conflicts with current state — for example reusing an `Idempotency-Key` with a different request body. (`code: "conflict"`)'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: 'Rate limit exceeded for this key. Buckets are per key, per minute, fixed window: **300 reads** (GET/HEAD) and **60 writes** (POST/PUT/PATCH/DELETE). Wait for the window named by `Retry-After`, then retry. (`code: "rate_limited"`)'
      headers:
        Retry-After:
          description: Seconds to wait before retrying. Always `60` — the fixed window length.
          schema:
            type: integer
            format: int32
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    OrgApiKey:
      type: http
      description: "Organization API key — `Authorization: Bearer mcp_at_…`\n\nMint one in DocJacket under **Settings → Advanced → API Keys**. The key acts within a single organization and carries one or more scopes:\n\n- `read` — GETs.\n- `draft` — low-risk writes (completing a task, saving a status summary).\n- `actions` — side-effecting writes (sending email, deleting, provisioning).\n\nEach operation below notes the scope it requires. `GET /api/v1/catalog` returns the same list with a `callable` flag computed against *your* key. The identical key also connects AI assistants over MCP at `https://mcp.docjacket.com/mcp`."
      scheme: bearer
    OAuth2:
      type: oauth2
      description: "OAuth 2.1 access token — `Authorization: Bearer <access token>`\n\nReaches the same operations as an organization API key, with the same `read` / `draft` / `actions` scopes. Use it when your application acts for many users: register once, then each user authorizes their own DocJacket account and you hold a token per user. The organization is carried in the token's `org_id` claim — key your storage on it.\n\nAuthorization code + PKCE (`S256` only). Public client: there is no client secret. Register dynamically (RFC 7591) at `/oauth/register`, and read the endpoints from `/.well-known/oauth-authorization-server` rather than hardcoding them.\n\nAccess tokens last 1 hour; refresh tokens last 90 days and **rotate on every use**. Presenting an already-used refresh token is treated as a replay and revokes every live token for that user and client — store the new one before using it, and never retry a failed refresh with the old one. Membership is re-read on every call, so a user removed from the organization loses access immediately rather than at token expiry: treat `401` as \"reconnect this account\", not as a transient error."
      flows:
        authorizationCode:
          authorizationUrl: https://app.docjacket.com/oauth/authorize
          tokenUrl: https://app.docjacket.com/oauth/token
          scopes:
            read: 'Read transactions, key dates, tasks, documents, contacts, communications.'
            draft: 'Low-risk writes: complete a task, update a key date, save a status summary.'
            actions: 'Side-effecting writes: send email or SMS, delete, share a document externally.'
    PartnerApiKey:
      type: http
      description: "White-label partner key — `Authorization: Bearer rsk_…`\n\nCross-tenant credential for resellers, accepted only on the `/api/v1/orgs/*` operations (the **Organizations (Partner)** section). Organization API keys (`mcp_at_…`) are rejected there, and a partner key is rejected everywhere else. Requests for an organization the key does not own return `404` rather than `403`, so ownership is never disclosed."
      scheme: bearer
security:
  - OrgApiKey: [ ]
  - OAuth2: [ ]
tags:
  - name: Meta
    description: 'Start here. Verify your key, discover the operations it can call, and see your own call volume.'
  - name: Transactions
    description: 'The transaction record itself — create, list, read, and update deals, and manage the checklists, contingencies, notes, and reminders attached to them.'
  - name: Messaging
    description: 'Email and messaging on a deal — send client updates and document requests, draft and schedule messages, and read the communication log.'
  - name: Key Dates
    description: Deadlines across your book of business. Key dates for a single transaction live under Transactions.
  - name: Tasks
    description: 'Task lists on a deal — create, list, comment on, complete, and delete tasks.'
  - name: Documents
    description: 'Files on a deal — request an upload URL, list and classify documents, share them, and manage versions.'
  - name: Extractions
    description: 'AI contract extraction — kick off a run, poll results, and apply extracted fields to a transaction.'
  - name: Contacts
    description: 'People and companies — search, create, and update contacts, and read the role vocabulary they are assigned to.'
  - name: Disclosure Packages
    description: 'Seller disclosure packages — assemble documents, activate, invite and revoke recipients, and read access activity.'
  - name: Proposals
    description: 'AI-generated proposals awaiting human review — list, inspect, approve, and reject.'
  - name: Templates & Forms
    description: 'Reusable content — email templates, checklist templates, intake forms, and the submissions they produce.'
  - name: Webhooks
    description: 'Push instead of poll — subscribe to events, manage subscriptions, and read the event catalog.'
  - name: Organization
    description: 'Your own organization — settings, the transaction-status vocabulary, notifications, and registered devices.'
  - name: Organizations (Partner)
    description: 'White-label partner tier. Provision and read the organizations in your book of business. Requires a partner key (rsk_…), not an organization key.'
webhooks:
  transaction.created:
    post:
      tags:
        - Webhooks
      summary: When a new transaction is created
      description: "Sent when: the `transaction.created` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_transaction_created
      requestBody:
        description: 'The `transaction.created` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: transaction.created
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                propertyAddress: 144 Peachtree Walk NE
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  transaction.status_changed:
    post:
      tags:
        - Webhooks
      summary: When a transaction status changes
      description: "Sent when: the `transaction.status_changed` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_transaction_status_changed
      requestBody:
        description: 'The `transaction.status_changed` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: transaction.status_changed
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                fromStatus: Under Contract
                toStatus: Closed
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  transaction.closed:
    post:
      tags:
        - Webhooks
      summary: When a transaction is closed
      description: "Sent when: the `transaction.closed` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_transaction_closed
      requestBody:
        description: 'The `transaction.closed` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: transaction.closed
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                propertyAddress: 144 Peachtree Walk NE
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  document.uploaded:
    post:
      tags:
        - Webhooks
      summary: When a document is uploaded to a transaction
      description: "Sent when: the `document.uploaded` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_document_uploaded
      requestBody:
        description: 'The `document.uploaded` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: document.uploaded
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                documentId: d0f6b1a2-4c3e-4f1b-9a7d-2e5c8b0a1f34
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                filename: purchase-agreement.pdf
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  document.deleted:
    post:
      tags:
        - Webhooks
      summary: When a document is deleted
      description: "Sent when: the `document.deleted` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_document_deleted
      requestBody:
        description: 'The `document.deleted` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: document.deleted
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                documentId: d0f6b1a2-4c3e-4f1b-9a7d-2e5c8b0a1f34
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                filename: purchase-agreement.pdf
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  task.created:
    post:
      tags:
        - Webhooks
      summary: When a new task is created
      description: "Sent when: the `task.created` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_task_created
      requestBody:
        description: 'The `task.created` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: task.created
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                taskId: b7e05d92-1a48-4c36-95f7-6d3e8a0b2c54
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                title: Order home inspection
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  task.completed:
    post:
      tags:
        - Webhooks
      summary: When a task is marked complete
      description: "Sent when: the `task.completed` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_task_completed
      requestBody:
        description: 'The `task.completed` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: task.completed
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                taskId: b7e05d92-1a48-4c36-95f7-6d3e8a0b2c54
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                title: Order home inspection
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  extraction.completed:
    post:
      tags:
        - Webhooks
      summary: When a document extraction job finishes (includes jobId + status)
      description: "Sent when: the `extraction.completed` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_extraction_completed
      requestBody:
        description: 'The `extraction.completed` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: extraction.completed
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                jobId: 6b1f9c4d-8e2a-4b7c-9f10-3d5a7e2c8b41
                status: completed
                documentId: d0f6b1a2-4c3e-4f1b-9a7d-2e5c8b0a1f34
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  key_date.updated:
    post:
      tags:
        - Webhooks
      summary: When a key date is added or changed on a transaction
      description: "Sent when: the `key_date.updated` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_key_date_updated
      requestBody:
        description: 'The `key_date.updated` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: key_date.updated
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                keyDateType: ClosingDate
                dateValue: '2026-09-15'
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  key_date.completed:
    post:
      tags:
        - Webhooks
      summary: When a key date is marked complete
      description: "Sent when: the `key_date.completed` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_key_date_completed
      requestBody:
        description: 'The `key_date.completed` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: key_date.completed
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                transactionId: 8a4c2e10-9f5b-4713-b6d8-0e2a7c5f3149
                keyDateType: ClosingDate
                dateValue: '2026-09-15'
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  contact.created:
    post:
      tags:
        - Webhooks
      summary: When a new contact is created
      description: "Sent when: the `contact.created` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_contact_created
      requestBody:
        description: 'The `contact.created` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: contact.created
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                contactId: 4e8b1f07-3c92-4a56-8d14-7f2b9c0e5a38
                email: buyer@example.com
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  disclosure.recipient_viewed:
    post:
      tags:
        - Webhooks
      summary: When a disclosure package recipient opens the disclosure
      description: "Sent when: the `disclosure.recipient_viewed` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_disclosure_recipient_viewed
      requestBody:
        description: 'The `disclosure.recipient_viewed` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: disclosure.recipient_viewed
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                packageId: 9d3f7a21-6e08-4b95-a742-1c5e8b0d3f67
                recipientId: 2b6c4e08-5a71-4f93-8d20-3e7a1c9b5f04
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  form.submission_received:
    post:
      tags:
        - Webhooks
      summary: When a public form submission is received
      description: "Sent when: the `form.submission_received` event occurs in your organization.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_form_submission_received
      requestBody:
        description: 'The `form.submission_received` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: form.submission_received
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                submissionId: 7c1e5a93-4b28-4d06-9f35-8a0d2e6b1c47
                formId: 5a9d3c71-2e46-4b80-91f5-6c8b0a4e7d23
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]
  webhook.test:
    post:
      tags:
        - Webhooks
      summary: Test webhook (triggered manually)
      description: "Sent when: you trigger a test delivery manually. Useful for verifying your signature check.\n\n**Delivery.** `POST` to your subscription URL with these headers:\n\n- `X-Webhook-Signature` — `sha256=<hex>`, an HMAC-SHA256 of the **raw request body** using your subscription's signing secret. Compute it over the bytes as received, before any JSON parsing or re-serialization, and compare in constant time.\n- `X-Webhook-Event-Type` — the event type, matching this entry.\n- `X-Webhook-Event-Id` — a per-delivery UUID. **Deduplicate on this**: retries reuse it.\n- `X-Webhook-Timestamp` — ISO-8601 UTC send time.\n\nSubscribe with `POST /api/v1/webhooks` (use `\"*\"` to receive every event). Rotate the secret with `POST /api/v1/webhooks/{id}/regenerate-secret`, and fire a sample with `POST /api/v1/webhooks/{id}/test`.\n\n`data` is the event-specific body and is **additive** — new fields may appear at any time, so ignore ones you do not recognise."
      operationId: webhook_webhook_test
      requestBody:
        description: 'The `webhook.test` event envelope, POSTed to your subscription URL.'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            example:
              eventType: webhook.test
              eventId: 3f2a91c7-5d64-4e18-9b03-8c7d1e4a6f52
              timestamp: '2026-08-04T15:42:07.1183940Z'
              organizationId: org-9fc8a1d2e3b4
              userId: 1c9e6b40-72af-4d35-8e21-5b0a3f7c9d16
              userName: Jordan Rivera
              data:
                message: This is a test delivery from DocJacket.
        required: true
      responses:
        2XX:
          description: 'Any 2xx acknowledges receipt. Respond within your subscription''s timeout; anything else is recorded as a failed delivery and retried, and repeated failures open the circuit breaker on the subscription.'
      security: [ ]