openapi: 3.1.0
info:
  title: ProspectConnect Public API
  version: 1.0.0
  description: |
    Whitelabel-neutral public REST API for the platform. This is an **additive** facade over the
    internal services — the product frontend keeps using the internal API unchanged.

    **Conventions (apply to every endpoint):**
    - **Auth:** OAuth 2.0 Bearer (`Authorization: Bearer <token>`). Per-tenant Personal Access Tokens (PAT)
      in v1; full auth-code + PKCE for marketplace apps. The token carries the tenant (`business_id`) and the
      user's role/permissions — the gateway enforces **scope ∩ role**.
    - **Versioning:** path `/v1` (major) + a required `Version: <YYYY-MM-DD>` header (dated behavior contract).
    - **IDs:** opaque, type-prefixed strings (`con_…`, `cmp_…`, `deal_…`, `task_…`, `note_…`, `act_…`).
    - **Pagination:** cursor-based; one uniform envelope `{ data, meta: { cursor: { next, has_more }, total? } }`.
      Search is `POST /{resource}/search` with `{ filters, search_text, order_by, sort_direction, limit, cursor }`.
    - **Errors:** RFC 9457 `application/problem+json` with `trace_id`.
    - **Dates:** ISO-8601 / RFC 3339 UTC (`2026-06-17T14:30:00Z`); date-only fields `YYYY-MM-DD`.
    - **Casing:** `snake_case` JSON (matches internal storage; reduces mapping).
    - **Methods:** POST create, GET read, PATCH partial update, DELETE; `Idempotency-Key` honored on creates.
    - **Rate limits:** per-token; `X-RateLimit-*` response headers.

    **Wave 1** ships Contacts, Companies, Deals (+Pipelines), Tasks, Notes, and Activities — all
    detailed in this spec on the identical pattern. Later waves (engagement & scheduling, commerce,
    content/automation, reporting) extend the same conventions; once you've integrated Wave 1, the rest
    of the surface looks identical.
servers:
  - url: https://papi.trendev.in/v1
    description: Development / sandbox.
security:
  - oauth2: []
  - bearerPat: []
tags:
  - name: Contacts
    description: People in the CRM — the anchor object most other records attach to.
  - name: Companies
    description: Organizations / accounts that contacts and deals belong to.
  - name: Deals
    description: Revenue opportunities moving through pipeline stages.
  - name: Pipelines
    description: Read-only pipeline + stage metadata for deals.
  - name: Tasks
    description: Actionable to-dos, optionally attached to a record.
  - name: Notes
    description: Free-text notes attached to a contact, company, or deal.
  - name: Activities
    description: The record timeline (emails, calls, meetings, system events).
paths:
  /contacts:
    post:
      tags:
        - Contacts
      operationId: createContact
      summary: Create a contact
      description: |
        Creates a contact. At least one identity field is required (`email` or `phone`).
        For idempotent create-or-update by a natural key, use `POST /contacts/upsert`.
        Maps to internal `POST /contact/upsert` with `force_create=true`.
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactCreate'
      responses:
        '201':
          description: Contact created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
  /contacts/upsert:
    post:
      tags:
        - Contacts
      operationId: upsertContact
      summary: Create or update a contact by natural key
      description: |
        Idempotent create-or-update. Matches an existing contact by `email`/`phone` (or `id` if supplied),
        otherwise creates one. Maps to internal `POST /contact/upsert`. Response `status` reports `created` or `updated`.
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/ContactCreate'
                - type: object
                  properties:
                    id:
                      type: string
                      description: Update this specific contact if provided.
                      examples:
                        - con_8f2a1c9b4d6e7f0a1b2c3d4e
      responses:
        '200':
          description: Contact upserted
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - contact
                  - status
                properties:
                  contact:
                    $ref: '#/components/schemas/Contact'
                  status:
                    type: string
                    enum:
                      - created
                      - updated
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
  /contacts/search:
    post:
      tags:
        - Contacts
      operationId: searchContacts
      summary: Search / list contacts
      description: |
        Full-text + structured filtering with cursor pagination. With an empty body, returns the most recent
        contacts (default `limit` 25, `order_by` `created_at`, `sort_direction` `desc`).
        Maps to internal `POST /contact/search/contact` (the gateway translates `cursor` ↔ `pagination_token`).
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactSearchRequest'
      responses:
        '200':
          description: A page of contacts
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Contact'
                  meta:
                    $ref: '#/components/schemas/PageMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /contacts/{contact_id}:
    parameters:
      - $ref: '#/components/parameters/ContactId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Contacts
      operationId: getContact
      summary: Get a contact
      description: Retrieves a contact by id. Maps to internal `POST /contact/get`.
      parameters:
        - name: fields
          in: query
          required: false
          description: Optional projection — comma-separated field names to return.
          schema:
            type: string
      responses:
        '200':
          description: The contact
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Contacts
      operationId: updateContact
      summary: Update a contact (partial)
      description: Partial update; only supplied fields change. Maps to internal `POST /contact/upsert` with the id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactUpdate'
      responses:
        '200':
          description: Updated contact
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags:
        - Contacts
      operationId: deleteContact
      summary: Delete a contact
      description: Soft-deletes the contact (cascades per internal logic). Maps to internal `POST /contact/delete-multiple-contacts` with a single id.
      responses:
        '204':
          description: Deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /contacts/{contact_id}/tags:
    parameters:
      - $ref: '#/components/parameters/ContactId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Contacts
      operationId: listContactTags
      summary: List a contact's tags
      responses:
        '200':
          description: Tags
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: string
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Contacts
      operationId: addContactTags
      summary: Add tags to a contact
      description: Maps to internal upsert with `tag_operation=add`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tags
              properties:
                tags:
                  type: array
                  items:
                    type: string
                  minItems: 1
      responses:
        '200':
          description: Tags added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Contacts
      operationId: removeContactTags
      summary: Remove tags from a contact
      description: Maps to internal upsert with `tag_operation=remove`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tags
              properties:
                tags:
                  type: array
                  items:
                    type: string
                  minItems: 1
      responses:
        '200':
          description: Tags removed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '404':
          $ref: '#/components/responses/NotFound'
  /contacts/{contact_id}/tasks:
    parameters:
      - $ref: '#/components/parameters/ContactId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Contacts
      operationId: listContactTasks
      summary: List tasks on a contact
      description: Maps to mcr-activity-service (tasks filtered by related contact). Returns the standard page envelope.
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of tasks (see Tasks schema in the Tasks spec)
          content:
            application/json:
              schema:
                type: object
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Contacts
      operationId: createContactTask
      summary: Create a task on a contact
      description: Create lives on the parent so a task always has an owner entity. Body = Task input (see Tasks spec).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '201':
          description: Task created
        '404':
          $ref: '#/components/responses/NotFound'
  /contacts/{contact_id}/notes:
    parameters:
      - $ref: '#/components/parameters/ContactId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Contacts
      operationId: listContactNotes
      summary: List notes on a contact
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of notes (see Notes spec)
          content:
            application/json:
              schema:
                type: object
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Contacts
      operationId: createContactNote
      summary: Add a note to a contact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - body
              properties:
                body:
                  type: string
      responses:
        '201':
          description: Note created
        '404':
          $ref: '#/components/responses/NotFound'
  /contacts/{contact_id}/activities:
    parameters:
      - $ref: '#/components/parameters/ContactId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Contacts
      operationId: listContactActivities
      summary: List a contact's activity timeline
      description: The merged timeline (emails, calls, meetings, system events). Maps to mcr-activity-service.
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of activities (see Activities spec)
          content:
            application/json:
              schema:
                type: object
        '404':
          $ref: '#/components/responses/NotFound'
  /contacts/{contact_id}/deals:
    parameters:
      - $ref: '#/components/parameters/ContactId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Contacts
      operationId: listContactDeals
      summary: List deals associated with a contact
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of deals (see Deals spec)
          content:
            application/json:
              schema:
                type: object
        '404':
          $ref: '#/components/responses/NotFound'
  /companies:
    post:
      tags:
        - Companies
      operationId: createCompany
      summary: Create a company
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyCreate'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Company'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
  /companies/upsert:
    post:
      tags:
        - Companies
      operationId: upsertCompany
      summary: Create or update a company
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/CompanyCreate'
                - type: object
                  properties:
                    id:
                      type: string
      responses:
        '200':
          description: Upserted
          content:
            application/json:
              schema:
                type: object
                required:
                  - company
                  - status
                properties:
                  company:
                    $ref: '#/components/schemas/Company'
                  status:
                    type: string
                    enum:
                      - created
                      - updated
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
  /companies/search:
    post:
      tags:
        - Companies
      operationId: searchCompanies
      summary: Search / list companies
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanySearchRequest'
      responses:
        '200':
          description: A page of companies
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Company'
                  meta:
                    $ref: '#/components/schemas/PageMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /companies/{company_id}:
    parameters:
      - $ref: '#/components/parameters/CompanyId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Companies
      operationId: getCompany
      summary: Get a company
      parameters:
        - name: fields
          in: query
          required: false
          schema:
            type: string
      responses:
        '200':
          description: The company
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Company'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Companies
      operationId: updateCompany
      summary: Update a company (partial)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyUpdate'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Company'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags:
        - Companies
      operationId: deleteCompany
      summary: Delete a company
      responses:
        '204':
          description: Deleted
        '404':
          $ref: '#/components/responses/NotFound'
  /deals/pipelines:
    get:
      tags:
        - Pipelines
      operationId: listPipelines
      summary: List pipelines (with stages)
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      responses:
        '200':
          description: Pipelines
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Pipeline'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /deals/pipelines/{pipeline_id}:
    parameters:
      - $ref: '#/components/parameters/PipelineId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Pipelines
      operationId: getPipeline
      summary: Get a pipeline
      responses:
        '200':
          description: The pipeline
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pipeline'
        '404':
          $ref: '#/components/responses/NotFound'
  /deals:
    post:
      tags:
        - Deals
      operationId: createDeal
      summary: Create a deal
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DealCreate'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
  /deals/upsert:
    post:
      tags:
        - Deals
      operationId: upsertDeal
      summary: Create or update a deal
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/DealCreate'
                - type: object
                  properties:
                    id:
                      type: string
      responses:
        '200':
          description: Upserted
          content:
            application/json:
              schema:
                type: object
                required:
                  - deal
                  - status
                properties:
                  deal:
                    $ref: '#/components/schemas/Deal'
                  status:
                    type: string
                    enum:
                      - created
                      - updated
        '422':
          $ref: '#/components/responses/ValidationError'
  /deals/search:
    post:
      tags:
        - Deals
      operationId: searchDeals
      summary: Search / list deals
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DealSearchRequest'
      responses:
        '200':
          description: A page of deals
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Deal'
                  meta:
                    $ref: '#/components/schemas/PageMeta'
        '403':
          $ref: '#/components/responses/Forbidden'
  /deals/{deal_id}/stage:
    parameters:
      - $ref: '#/components/parameters/DealId'
      - $ref: '#/components/parameters/VersionHeader'
    post:
      tags:
        - Deals
      operationId: changeDealStage
      summary: Move a deal to a different stage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - stage_id
              properties:
                stage_id:
                  type: string
      responses:
        '200':
          description: Updated deal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '404':
          $ref: '#/components/responses/NotFound'
  /deals/{deal_id}:
    parameters:
      - $ref: '#/components/parameters/DealId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Deals
      operationId: getDeal
      summary: Get a deal
      parameters:
        - name: fields
          in: query
          required: false
          schema:
            type: string
      responses:
        '200':
          description: The deal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Deals
      operationId: updateDeal
      summary: Update a deal (partial)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DealUpdate'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Deals
      operationId: deleteDeal
      summary: Delete a deal
      responses:
        '204':
          description: Deleted
        '404':
          $ref: '#/components/responses/NotFound'
  /tasks:
    post:
      tags:
        - Tasks
      operationId: createTask
      summary: Create a task
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCreate'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '422':
          $ref: '#/components/responses/ValidationError'
  /tasks/search:
    post:
      tags:
        - Tasks
      operationId: searchTasks
      summary: Search / list tasks
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskSearchRequest'
      responses:
        '200':
          description: A page of tasks
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Task'
                  meta:
                    $ref: '#/components/schemas/PageMeta'
  /tasks/{task_id}/complete:
    parameters:
      - $ref: '#/components/parameters/TaskId'
      - $ref: '#/components/parameters/VersionHeader'
    post:
      tags:
        - Tasks
      operationId: completeTask
      summary: Mark a task complete
      responses:
        '200':
          description: Completed task
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
  /tasks/{task_id}:
    parameters:
      - $ref: '#/components/parameters/TaskId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Tasks
      operationId: getTask
      summary: Get a task
      responses:
        '200':
          description: The task
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Tasks
      operationId: updateTask
      summary: Update a task (partial)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskUpdate'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Tasks
      operationId: deleteTask
      summary: Delete a task
      responses:
        '204':
          description: Deleted
        '404':
          $ref: '#/components/responses/NotFound'
  /notes:
    post:
      tags:
        - Notes
      operationId: createNote
      summary: Create a note on a record
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NoteCreate'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Note'
        '422':
          $ref: '#/components/responses/ValidationError'
  /notes/search:
    post:
      tags:
        - Notes
      operationId: searchNotes
      summary: List notes for a record
      description: Parent-scoped — `related_to` is required (the internal notes service lists by parent).
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NoteSearchRequest'
      responses:
        '200':
          description: Notes for the record
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Note'
                  meta:
                    $ref: '#/components/schemas/PageMeta'
        '422':
          $ref: '#/components/responses/ValidationError'
  /notes/{note_id}:
    parameters:
      - $ref: '#/components/parameters/NoteId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Notes
      operationId: getNote
      summary: Get a note
      responses:
        '200':
          description: The note
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Note'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Notes
      operationId: updateNote
      summary: Update a note's body
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - body
              properties:
                body:
                  type: string
                  minLength: 1
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Note'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Notes
      operationId: deleteNote
      summary: Delete a note (not yet supported)
      responses:
        '501':
          $ref: '#/components/responses/NotImplemented'
  /notes/{note_id}/pin:
    parameters:
      - $ref: '#/components/parameters/NoteId'
      - $ref: '#/components/parameters/VersionHeader'
    post:
      tags:
        - Notes
      operationId: pinNote
      summary: Pin or unpin a note
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - is_pinned
              properties:
                is_pinned:
                  type: boolean
      responses:
        '200':
          description: Updated note
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Note'
        '404':
          $ref: '#/components/responses/NotFound'
  /activities:
    post:
      tags:
        - Activities
      operationId: logActivity
      summary: Log a timeline activity
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActivityLog'
      responses:
        '202':
          description: Logged
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - logged
        '422':
          $ref: '#/components/responses/ValidationError'
  /activities/search:
    post:
      tags:
        - Activities
      operationId: searchActivities
      summary: Search the activity timeline
      parameters:
        - $ref: '#/components/parameters/VersionHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActivitySearchRequest'
      responses:
        '200':
          description: A page of activities
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Activity'
                  meta:
                    $ref: '#/components/schemas/PageMeta'
  /activities/{activity_id}:
    parameters:
      - $ref: '#/components/parameters/ActivityId'
      - $ref: '#/components/parameters/VersionHeader'
    get:
      tags:
        - Activities
      operationId: getActivity
      summary: Get a single activity (not yet supported)
      responses:
        '501':
          $ref: '#/components/responses/NotImplemented'
    delete:
      tags:
        - Activities
      operationId: deleteActivity
      summary: Delete a single activity (not yet supported)
      responses:
        '501':
          $ref: '#/components/responses/NotImplemented'
components:
  securitySchemes:
    oauth2:
      type: oauth2
      description: Authorization-code + PKCE for marketplace apps.
      flows:
        authorizationCode:
          authorizationUrl: https://auth.prospectconnect.ai/authorize
          tokenUrl: https://auth.prospectconnect.ai/token
          scopes:
            contact.read: Read contacts and their sub-resources
            contact.write: Create / update contacts and tags
            contact.delete: Delete contacts
            contact.all: Full contact access
            company.read: Read companies
            company.write: Create / update companies
            company.delete: Delete companies
            company.all: Full company access
            deal.read: Read deals and pipelines
            deal.write: Create / update deals (incl. stage changes)
            deal.delete: Delete deals
            deal.all: Full deal access
            task.read: Read tasks
            task.write: Create / update / complete tasks
            task.delete: Delete tasks
            task.all: Full task access
            note.read: Read notes
            note.write: Create / update / pin notes
            note.delete: Delete notes
            note.all: Full note access
            activity.read: Read the activity timeline
            activity.write: Log activities
            activity.delete: Delete activities
            activity.all: Full activity access
    bearerPat:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Per-tenant Personal Access Token (client-credentials) for server-to-server use.
  parameters:
    VersionHeader:
      name: Version
      in: header
      required: true
      description: Dated behavior contract, e.g. `2026-06-01`.
      schema:
        type: string
        pattern: ^\d{4}-\d{2}-\d{2}$
      examples:
        default:
          value: '2026-06-01'
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: Client-supplied key to make a create safely retryable.
      schema:
        type: string
        maxLength: 255
    ContactId:
      name: contact_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^con_[A-Za-z0-9]+$
      examples:
        default:
          value: con_8f2a1c9b4d6e7f0a1b2c3d4e
    CompanyId:
      name: company_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^cmp_[A-Za-z0-9]+$
    DealId:
      name: deal_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^deal_[A-Za-z0-9]+$
    PipelineId:
      name: pipeline_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^pipe_[A-Za-z0-9]+$
    TaskId:
      name: task_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^task_[A-Za-z0-9]+$
    NoteId:
      name: note_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^note_[A-Za-z0-9]+$
    ActivityId:
      name: activity_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^act_[A-Za-z0-9]+$
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    Cursor:
      name: cursor
      in: query
      required: false
      schema:
        type: string
  responses:
    BadRequest:
      description: Malformed request
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Unauthorized:
      description: Missing or invalid token
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Forbidden:
      description: Token lacks the required scope, or the user's role/permissions deny this action
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: Resource not found (or not visible to this user's role)
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    ValidationError:
      description: Request failed schema validation
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - type: object
                properties:
                  errors:
                    type: array
                    items:
                      type: object
                      properties:
                        field:
                          type: string
                        message:
                          type: string
    RateLimited:
      description: Rate limit exceeded
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotImplemented:
      description: The operation has no internal endpoint yet
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  schemas:
    Problem:
      type: object
      description: RFC 9457 problem detail.
      properties:
        type:
          type: string
          format: uri
          examples:
            - https://papi.prospectconnect.ai/problems/validation
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        trace_id:
          type: string
      required:
        - type
        - title
        - status
    Cursor:
      type: object
      additionalProperties: false
      properties:
        next:
          type:
            - string
            - 'null'
          description: Opaque cursor for the next page; null when exhausted.
        has_more:
          type: boolean
      required:
        - next
        - has_more
    PageMeta:
      type: object
      additionalProperties: false
      properties:
        cursor:
          $ref: '#/components/schemas/Cursor'
        total:
          type: integer
          description: Total matches (may be omitted for large/expensive queries).
      required:
        - cursor
    Country:
      type: object
      properties:
        code:
          type: string
          description: ISO 3166-1 alpha-2, e.g. US.
        name:
          type: string
        currency:
          type: string
          description: ISO 4217, e.g. USD.
    Dnd:
      type: object
      description: Do-not-disturb flags per channel.
      properties:
        all:
          type: boolean
        email:
          type: boolean
        sms:
          type: boolean
        call:
          type: boolean
        whatsapp:
          type: boolean
    NamedRef:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
    Contact:
      type: object
      description: A person in the CRM. (Curated public view; internal integration IDs and flags are omitted.)
      example:
        id: con_8f2a1c9b4d6e7f0a1b2c3d4e
        contact_number: 1042
        first_name: Avery
        last_name: Diaz
        name: Avery Diaz
        email: avery.diaz@example.com
        phone: '+14155550142'
        job_title: VP of Operations
        company_name: Northwind Trading
        city: San Francisco
        state: CA
        tags:
          - vip
          - inbound
        owner_id: usr_3a7c91e2
        account_id: cmp_5d2e8a1b4c6f7091
        lifecycle_stage:
          id: stage_lead
          name: Lead
        lead_score: 82
        last_contacted_at: '2026-06-10T17:24:00Z'
        created_at: '2026-05-02T09:13:00Z'
        updated_at: '2026-06-10T17:24:05Z'
      properties:
        id:
          type: string
          examples:
            - con_8f2a1c9b4d6e7f0a1b2c3d4e
        contact_number:
          type: integer
          description: Human-friendly unique number (internal `ticket_number`).
        first_name:
          type:
            - string
            - 'null'
        last_name:
          type:
            - string
            - 'null'
        name:
          type:
            - string
            - 'null'
          description: Display name.
        email:
          type:
            - string
            - 'null'
          format: email
        phone:
          type:
            - string
            - 'null'
          description: E.164 normalized.
        website:
          type:
            - string
            - 'null'
        company_name:
          type:
            - string
            - 'null'
          description: Free-text employer label (distinct from a linked account).
        job_title:
          type:
            - string
            - 'null'
          description: Internal `contact_designation`.
        gender:
          type:
            - string
            - 'null'
        birth_date:
          type:
            - string
            - 'null'
          format: date
        profile_photo_url:
          type:
            - string
            - 'null'
          description: Internal `profile_photo`.
        address_line1:
          type:
            - string
            - 'null'
          description: Internal `address1`.
        city:
          type:
            - string
            - 'null'
        state:
          type:
            - string
            - 'null'
        postal_code:
          type:
            - string
            - 'null'
        country:
          $ref: '#/components/schemas/Country'
        time_zone:
          type:
            - string
            - 'null'
        tags:
          type: array
          items:
            type: string
        dnd:
          $ref: '#/components/schemas/Dnd'
        owner_id:
          type:
            - string
            - 'null'
          description: Assigned user (internal `assigned_to_id`).
        team_id:
          type:
            - string
            - 'null'
        account_id:
          type:
            - string
            - 'null'
          description: Linked company/account (internal `b_company_id`).
        source_id:
          type:
            - string
            - 'null'
        lifecycle_stage:
          $ref: '#/components/schemas/NamedRef'
        lead_status:
          $ref: '#/components/schemas/NamedRef'
        lead_score:
          type:
            - number
            - 'null'
          description: Internal `score`.
        score_label:
          type:
            - string
            - 'null'
          description: Internal `score_threshold_label` (e.g. good/average/poor).
        custom_fields:
          type: object
          additionalProperties: true
        number_of_activities:
          type:
            - integer
            - 'null'
        number_of_times_contacted:
          type:
            - integer
            - 'null'
        last_contacted_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
          description: Internal `date_created`.
        updated_at:
          type: string
          format: date-time
          description: Internal `date_modified`.
        created_by:
          type:
            - string
            - 'null'
        updated_by:
          type:
            - string
            - 'null'
          description: Internal `modified_by`.
      required:
        - id
        - created_at
        - updated_at
    ContactCreate:
      type: object
      additionalProperties: false
      description: At least one of `email` or `phone` is required.
      example:
        first_name: Avery
        last_name: Diaz
        email: avery.diaz@example.com
        phone: '+14155550142'
        job_title: VP of Operations
        company_name: Northwind Trading
        city: San Francisco
        state: CA
        country_code: US
        tags:
          - vip
          - inbound
        lead_score: 82
      properties:
        first_name:
          type: string
        last_name:
          type: string
        name:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
        website:
          type: string
        company_name:
          type: string
        job_title:
          type: string
        gender:
          type: string
        birth_date:
          type: string
          format: date
        address_line1:
          type: string
        city:
          type: string
        state:
          type: string
        postal_code:
          type: string
        country_code:
          type: string
          description: ISO 3166-1 alpha-2.
        time_zone:
          type: string
        tags:
          type: array
          items:
            type: string
        dnd:
          $ref: '#/components/schemas/Dnd'
        owner_id:
          type: string
        team_id:
          type: string
        account_id:
          type: string
        source_id:
          type: string
        lifecycle_stage_id:
          type: string
        lead_status_id:
          type: string
        lead_score:
          type: number
        custom_fields:
          type: object
          additionalProperties: true
      anyOf:
        - required:
            - email
        - required:
            - phone
    ContactUpdate:
      type: object
      additionalProperties: false
      description: Partial update — supply only the fields to change.
      properties:
        first_name:
          type:
            - string
            - 'null'
        last_name:
          type:
            - string
            - 'null'
        name:
          type:
            - string
            - 'null'
        email:
          type:
            - string
            - 'null'
          format: email
        phone:
          type:
            - string
            - 'null'
        website:
          type:
            - string
            - 'null'
        company_name:
          type:
            - string
            - 'null'
        job_title:
          type:
            - string
            - 'null'
        gender:
          type:
            - string
            - 'null'
        birth_date:
          type:
            - string
            - 'null'
          format: date
        address_line1:
          type:
            - string
            - 'null'
        city:
          type:
            - string
            - 'null'
        state:
          type:
            - string
            - 'null'
        postal_code:
          type:
            - string
            - 'null'
        country_code:
          type:
            - string
            - 'null'
        time_zone:
          type:
            - string
            - 'null'
        owner_id:
          type:
            - string
            - 'null'
        team_id:
          type:
            - string
            - 'null'
        account_id:
          type:
            - string
            - 'null'
        source_id:
          type:
            - string
            - 'null'
        lifecycle_stage_id:
          type:
            - string
            - 'null'
        lead_status_id:
          type:
            - string
            - 'null'
        lead_score:
          type:
            - number
            - 'null'
        custom_fields:
          type: object
          additionalProperties: true
    Filter:
      type: object
      description: One structured filter condition.
      additionalProperties: false
      properties:
        field:
          type: string
          examples:
            - lifecycle_stage_id
            - owner_id
            - city
        operator:
          type: string
          enum:
            - eq
            - neq
            - in
            - nin
            - contains
            - starts_with
            - gt
            - gte
            - lt
            - lte
            - between
            - is_set
            - is_not_set
        value: {}
        values:
          type: array
          items: {}
      required:
        - field
        - operator
    ContactSearchRequest:
      type: object
      additionalProperties: false
      example:
        search_text: acme
        filters:
          - field: lifecycle_stage_id
            operator: eq
            value: stage_lead
          - field: lead_score
            operator: gte
            value: 50
        order_by: lead_score
        sort_direction: desc
        limit: 25
      properties:
        search_text:
          type: string
          description: Full-text across name, email, phone, etc.
        filters:
          type: array
          items:
            $ref: '#/components/schemas/Filter'
        order_by:
          type: string
          default: created_at
          examples:
            - created_at
            - updated_at
            - name
            - lead_score
            - last_contacted_at
        sort_direction:
          type: string
          enum:
            - asc
            - desc
          default: desc
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        cursor:
          type: string
          description: Opaque cursor from a prior page's `meta.cursor.next`.
        account_id:
          type: string
          description: Convenience filter for contacts linked to an account.
        date_range:
          type: object
          properties:
            start:
              type: string
              format: date-time
            end:
              type: string
              format: date-time
        fields:
          type: array
          items:
            type: string
          description: Projection.
    RelatedTo:
      type: object
      additionalProperties: false
      required:
        - type
        - id
      description: Attaches a record to a parent contact / company / deal.
      properties:
        type:
          type: string
          enum:
            - contact
            - company
            - deal
        id:
          type: string
    Company:
      type: object
      example:
        id: cmp_5d2e8a1b4c6f7091
        company_number: 318
        name: Northwind Trading
        domain: northwind.example.com
        industry: Wholesale
        revenue: 4200000
        city: San Francisco
        state: CA
        tags:
          - enterprise
          - west-coast
        owner_id: usr_3a7c91e2
        lifecycle_stage:
          id: stage_customer
          name: Customer
        lead_score: 74
        created_at: '2026-03-18T11:02:00Z'
        updated_at: '2026-06-09T15:40:00Z'
      properties:
        id:
          type: string
        company_number:
          type: integer
        name:
          type: string
        domain:
          type: string
        industry:
          type: string
        revenue:
          type: number
        type:
          type: string
          description: Account type.
        description:
          type: string
        address_line1:
          type: string
        city:
          type: string
        state:
          type: string
        postal_code:
          type: string
        country:
          $ref: '#/components/schemas/Country'
        time_zone:
          type: string
        tags:
          type: array
          items:
            type: string
        dnd:
          type: boolean
        owner_id:
          type: string
        team_id:
          type: string
        primary_contact_id:
          type: string
        source_id:
          type: string
        lifecycle_stage:
          $ref: '#/components/schemas/NamedRef'
        lead_status:
          $ref: '#/components/schemas/NamedRef'
        lead_score:
          type: number
        linkedin_url:
          type: string
        twitter_url:
          type: string
        facebook_url:
          type: string
        instagram_url:
          type: string
        latitude:
          type: number
        longitude:
          type: number
        custom_fields:
          type: object
          additionalProperties: true
        contact_ids:
          type: array
          items:
            type: string
        deal_ids:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        created_by:
          type: string
        updated_by:
          type: string
      required:
        - id
        - created_at
        - updated_at
    CompanyCreate:
      type: object
      additionalProperties: false
      required:
        - name
      example:
        name: Northwind Trading
        domain: northwind.example.com
        industry: Wholesale
        revenue: 4200000
        city: San Francisco
        state: CA
        country_code: US
        owner_id: usr_3a7c91e2
        tags:
          - enterprise
          - west-coast
      properties:
        name:
          type: string
        domain:
          type: string
        industry:
          type: string
        revenue:
          type: number
        type:
          type: string
        description:
          type: string
        address_line1:
          type: string
        city:
          type: string
        state:
          type: string
        postal_code:
          type: string
        country_code:
          type: string
        time_zone:
          type: string
        tags:
          type: array
          items:
            type: string
        dnd:
          type: boolean
        owner_id:
          type: string
        team_id:
          type: string
        primary_contact_id:
          type: string
        source_id:
          type: string
        lifecycle_stage_id:
          type: string
        lead_status_id:
          type: string
        lead_score:
          type: number
        linkedin_url:
          type: string
        twitter_url:
          type: string
        facebook_url:
          type: string
        instagram_url:
          type: string
        latitude:
          type: number
        longitude:
          type: number
        custom_fields:
          type: object
          additionalProperties: true
    CompanyUpdate:
      allOf:
        - $ref: '#/components/schemas/CompanyCreate'
      description: Partial update — same fields as create, all optional.
    CompanySearchRequest:
      type: object
      additionalProperties: false
      properties:
        search_text:
          type: string
        filters:
          type: array
          items:
            $ref: '#/components/schemas/Filter'
        order_by:
          type: string
          default: created_at
        sort_direction:
          type: string
          enum:
            - asc
            - desc
          default: desc
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        cursor:
          type: string
        contact_id:
          type: string
        fields:
          type: array
          items:
            type: string
    Deal:
      type: object
      example:
        id: deal_7b1f4c9a2e6d8035
        deal_number: 204
        name: Northwind — Annual Platform License
        pipeline_id: pipe_2c9a1f
        stage_id: stage_proposal
        deal_status: open
        value: 48000
        priority: high
        type: new_business
        expected_close_date: '2026-07-31T00:00:00Z'
        owner_id: usr_3a7c91e2
        account_id: cmp_5d2e8a1b4c6f7091
        primary_contact_id: con_8f2a1c9b4d6e7f0a1b2c3d4e
        tags:
          - q3
          - expansion
        created_at: '2026-06-01T08:30:00Z'
        updated_at: '2026-06-12T14:05:00Z'
      properties:
        id:
          type: string
        deal_number:
          type: integer
        name:
          type: string
        pipeline_id:
          type: string
        stage_id:
          type: string
        deal_status:
          type: string
        value:
          type: number
        priority:
          type: string
          enum:
            - low
            - medium
            - high
        type:
          type: string
          enum:
            - new_business
            - existing_business
        expected_close_date:
          type: string
          format: date-time
        owner_id:
          type: string
        account_id:
          type: string
        primary_contact_id:
          type: string
        tags:
          type: array
          items:
            type: string
        lead_score:
          type: number
        custom_fields:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        created_by:
          type: string
        updated_by:
          type: string
      required:
        - id
        - created_at
        - updated_at
    DealCreate:
      type: object
      additionalProperties: false
      required:
        - name
        - pipeline_id
        - stage_id
        - owner_id
      example:
        name: Northwind — Annual Platform License
        pipeline_id: pipe_2c9a1f
        stage_id: stage_proposal
        owner_id: usr_3a7c91e2
        value: 48000
        priority: high
        type: new_business
        expected_close_date: '2026-07-31'
        account_id: cmp_5d2e8a1b4c6f7091
        primary_contact_id: con_8f2a1c9b4d6e7f0a1b2c3d4e
        tags:
          - q3
          - expansion
      properties:
        name:
          type: string
        pipeline_id:
          type: string
        stage_id:
          type: string
        owner_id:
          type: string
        value:
          type: number
        priority:
          type: string
          enum:
            - low
            - medium
            - high
        type:
          type: string
          enum:
            - new_business
            - existing_business
        expected_close_date:
          type: string
        account_id:
          type: string
        primary_contact_id:
          type: string
        tags:
          type: array
          items:
            type: string
        lead_score:
          type: number
        custom_fields:
          type: object
          additionalProperties: true
    DealUpdate:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
        pipeline_id:
          type: string
        stage_id:
          type: string
        owner_id:
          type: string
        value:
          type: number
        priority:
          type: string
          enum:
            - low
            - medium
            - high
        type:
          type: string
          enum:
            - new_business
            - existing_business
        expected_close_date:
          type: string
        account_id:
          type: string
        primary_contact_id:
          type: string
        tags:
          type: array
          items:
            type: string
        lead_score:
          type: number
        custom_fields:
          type: object
          additionalProperties: true
    DealSearchRequest:
      type: object
      additionalProperties: false
      properties:
        search_text:
          type: string
        filters:
          type: array
          items:
            $ref: '#/components/schemas/Filter'
        order_by:
          type: string
          default: created_at
        sort_direction:
          type: string
          enum:
            - asc
            - desc
          default: desc
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        cursor:
          type: string
        pipeline_id:
          type: string
        account_id:
          type: string
        contact_id:
          type: string
        fields:
          type: array
          items:
            type: string
    PipelineStage:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        probability:
          type: number
    Pipeline:
      type: object
      example:
        id: pipe_2c9a1f
        name: Sales Pipeline
        is_default: true
        stages:
          - id: stage_lead
            name: Lead
            probability: 10
          - id: stage_proposal
            name: Proposal
            probability: 50
          - id: stage_won
            name: Closed Won
            probability: 100
      properties:
        id:
          type: string
        name:
          type: string
        is_default:
          type: boolean
        stages:
          type: array
          items:
            $ref: '#/components/schemas/PipelineStage'
    Task:
      type: object
      example:
        id: task_4e9c2a7b1f6d8053
        task_number: 87
        title: Send Northwind the revised proposal
        description: Include the updated volume pricing tier.
        priority: high
        status:
          id: status_open
          name: Open
        type:
          id: type_followup
          name: Follow-up
        due_date: '2026-06-25T17:00:00Z'
        assignee_id: usr_3a7c91e2
        related_to:
          type: deal
          id: deal_7b1f4c9a2e6d8035
        tags:
          - proposal
        created_at: '2026-06-12T14:10:00Z'
        updated_at: '2026-06-12T14:10:00Z'
      properties:
        id:
          type: string
        task_number:
          type: integer
        title:
          type: string
        description:
          type: string
        priority:
          type: string
          enum:
            - low
            - medium
            - high
        status:
          $ref: '#/components/schemas/NamedRef'
        type:
          $ref: '#/components/schemas/NamedRef'
        due_date:
          type: string
          format: date-time
        assignee_id:
          type: string
        assigned_by_id:
          type: string
        team_id:
          type: string
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        tags:
          type: array
          items:
            type: string
        custom_fields:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        created_by:
          type: string
        updated_by:
          type: string
      required:
        - id
        - created_at
        - updated_at
    TaskCreate:
      type: object
      additionalProperties: false
      required:
        - title
      example:
        title: Send Northwind the revised proposal
        description: Include the updated volume pricing tier.
        priority: high
        due_date: '2026-06-25T17:00:00Z'
        assignee_id: usr_3a7c91e2
        related_to:
          type: deal
          id: deal_7b1f4c9a2e6d8035
        tags:
          - proposal
      properties:
        title:
          type: string
        description:
          type: string
        priority:
          type: string
          enum:
            - low
            - medium
            - high
        due_date:
          type: string
        assignee_id:
          type: string
        status_id:
          type: string
        type_id:
          type: string
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        tags:
          type: array
          items:
            type: string
        custom_fields:
          type: object
          additionalProperties: true
    TaskUpdate:
      allOf:
        - $ref: '#/components/schemas/TaskCreate'
      description: Partial update — same fields as create, all optional.
    TaskSearchRequest:
      type: object
      additionalProperties: false
      properties:
        search_text:
          type: string
        status_id:
          type: string
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        order_by:
          type: string
          default: created_at
        sort_direction:
          type: string
          enum:
            - asc
            - desc
          default: desc
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        cursor:
          type: string
        fields:
          type: array
          items:
            type: string
    Note:
      type: object
      example:
        id: note_6a2d9f1c4b7e8350
        body: Spoke with Avery — they want SSO before signing. Looping in solutions eng.
        related_to:
          type: contact
          id: con_8f2a1c9b4d6e7f0a1b2c3d4e
        is_pinned: true
        created_at: '2026-06-11T16:45:00Z'
        updated_at: '2026-06-11T16:45:00Z'
      properties:
        id:
          type: string
        body:
          type: string
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        is_pinned:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        created_by:
          type: string
        updated_by:
          type: string
      required:
        - id
    NoteCreate:
      type: object
      additionalProperties: false
      required:
        - body
        - related_to
      example:
        body: Spoke with Avery — they want SSO before signing. Looping in solutions eng.
        related_to:
          type: contact
          id: con_8f2a1c9b4d6e7f0a1b2c3d4e
      properties:
        body:
          type: string
          minLength: 1
        related_to:
          $ref: '#/components/schemas/RelatedTo'
    NoteSearchRequest:
      type: object
      additionalProperties: false
      required:
        - related_to
      properties:
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        search_text:
          type: string
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
    Activity:
      type: object
      example:
        id: act_9c3e1a7d2f5b8064
        type: call
        operation: completed
        related_to:
          type: contact
          id: con_8f2a1c9b4d6e7f0a1b2c3d4e
        title: Discovery call
        summary: 25-min call; confirmed budget and timeline for Q3.
        actor_id: usr_3a7c91e2
        occurred_at: '2026-06-10T17:00:00Z'
        created_at: '2026-06-10T17:24:00Z'
      properties:
        id:
          type: string
        type:
          type: string
          description: Activity type (email/call/meeting/note/...).
        operation:
          type: string
          description: created/sent/received/...
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        description:
          type: string
        title:
          type: string
        summary:
          type: string
        actor_id:
          type: string
        occurred_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
      required:
        - id
    ActivityLog:
      type: object
      additionalProperties: false
      required:
        - type
        - operation
        - related_to
      example:
        type: call
        operation: completed
        related_to:
          type: contact
          id: con_8f2a1c9b4d6e7f0a1b2c3d4e
        description: 25-min discovery call; confirmed budget and timeline for Q3.
      properties:
        type:
          type: string
        operation:
          type: string
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        description:
          type: string
        operand_id:
          type: string
    ActivitySearchRequest:
      type: object
      additionalProperties: false
      properties:
        related_to:
          $ref: '#/components/schemas/RelatedTo'
        type:
          type: string
        operation:
          type: string
        start_date:
          type: string
        end_date:
          type: string
        timezone:
          type: string
        order_by:
          type: string
          default: occurred_at
        sort_direction:
          type: string
          enum:
            - asc
            - desc
          default: desc
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        cursor:
          type: string
