openapi: 3.1.0
info:
  title: HyperRep API
  version: 2.0.0
  description: >
    The HyperRep API is a RESTful API for accessing organizations, contacts,
    companies, agents, conversations, messages, sales, and analytics.
    All requests are authenticated with an API key passed in the X-API-KEY header.

servers:
  - url: https://api.hyperrep.ai

security:
  - ApiKeyAuth: []

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY

  schemas:

    Organization:
      type: object
      properties:
        organization_id:
          type: string
          example: HR_id_example_organization_id
        name:
          type: string
          example: Acme Roofing

    Contact:
      type: object
      properties:
        contact_id:
          type: string
          example: HR_id_example_contact_id
        name:
          type: string
          example: Diana Prince
        email:
          type: string
          description: Primary email address for the contact (for example hal.jordan@example.com or hal+jobs@example.co.uk).
          example: diana.prince@example.com
        phone_number:
          type: string
          description: Primary phone number for the contact (for example +14155550199, 415-555-0199, (415) 555-0199, or 1-415-555-0199).
          example: 513-867-5309
        created_at:
          type: string
          format: date-time
          example: "2026-03-12T00:32:13.782565Z"
        company_id:
          type: string
          nullable: true
          example: HR_id_example_company_id
        custom_fields:
          type: object
          description: Custom contact field values keyed by field name.
          additionalProperties: true
          example:
            notes: Prefers SMS
        company_role:
          type: string
          nullable: true
          example: Facilities Manager

    Company:
      type: object
      properties:
        company_id:
          type: string
          example: HR_id_example_company_id
        name:
          type: string
          example: HyperRep Consumer Group
        domain:
          type: string
          nullable: true
          example: hyperrep.com
        custom_fields:
          type: object
          description: Custom company field values keyed by field name.
          additionalProperties: true
          example:
            notes: Priority account
        address:
          type: string
          nullable: true
          example: 123 Main St, Anytown USA
        agent_id:
          type: string
          nullable: true
          example: HR_id_example_agent_id
        created_at:
          type: string
          format: date-time
          example: "2026-03-10T12:00:00Z"

    CreateCompanyRequest:
      type: object
      required: [name]
      description: Request payload for creating a company.
      properties:
        name:
          type: string
          description: Required company name.
        domain:
          type: string
          nullable: true
          description: Optional company domain. Values are lowercased, protocol is removed, path is removed, and the value must include a dot.
          example: "https://ferrisaircraft.com/sales"
        custom_fields:
          type: object
          nullable: true
          description: Optional custom company field values keyed by field name.
          additionalProperties: true
        address:
          type: string
          nullable: true
        agent_id:
          type: string
          nullable: true
          description: Optional agent identifier. Must belong to the same organization if provided.

    Agent:
      type: object
      properties:
        agent_id:
          type: string
          example: HR_id_example_agent_id
        name:
          type: string
          example: Kara Danvers
        email:
          type: string
          nullable: true
          example: kara@example.com

    ConversationSummary:
      type: object
      properties:
        conversation_id:
          type: string
          example: HR_id_example_conversation_id
        start_at:
          type: string
          format: date-time
          example: "2026-04-29T14:00:00Z"
        last_activity_at:
          type: string
          format: date-time
          example: "2026-04-29T16:00:00Z"
        folder:
          type: string
          nullable: true
          example: inbox
        is_read:
          type: boolean
          example: false
        contact_id:
          type: string
          example: HR_id_example_contact_id

    EmailMetadata:
      type: object
      properties:
        email_message_id:
          type: string
          nullable: true
          example: "<177204662479.53482.15965180402643609222@mail.example.com>"
        cc_emails:
          type: array
          items:
            type: string
        bcc_emails:
          type: array
          items:
            type: string

    CallMetadata:
      type: object
      properties:
        duration:
          type: integer
          nullable: true
          description: Call duration in seconds.

    TrackedLink:
      type: object
      properties:
        link_id:
          type: string
          example: HR_id_example_tracked_link_id
        url:
          type: string
          example: https://example.com/quote
          description: Original destination URL.
        click_count:
          type: integer
          example: 3
          description: Number of tracked clicks for this link.

    Message:
      type: object
      properties:
        message_id:
          type: string
          example: HR_id_example_message_id
        conversation_id:
          type: string
          example: HR_id_example_conversation_id
        agent_id:
          type: string
          nullable: true
        modality:
          type: string
          example: email
          description: "Channel the message was sent through. Common values: email, call, text, live_chat."
        message_direction:
          type: string
          enum: [sent, received]
          example: sent
        is_form_request:
          type: boolean
          example: false
        subject:
          type: string
          nullable: true
          example: Following up on your inquiry
        content:
          type: string
          example: Hi Diana, just checking in on the quote we sent over last week.
        call_transcript:
          type: string
          nullable: true
        form_request_body:
          type: object
          nullable: true
          description: Submitted form data including UTM info and form fields. Null when is_form_request is false.
        sent_at:
          type: string
          format: date-time
          example: "2026-04-29T15:02:00Z"
        drafted_at:
          type: string
          format: date-time
          nullable: true
        email_metadata:
          $ref: '#/components/schemas/EmailMetadata'
        call_metadata:
          $ref: '#/components/schemas/CallMetadata'
        tracked_links:
          type: array
          items:
            $ref: '#/components/schemas/TrackedLink'

    AssociatedContact:
      type: object
      properties:
        contact_id:
          type: string
          example: HR_id_example_contact_id
        is_primary:
          type: boolean
          example: true

    QuoteProduct:
      type: object
      properties:
        quantity:
          type: integer
          example: 1
        unit_price:
          type: string
          example: "1200.00"
        product_name:
          type: string
          example: Legacy Seed Service

    QuoteDetails:
      type: object
      nullable: true
      properties:
        output:
          type: object
          properties:
            currency:
              type: string
              example: USD
            total_price:
              type: string
              example: "1200.00"
        arguments:
          type: object
          properties:
            products:
              type: array
              items:
                $ref: '#/components/schemas/QuoteProduct'
        agent_overrides:
          type: object
          nullable: true
          additionalProperties: true
          description: Agent-entered quote overrides.

    StatusEvent:
      type: object
      properties:
        label:
          type: string
          example: Accepted
        status:
          type: string
          nullable: true
          description: Internal status key for the stage, when present.
          example: accepted
        changed_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-03-13T01:28:41.829924Z"

    Sale:
      type: object
      properties:
        sale_id:
          type: string
          example: HR_id_example_sale_id
        associated_conversation_ids:
          type: array
          items:
            type: string
        company_id:
          type: string
          example: HR_id_example_company_id
        owner_id:
          type: string
          example: HR_id_example_agent_id
        associated_contacts:
          type: array
          items:
            $ref: '#/components/schemas/AssociatedContact'
        quote_details:
          $ref: '#/components/schemas/QuoteDetails'
        custom_fields:
          type: object
          description: Custom sales field values keyed by field name.
          additionalProperties: true
          example:
            notes: Backfilled sale for new sales framework compatibility.
        current_status:
          $ref: '#/components/schemas/StatusEvent'
          nullable: true
        status_history:
          type: array
          description: Ordered status events for the sale. Created sales begin at the status_id supplied on create.
          items:
            $ref: '#/components/schemas/StatusEvent'
        created_at:
          type: string
          format: date-time
          example: "2026-03-12T01:28:41.829924Z"
        last_activity_at:
          type: string
          format: date-time
          example: "2026-03-12T01:28:41.829924Z"

    SaleStatusDefinition:
      type: object
      properties:
        status_id:
          type: string
          example: HR_id_example_status_id
        label:
          type: string
          example: Quoted
        status:
          type: string
          nullable: true
          description: System status tag for the stage, when present.
          example: quote_sent
        position:
          type: integer
          description: Stage ordering within the pipeline (0-based).
          example: 1

    SaleStatusList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/SaleStatusDefinition'

    AssociatedContactInput:
      type: object
      required: [contact_id]
      properties:
        contact_id:
          type: string
          example: HR_id_example_contact_id
        is_primary:
          type: boolean
          default: false
          description: At most one contact can be primary; when none is marked, the first contact becomes primary.

    CreateSaleRequest:
      type: object
      description: At least one of company_id or associated_contacts is required.
      required: [status_id]
      properties:
        company_id:
          type: string
          nullable: true
          example: HR_id_example_company_id
        associated_contacts:
          type: array
          items:
            $ref: '#/components/schemas/AssociatedContactInput'
        owner_id:
          type: string
          nullable: true
          example: HR_id_example_agent_id
        status_id:
          type: string
          description: Initial pipeline stage from the sale statuses endpoint.
          example: HR_id_example_status_id
        custom_fields:
          type: object
          additionalProperties: true
          description: Custom sales field values keyed by field name. Validated against the organization's custom field definitions. Fields with the date presentation take a YYYY-MM-DD string.
        conversation_ids:
          type: array
          items:
            type: string

    UpdateSaleRequest:
      type: object
      description: >-
        Fields to update on a sale. Omitted fields are left unchanged. Setting
        company_id, owner_id, custom_fields, associated_contacts, or
        conversation_ids to null clears that value. At least one linked company
        or contact must remain after the update.
      properties:
        company_id:
          type: string
          nullable: true
          example: HR_id_example_company_id
        associated_contacts:
          type: array
          nullable: true
          description: Replaces the sale's associated contacts when provided.
          items:
            $ref: '#/components/schemas/AssociatedContactInput'
        owner_id:
          type: string
          nullable: true
          example: HR_id_example_agent_id
        status_id:
          type: string
          description: New current pipeline stage. Cannot be null.
          example: HR_id_example_status_id
        custom_fields:
          type: object
          nullable: true
          additionalProperties: true
          description: Custom field values to merge into the sale. Null clears all custom fields.
        conversation_ids:
          type: array
          nullable: true
          description: Replaces associated conversations when provided.
          items:
            type: string

    CustomFieldDefinition:
      type: object
      properties:
        key:
          type: string
          description: Generated field key to use in custom_fields payloads. HyperRep creates this from the admin label and appends a number when needed for uniqueness.
          example: tier
        label:
          type: string
          description: Human-readable field label configured in the HyperRep admin UI.
          example: Tier
        type:
          type: string
          enum: [string, number, boolean, enum]
          example: enum
        ui_type:
          type: string
          nullable: true
          description: Optional presentation hint (text, number, boolean, enum, date, address, or paragraph).
          example: enum
        options:
          type: array
          nullable: true
          items:
            type: string
          description: Allowed values when the field type is enum. Normalized from stored enum or options configuration.
          example: [Gold, Silver]
        default:
          nullable: true
          description: Default value snapshotted onto newly created records.

    CustomFieldsMetadata:
      type: object
      properties:
        sales:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldDefinition'
        contacts:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldDefinition'
        companies:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldDefinition'

    Task:
      type: object
      properties:
        task_id:
          type: string
          example: HR_id_example_task_id
        title:
          type: string
          example: Send onboarding docs
        notes:
          type: string
          nullable: true
        due_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
          description: When the task was completed. null while the task is open.
        agent_id:
          type: string
          nullable: true
          description: Agent the task is assigned to.
        company_id:
          type: string
          nullable: true
        contact_id:
          type: string
          nullable: true
        sale_id:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true

    TaskList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Task'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    CreateTaskRequest:
      type: object
      required: [title]
      description: A task can link to at most one of company_id, contact_id, or sale_id.
      properties:
        title:
          type: string
          example: Send onboarding docs
        notes:
          type: string
          nullable: true
        due_at:
          type: string
          format: date-time
          nullable: true
        agent_id:
          type: string
          nullable: true
          description: Agent to assign the task to. Must belong to the organization.
        company_id:
          type: string
          nullable: true
        contact_id:
          type: string
          nullable: true
        sale_id:
          type: string
          nullable: true

    UpdateTaskRequest:
      type: object
      description: >-
        Fields to update on a task. Omitted fields are left unchanged. Nullable
        fields may be set to null to clear them. A task can link to at most one
        of company_id, contact_id, or sale_id after the update.
      properties:
        title:
          type: string
          example: Send onboarding docs
        notes:
          type: string
          nullable: true
        due_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
          description: Set to a timestamp to complete the task, or null to reopen it.
        agent_id:
          type: string
          nullable: true
          description: Agent to assign the task to. Must belong to the organization.
        company_id:
          type: string
          nullable: true
        contact_id:
          type: string
          nullable: true
        sale_id:
          type: string
          nullable: true

    PipelineStage:
      type: object
      properties:
        name:
          type: string
          example: Qualified
        status:
          type: string
          nullable: true
          description: Internal status key. null for the first (Qualified) stage.
          example: null
        volume:
          type: integer
          example: 24
        value:
          type: string
          description: Total monetary value of sales in this stage as a decimal string.
          example: "28800.00"

    SalesAnalytics:
      type: object
      properties:
        pipeline:
          type: array
          items:
            $ref: '#/components/schemas/PipelineStage'
        salesRate:
          type: number
          format: float
          example: 0.46

    CursorPagination:
      type: object
      properties:
        limit:
          type: integer
          example: 25
        cursor:
          type: string
          nullable: true
        next_cursor:
          type: string
          nullable: true
        has_more:
          type: boolean

    OrganizationPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Organization'

    ContactPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Contact'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    CompanyPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Company'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    AgentPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Agent'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    ConversationPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ConversationSummary'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    MessagePage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Message'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    MessageDraft:
      type: object
      properties:
        draft_id:
          type: string
          example: HR_id_example_message_draft_id
        subject:
          type: string
          nullable: true
        body_html:
          type: string
          nullable: true
        modality:
          type: string
          enum: [email, text, live_chat, call]
        agent_id:
          type: string
          nullable: true
          description: Encoded `admin_profiles.uuid`.
        recipient_name:
          type: string
          nullable: true
        recipient_email:
          type: string
          nullable: true
        recipient_phone_number:
          type: string
          nullable: true
        cc_emails:
          type: array
          items:
            type: string
        bcc_emails:
          type: array
          items:
            type: string
        scheduled_send_at:
          type: string
          format: date-time
          nullable: true
        folder:
          type: string
          example: sales-follow-up
        created_at:
          type: string
          format: date-time

    MessageDraftPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/MessageDraft'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    ResponseDraft:
      type: object
      properties:
        response_id:
          type: string
          example: HR_id_example_response_draft_id
        conversation_id:
          type: string
          example: HR_id_example_conversation_id
        body_html:
          type: string
          nullable: true
        recipient_name:
          type: string
          nullable: true
        recipient_email:
          type: string
          nullable: true
        recipient_phone_number:
          type: string
          nullable: true
        cc_emails:
          type: array
          items:
            type: string
        bcc_emails:
          type: array
          items:
            type: string
        scheduled_send_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time

    ResponseDraftPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ResponseDraft'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    SalePage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Sale'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    AuditLogRecord:
      type: object
      properties:
        type:
          type: string
          enum: [sale, conversation, message_draft, response_draft]
          example: sale
        id:
          type: string
          nullable: true
          example: HR_id_example_sale_id

    AuditLogChange:
      type: object
      properties:
        field:
          type: string
          example: status
        from:
          nullable: true
        to:
          nullable: true

    AuditLog:
      type: object
      properties:
        audit_log_id:
          type: string
          example: HR_id_example_audit_log_id
        occurred_at:
          type: string
          format: date-time
          example: "2026-05-21T17:42:31.000000Z"
        agent_id:
          type: string
          nullable: true
          example: HR_id_example_agent_id
        action_type:
          type: string
          description: Audit log action type.
          enum: [insert, update, delete]
          example: update
        record:
          $ref: '#/components/schemas/AuditLogRecord'
        changes:
          type: array
          items:
            $ref: '#/components/schemas/AuditLogChange'

    AuditLogPage:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AuditLog'
        pagination:
          $ref: '#/components/schemas/CursorPagination'

    CreateContactRequest:
      type: object
      description: Request payload for creating a contact.
      oneOf:
        - required: [name]
        - required: [email]
        - required: [phone_number]
      properties:
        name:
          type: string
          nullable: true
        email:
          type: string
          description: Optional primary email address for the contact (for example hal.jordan@example.com or hal+jobs@example.co.uk).
          nullable: true
        phone_number:
          type: string
          description: Optional primary phone number for the contact (for example +14155550199, 415-555-0199, (415) 555-0199, or 1-415-555-0199).
          nullable: true
        company_id:
          type: string
          nullable: true
        custom_fields:
          type: object
          nullable: true
          description: Optional custom contact field values keyed by field name.
          additionalProperties: true
        company_role:
          type: string
          nullable: true

    CreateDraftRequest:
      type: object
      required: [modality]
      description: Request payload for creating a chat draft.
      properties:
        modality:
          type: string
          enum: [email, text]
          description: Draft delivery channel. The organization must have credentials configured for the selected modality.
        subject:
          type: string
          nullable: true
        body_html:
          type: string
          nullable: true
          description: HTML body for the message draft.
        to_user_name:
          type: string
          nullable: true
        to_email:
          type: string
          nullable: true
        to_phone_number:
          type: string
          nullable: true
        cc_emails:
          type: array
          items:
            type: string
        bcc_emails:
          type: array
          items:
            type: string
        autosend_draft:
          type: boolean
          default: false
        add_notification:
          type: boolean
          default: true
        folder:
          type: string
          nullable: true

    DraftCreateResponse:
      type: object
      required: [draft_id, scheduled_to_send, folder]
      description: Response payload after creating a chat draft.
      properties:
        draft_id:
          type: string
          example: HR_id_example_message_draft_id
        scheduled_to_send:
          type: boolean
        scheduled_send_at:
          type: string
          format: date-time
          nullable: true
        folder:
          type: string
          example: drafts

    CreateResponseDraftRequest:
      type: object
      required: [body_html]
      description: Request payload for creating or updating a conversation response draft.
      properties:
        body_html:
          type: string
          description: HTML reply body for the response draft within the existing conversation.
        cc_emails:
          type: array
          items:
            type: string
        bcc_emails:
          type: array
          items:
            type: string
        autosend_draft:
          type: boolean
          default: false
        add_notification:
          type: boolean
          default: true

    ResponseDraftCreateResponse:
      type: object
      required: [response_id, conversation_id, scheduled_to_send]
      description: Response payload after creating or updating a conversation response draft.
      properties:
        response_id:
          type: string
          example: HR_id_example_response_draft_id
        conversation_id:
          type: string
          example: HR_id_example_conversation_id
        scheduled_to_send:
          type: boolean
        scheduled_send_at:
          type: string
          format: date-time
          nullable: true

    LegacySolution:
      type: object
      properties:
        uuid:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          example: Acme Roofing

  parameters:
    organization_id:
      name: organization_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_organization_id

    contact_id:
      name: contact_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_contact_id

    company_id:
      name: company_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_company_id

    agent_id:
      name: agent_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_agent_id

    conversation_id:
      name: conversation_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_conversation_id

    message_id:
      name: message_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_message_id

    draft_id:
      name: draft_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_message_draft_id

    response_id:
      name: response_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_response_draft_id

    sale_id:
      name: sale_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_sale_id

    audit_log_id:
      name: audit_log_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_audit_log_id

    task_id:
      name: task_id
      in: path
      required: true
      schema:
        type: string
      example: HR_id_example_task_id

    audit_agent_id:
      name: agent_id
      in: query
      required: false
      schema:
        type: string
      description: Optional agent ID to filter audit logs.

    audit_record_type:
      name: record_type
      in: query
      required: false
      schema:
        type: string
        enum: [sale, conversation, message_draft, response_draft]
      description: Optional record type filter for audit logs.

    audit_action_type:
      name: action_type
      in: query
      required: false
      schema:
        type: string
        enum: [insert, update, delete]
      description: Optional action type filter for audit logs.

    limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
      description: Number of items to return.

    cursor:
      name: cursor
      in: query
      required: false
      schema:
        type: string
      description: Cursor value from a previous response to fetch the next page.

    start_date_optional:
      name: start_date
      in: query
      required: false
      schema:
        type: string
        format: date-time
      description: Optional. Filter results to items on or after this timestamp.
      example: "2026-04-01T00:00:00Z"

    end_date_optional:
      name: end_date
      in: query
      required: false
      schema:
        type: string
        format: date-time
      description: Optional. Filter results to items before this timestamp.
      example: "2026-04-30T23:59:59Z"

    start_date_required:
      name: start_date
      in: query
      required: true
      schema:
        type: string
        format: date-time
      description: Required. Inclusive start of the analytics window.
      example: "2026-04-01T00:00:00Z"

    end_date_required:
      name: end_date
      in: query
      required: true
      schema:
        type: string
        format: date-time
      description: Required. Exclusive end of the analytics window.
      example: "2026-04-30T23:59:59Z"

paths:

  # ── Organizations ──────────────────────────────────────────────────────────

  /api/v2/organizations:
    get:
      summary: List organizations
      operationId: listOrganizations
      tags: [Organizations]
      responses:
        '200':
          description: Organizations available to the API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationPage'

  # ── Contacts ─────────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/contacts:
    get:
      summary: List contacts
      operationId: listContacts
      tags: [Contacts]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
      responses:
        '200':
          description: Contacts for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactPage'
    post:
      summary: Create a contact
      operationId: createContact
      tags: [Contacts]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContactRequest'
      responses:
        '201':
          description: Contact created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          description: Invalid request payload.
        '404':
          description: Organization or company not found.

  /api/v2/organizations/{organization_id}/contacts/{contact_id}:
    get:
      summary: Get a contact
      operationId: getContact
      tags: [Contacts]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/contact_id'
      responses:
        '200':
          description: A single contact.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'

  /api/v2/organizations/{organization_id}/contacts/{contact_id}/conversations:
    get:
      summary: List conversations for a contact
      operationId: listContactConversations
      tags: [Contacts]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/contact_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated conversations for the contact.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationPage'
        '404':
          description: Contact not found.

  # ── Companies ──────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/companies:
    get:
      summary: List companies
      operationId: listCompanies
      tags: [Companies]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
      responses:
        '200':
          description: Companies for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyPage'
    post:
      summary: Create a company
      operationId: createCompany
      tags: [Companies]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCompanyRequest'
      responses:
        '201':
          description: Company created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Company'
        '400':
          description: Invalid request payload.
        '404':
          description: Organization or agent not found.

  /api/v2/organizations/{organization_id}/companies/{company_id}:
    get:
      summary: Get a company
      operationId: getCompany
      tags: [Companies]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/company_id'
      responses:
        '200':
          description: A single company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Company'

  # ── Agents ─────────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/agents:
    get:
      summary: List agents
      operationId: listAgents
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
      responses:
        '200':
          description: Agents for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentPage'

  /api/v2/organizations/{organization_id}/agents/{agent_id}:
    get:
      summary: Get an agent
      operationId: getAgent
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/agent_id'
      responses:
        '200':
          description: A single agent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'

  /api/v2/organizations/{organization_id}/agents/{agent_id}/conversations:
    get:
      summary: List agent conversations
      operationId: listAgentConversations
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/agent_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated conversations owned by the agent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationPage'

  /api/v2/organizations/{organization_id}/agents/{agent_id}/sales:
    get:
      summary: List agent sales
      operationId: listAgentSales
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/agent_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated sales owned by the agent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalePage'

  /api/v2/organizations/{organization_id}/agents/{agent_id}/analytics:
    get:
      summary: Get combined agent analytics
      operationId: getAgentAnalytics
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/agent_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Combined conversation and sales analytics for the agent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  conversations:
                    type: object
                    properties:
                      conversationsRespondedTo:
                        type: integer
                        description: Distinct conversations where the agent sent at least one response in the selected window.
                        example: 42
                      messagesSent:
                        type: integer
                        example: 118
                  sales:
                    $ref: '#/components/schemas/SalesAnalytics'

  /api/v2/organizations/{organization_id}/agents/{agent_id}/analytics/conversations:
    get:
      summary: Get agent conversation analytics
      operationId: getAgentConversationAnalytics
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/agent_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Conversation analytics for the agent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  conversationsRespondedTo:
                    type: integer
                    description: Distinct conversations where the agent sent at least one response in the selected window.
                    example: 42
                  messagesSent:
                    type: integer
                    example: 118

  /api/v2/organizations/{organization_id}/agents/{agent_id}/analytics/sales:
    get:
      summary: Get agent sales analytics
      operationId: getAgentSalesAnalytics
      tags: [Agents]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/agent_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Sales analytics for the agent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalesAnalytics'

  # ── Conversations ──────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/conversations:
    get:
      summary: List conversations
      operationId: listConversations
      tags: [Conversations]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated conversations for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationPage'

  /api/v2/organizations/{organization_id}/conversations/{conversation_id}:
    get:
      summary: Get a conversation
      operationId: getConversation
      tags: [Conversations]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
      responses:
        '200':
          description: A single conversation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationSummary'

  # ── Messages ───────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/conversations/{conversation_id}/messages:
    get:
      summary: List messages
      operationId: listMessages
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated messages for the conversation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessagePage'

  /api/v2/organizations/{organization_id}/conversations/{conversation_id}/messages/{message_id}:
    get:
      summary: Get a message
      operationId: getMessage
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
        - $ref: '#/components/parameters/message_id'
      responses:
        '200':
          description: A single message.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'

  /api/v2/organizations/{organization_id}/message-drafts:
    get:
      summary: List message drafts
      operationId: listMessageDrafts
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated message drafts for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageDraftPage'
    post:
      summary: Create a message draft
      operationId: createMessageDraft
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDraftRequest'
      responses:
        '201':
          description: Created message draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DraftCreateResponse'
        '400':
          description: Invalid draft request.
        '404':
          description: Organization not found.

  /api/v2/organizations/{organization_id}/message-drafts/{draft_id}:
    get:
      summary: Get a message draft
      operationId: getMessageDraft
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/draft_id'
      responses:
        '200':
          description: A single message draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageDraft'
        '404':
          description: Message draft not found, already promoted to a conversation, or ID belongs to a response draft.
    delete:
      summary: Delete a message draft
      operationId: deleteMessageDraft
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/draft_id'
      responses:
        '204':
          description: Message draft deleted.
        '400':
          description: Draft is currently sending or request is invalid.
        '404':
          description: Message draft not found, already promoted to a conversation, or ID belongs to a response draft.

  /api/v2/organizations/{organization_id}/conversations/{conversation_id}/response-drafts:
    get:
      summary: List response drafts
      operationId: listResponseDrafts
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated response drafts for the conversation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseDraftPage'
    post:
      summary: Create or update a response draft
      operationId: createResponseDraft
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateResponseDraftRequest'
      responses:
        '201':
          description: Created or updated response draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseDraftCreateResponse'
        '400':
          description: Invalid response draft request.
        '404':
          description: Organization or conversation not found.

  /api/v2/organizations/{organization_id}/conversations/{conversation_id}/response-drafts/{response_id}:
    get:
      summary: Get a response draft
      operationId: getResponseDraft
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
        - $ref: '#/components/parameters/response_id'
      responses:
        '200':
          description: A single response draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseDraft'
        '404':
          description: Response draft not found, already promoted to a conversation message, or ID belongs to a message draft.
    delete:
      summary: Delete a response draft
      operationId: deleteResponseDraft
      tags: [Messages]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/conversation_id'
        - $ref: '#/components/parameters/response_id'
      responses:
        '204':
          description: Response draft deleted.
        '400':
          description: Draft is currently sending or request is invalid.
        '404':
          description: Response draft not found, already promoted to a conversation message, or ID belongs to a message draft.

  # ── Analytics ──────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/analytics/conversations:
    get:
      summary: Get combined conversation analytics
      operationId: getConversationAnalytics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Combined conversation analytics for the organization.
          content:
            application/json:
              schema:
                type: object
                properties:
                  responseRateStats:
                    type: object
                    properties:
                      numConversationsRespondedTo:
                        type: integer
                        description: Conversations whose first inbound message landed in the selected window and whose first assistant response after that message exists.
                        example: 14
                      numConversationsReceived:
                        type: integer
                        description: Conversations whose first inbound message landed in the selected window.
                        example: 20
                      responseRate:
                        type: number
                        format: float
                        example: 0.7
                  responseTimeStats:
                    type: object
                    properties:
                      responseTime:
                        type: integer
                        description: Median response time in seconds.
                        example: 3600
                  volumeStats:
                    type: object
                    properties:
                      numMessagesReceived:
                        type: integer
                        example: 120
                      numMessagesSent:
                        type: integer
                        example: 118
                      totalConversations:
                        type: integer
                        example: 42
                  modalityStats:
                    type: object
                    properties:
                      textMessages:
                        type: integer
                        example: 10
                      calls:
                        type: integer
                        example: 4
                      emails:
                        type: integer
                        example: 20
                      liveChats:
                        type: integer
                        example: 6
                      formRequests:
                        type: integer
                        example: 2

  /api/v2/organizations/{organization_id}/analytics/conversations/response-rate:
    get:
      summary: Get response rate analytics
      operationId: getResponseRateAnalytics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Response rate stats for the organization.
          content:
            application/json:
              schema:
                type: object
                properties:
                  numConversationsRespondedTo:
                    type: integer
                    description: Conversations whose first inbound message landed in the selected window and whose first assistant response after that message exists.
                    example: 14
                  numConversationsReceived:
                    type: integer
                    description: Conversations whose first inbound message landed in the selected window.
                    example: 20
                  responseRate:
                    type: number
                    format: float
                    example: 0.7

  /api/v2/organizations/{organization_id}/analytics/conversations/response-time:
    get:
      summary: Get response time analytics
      operationId: getResponseTimeAnalytics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Response time stats for the organization.
          content:
            application/json:
              schema:
                type: object
                properties:
                  responseTime:
                    type: integer
                    description: Median response time in seconds.
                    example: 3600

  /api/v2/organizations/{organization_id}/analytics/conversations/volume:
    get:
      summary: Get conversation volume analytics
      operationId: getVolumeAnalytics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Conversation volume stats for the organization.
          content:
            application/json:
              schema:
                type: object
                properties:
                  numMessagesReceived:
                    type: integer
                    example: 120
                  numMessagesSent:
                    type: integer
                    example: 118
                  totalConversations:
                    type: integer
                    example: 42

  /api/v2/organizations/{organization_id}/analytics/conversations/modality:
    get:
      summary: Get modality analytics
      operationId: getModalityAnalytics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Distribution of first inbound conversation message modalities.
          content:
            application/json:
              schema:
                type: object
                properties:
                  textMessages:
                    type: integer
                    example: 10
                  calls:
                    type: integer
                    example: 4
                  emails:
                    type: integer
                    example: 20
                  liveChats:
                    type: integer
                    example: 6
                  formRequests:
                    type: integer
                    example: 2

  /api/v2/organizations/{organization_id}/analytics/sales:
    get:
      summary: Get sales analytics
      operationId: getSalesAnalytics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Sales pipeline analytics for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalesAnalytics'

  # ── Audit Logs ─────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/audit-logs:
    get:
      summary: List audit logs
      operationId: listAuditLogs
      tags: [Audit Logs]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
        - $ref: '#/components/parameters/audit_agent_id'
        - $ref: '#/components/parameters/audit_record_type'
        - $ref: '#/components/parameters/audit_action_type'
      responses:
        '200':
          description: Paginated audit logs for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditLogPage'

  /api/v2/organizations/{organization_id}/audit-logs/{audit_log_id}:
    get:
      summary: Get an audit log
      operationId: getAuditLog
      tags: [Audit Logs]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/audit_log_id'
      responses:
        '200':
          description: A single audit log entry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditLog'

  # ── Sales ──────────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/sales:
    get:
      summary: List sales
      operationId: listSales
      tags: [Sales]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/start_date_optional'
        - $ref: '#/components/parameters/end_date_optional'
      responses:
        '200':
          description: Paginated sales for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalePage'
    post:
      summary: Create a sale
      operationId: createSale
      description: >-
        Creates a lead record: pipeline stage, contacts, and custom fields.
        A sale value or quote information cannot be included yet.
      tags: [Sales]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSaleRequest'
      responses:
        '201':
          description: Sale created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sale'
        '400':
          description: Invalid request payload or custom field value.
        '404':
          description: Organization, company, contact, agent, status, or conversation not found.

  /api/v2/organizations/{organization_id}/sales/{sale_id}:
    get:
      summary: Get a sale
      operationId: getSale
      tags: [Sales]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/sale_id'
      responses:
        '200':
          description: A single sale.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sale'
    patch:
      summary: Update a sale
      operationId: updateSale
      tags: [Sales]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/sale_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSaleRequest'
      responses:
        '200':
          description: Sale updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sale'
        '400':
          description: Invalid request payload or custom field value.
        '404':
          description: Organization, sale, company, contact, agent, status, or conversation not found.

  /api/v2/organizations/{organization_id}/sale-statuses:
    get:
      summary: List sale statuses
      operationId: listSaleStatuses
      tags: [Sales]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      responses:
        '200':
          description: Pipeline stages configured for the organization, ordered by position.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleStatusList'

  # ── Custom Fields ──────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/custom-fields:
    get:
      summary: Get custom fields
      operationId: getCustomFields
      tags: [Custom Fields]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      responses:
        '200':
          description: Custom field definitions for the organization's sales, contacts, and companies.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomFieldsMetadata'

  # ── Tasks ──────────────────────────────────────────────────────────────────

  /api/v2/organizations/{organization_id}/tasks:
    get:
      summary: List tasks
      operationId: listTasks
      tags: [Tasks]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
      responses:
        '200':
          description: Paginated tasks for the organization, newest first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskList'
    post:
      summary: Create a task
      operationId: createTask
      tags: [Tasks]
      parameters:
        - $ref: '#/components/parameters/organization_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaskRequest'
      responses:
        '201':
          description: Task created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          description: Invalid request payload, or more than one linked entity provided.
        '404':
          description: Organization, agent, company, contact, or sale not found.

  /api/v2/organizations/{organization_id}/tasks/{task_id}:
    get:
      summary: Get a task
      operationId: getTask
      tags: [Tasks]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/task_id'
      responses:
        '200':
          description: One task.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '404':
          description: Organization or task not found.
    patch:
      summary: Update a task
      operationId: updateTask
      tags: [Tasks]
      parameters:
        - $ref: '#/components/parameters/organization_id'
        - $ref: '#/components/parameters/task_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskRequest'
      responses:
        '200':
          description: Task updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          description: Invalid request payload, or more than one linked entity provided.
        '404':
          description: Organization, task, agent, company, contact, or sale not found.

  # ── Legacy v1 ──────────────────────────────────────────────────────────────

  /api/v1/solutions:
    get:
      summary: List solutions (legacy)
      operationId: listSolutions
      tags: [Legacy v1]
      responses:
        '200':
          description: Array of legacy solutions available to the API key.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/LegacySolution'

  /api/v1/solutions/{solution_id}/analytics:
    get:
      summary: Get solution analytics (legacy)
      operationId: getSolutionAnalytics
      tags: [Legacy v1]
      parameters:
        - name: solution_id
          in: path
          required: true
          schema:
            type: string
          example: 550e8400-e29b-41d4-a716-446655440000
        - $ref: '#/components/parameters/start_date_required'
        - $ref: '#/components/parameters/end_date_required'
      responses:
        '200':
          description: Analytics for a legacy solution.
          content:
            application/json:
              schema:
                type: object
                properties:
                  responseRateStats:
                    type: object
                    properties:
                      numChatsSent:
                        type: integer
                      numPendingChats:
                        type: integer
                      responseRate:
                        type: number
                  responseTimeStats:
                    type: object
                    properties:
                      responseTime:
                        type: number
                      responseTimeBusinessHours:
                        type: number
                      numResponses:
                        type: integer
                      numResponsesBusinessHours:
                        type: integer
                  salesStats:
                    type: object
                    properties:
                      averageSpeedToSale:
                        type: number
                      numSalesCounted:
                        type: integer
                  valueStats:
                    type: object
                    properties:
                      salesValue:
                        type: number
                      quoteValue:
                        type: number
                  salesFunnelStats:
                    type: object
                    properties:
                      source:
                        type: object
                        properties:
                          sessionsDirectEmail:
                            type: integer
                          sessionsIndirectEmail:
                            type: integer
                          sessionsFormRequest:
                            type: integer
                      response:
                        type: object
                        properties:
                          sessionsLeadResponded:
                            type: integer
                          sessionsGhosted:
                            type: integer
                          sessionsResponseSent:
                            type: integer
                          sessionsPendingOutreach:
                            type: integer
                      outcome:
                        type: object
                        properties:
                          sessionsQuoted:
                            type: integer
                      sale:
                        type: object
                        properties:
                          sessionsSale:
                            type: integer
                  autonomyRateStats:
                    type: object
                    properties:
                      chatsSentWithChanges:
                        type: integer
                      chatsSentWithoutChanges:
                        type: integer
                      autonomyRate:
                        type: number
                  numLeads:
                    type: integer
                  leadsHandled:
                    type: integer
                  salesRate:
                    type: number
                  quoteRate:
                    type: number
                  ghostRate:
                    type: number
                  leadResponseScore:
                    type: number
