openapi: 3.1.0
info:
  title: Kapso Platform API
  version: 0.2.0
  description: |
    Build WhatsApp messaging into your product. Manage customers, connect phone numbers, send broadcasts, and handle conversations.

servers:
  - url: https://api.kapso.ai/platform/v1
    description: Production

security:
  - ApiKeyAuth: []

tags:
  - name: Customers
    description: Manage customer accounts
  - name: Setup Links
    description: WhatsApp onboarding via embedded signup
  - name: Phone Numbers
    description: Connect and manage WhatsApp numbers
  - name: Webhooks
    description: Subscribe to WhatsApp events
  - name: Display Names
    description: Update WhatsApp business display names
  - name: Broadcasts
    description: |
      Send template messages at scale

      **Alpha**: This API is in alpha and subject to change
  - name: Conversations
    description: Manage conversation state
  - name: Media
    description: Upload media files for WhatsApp messaging
  - name: Users
    description: Manage project team members
  - name: Inbox Embeds
    description: Create and manage embeddable inbox access links
  - name: Webhook Deliveries
    description: View webhook delivery attempts and their status
  - name: External API Logs
    description: View logs of external API calls made by the project
  - name: Log Search
    description: Search log events across API, Meta, workflow, and webhook sources
  - name: Events
    description: Emit and query project-scoped events
  - name: Provider Models
    description: List available AI provider models
  - name: WhatsApp Flows
    description: Build interactive WhatsApp Flows for surveys and forms
  - name: Contacts
    description: Manage WhatsApp contacts

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

  schemas:
    WhatsappContact:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique contact ID
          example: "123e4567-e89b-12d3-a456-426614174000"
        wa_id:
          type:
            - string
            - "null"
          description: WhatsApp ID (E.164 phone number). Can be null when Meta only
            provides BSUID-based identity.
          example: "15551234567"
        business_scoped_user_id:
          type:
            - string
            - "null"
          description: WhatsApp business-scoped user ID
          example: "US.13491208655302741918"
        parent_business_scoped_user_id:
          type:
            - string
            - "null"
          description: Parent business-scoped user ID when provided by Meta
          example: "US.ENT.506847293015824"
        username:
          type:
            - string
            - "null"
          description: WhatsApp username when available
          example: "@testusername"
        profile_name:
          type: string
          description: Name as shown on WhatsApp
          example: "John Doe"
        display_name:
          type: string
          nullable: true
          description: Custom display name set by your app
          example: "John (VIP)"
        customer_id:
          type: string
          format: uuid
          nullable: true
          description: Associated customer ID
        metadata:
          type: object
          nullable: true
          additionalProperties: true
          description: Custom key-value data
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    WhatsappContactResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappContact"

    WhatsappContactListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappContact"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
        paging:
          $ref: "#/components/schemas/Paging"

    WhatsappContactMarketingPreference:
      type: object
      description: A contact's marketing message preference on one of your
        WhatsApp numbers. Recorded when the contact stops or resumes marketing
        messages inside WhatsApp. Read-only — only the contact can change it.
      properties:
        id:
          type: string
          description: Meta phone number ID of the number the preference applies to
          example: "123456789012345"
        phone_number_id:
          type: string
          description: Meta phone number ID (same as `id`)
          example: "123456789012345"
        status:
          type: string
          enum:
            - stopped
            - resumed
          description: Current preference. `stopped` blocks marketing template
            sends to this contact on this number.
        detail:
          type:
            - string
            - "null"
          description: Free-text reason reported by Meta
          example: User requested to stop marketing messages
        source:
          type: string
          description: How the preference was captured
          example: meta_webhook
        marketing_allowed:
          type: boolean
          description: Whether marketing template sends are allowed. `false` when
            `status` is `stopped`.
        config_display_name:
          type:
            - string
            - "null"
          description: Display name of the WhatsApp number
        last_event_at:
          type: string
          format: date-time
          description: Timestamp WhatsApp reported for the latest preference change
        whatsapp_contact_id:
          type: string
          format: uuid
        internal_id:
          type: string
          format: uuid
          description: Internal ID of the preference record
        whatsapp_config_id:
          type: string
          format: uuid
          description: Internal ID of the WhatsApp number's configuration
        whatsapp_config_internal_id:
          type: string
          format: uuid
          description: Same as `whatsapp_config_id`
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    WhatsappContactMarketingPreferenceResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappContactMarketingPreference"

    WhatsappContactMarketingPreferenceListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappContactMarketingPreference"

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string

    InboxEmbed:
      type: object
      required:
        - id
        - scope_type
        - status
        - allowed_origins
        - default_mode
        - language
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type:
            - string
            - "null"
        scope_type:
          type: string
          description: Public embed scope
          enum:
            - project
            - customer
            - phone_number
        scope_id:
          type:
            - string
            - "null"
          description: Customer UUID for `customer`, WhatsApp `phone_number_id` for
            `phone_number`, null for `project`
        scope_name:
          type:
            - string
            - "null"
          description: Human-readable customer or phone number label when available
        assigned_user_id:
          type:
            - string
            - "null"
          format: uuid
          description: Limits visible conversations to the active assignee
        assigned_user_name:
          type:
            - string
            - "null"
        status:
          type: string
          enum:
            - active
            - revoked
        allowed_origins:
          type: array
          description: Origins allowed to embed the inbox. Empty means any origin.
          items:
            type: string
          example:
            - https://app.example.com
        default_mode:
          type: string
          enum:
            - system
            - light
            - dark
        language:
          type: string
          enum:
            - en
            - es
          description: UI language for the embedded inbox.
          default: en
        expires_at:
          type:
            - string
            - "null"
          format: date-time
        last_used_at:
          type:
            - string
            - "null"
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    InboxEmbedCreated:
      allOf:
        - $ref: "#/components/schemas/InboxEmbed"
        - type: object
          required:
            - token
            - embed_url
          properties:
            token:
              type: string
              description: Raw inbox token. Returned only when the embed is created.
            embed_url:
              type: string
              format: uri
              description: Iframe URL. Returned only when the embed is created.

    InboxEmbedCreateRequest:
      type: object
      required:
        - inbox_embed
      properties:
        inbox_embed:
          type: object
          required:
            - scope_type
          properties:
            name:
              type:
                - string
                - "null"
            scope_type:
              type: string
              enum:
                - project
                - customer
                - phone_number
            scope_id:
              type:
                - string
                - "null"
              description: Required for `customer` and `phone_number`; must be blank for
                `project`
            assigned_user_id:
              type:
                - string
                - "null"
              format: uuid
            allowed_origins:
              type: array
              items:
                type: string
            default_mode:
              type: string
              enum:
                - system
                - light
                - dark
              default: system
            language:
              type: string
              enum:
                - en
                - es
              description: UI language for the embedded inbox.
              default: en
            expires_at:
              type:
                - string
                - "null"
              format: date-time

    InboxEmbedUpdateRequest:
      type: object
      required:
        - inbox_embed
      properties:
        inbox_embed:
          type: object
          properties:
            name:
              type:
                - string
                - "null"
            assigned_user_id:
              type:
                - string
                - "null"
              format: uuid
            allowed_origins:
              type: array
              items:
                type: string
            default_mode:
              type: string
              enum:
                - system
                - light
                - dark
            language:
              type: string
              enum:
                - en
                - es
              description: UI language for the embedded inbox.
            expires_at:
              type:
                - string
                - "null"
              format: date-time

    InboxEmbedResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/InboxEmbed"

    InboxEmbedCreatedResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/InboxEmbedCreated"

    InboxEmbedListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/InboxEmbed"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    PaginationMeta:
      type: object
      required:
        - page
        - per_page
        - total_pages
        - total_count
      properties:
        page:
          type: integer
          minimum: 1
        per_page:
          type: integer
          minimum: 1
        total_pages:
          type: integer
          minimum: 0
        total_count:
          type: integer
          minimum: 0

    PaginationCursor:
      type: object
      properties:
        before:
          type: string
          description: Cursor for previous page (Base64 encoded)
        after:
          type: string
          description: Cursor for next page (Base64 encoded)

    Paging:
      type: object
      properties:
        cursors:
          $ref: "#/components/schemas/PaginationCursor"
        next:
          type: string
          nullable: true
        previous:
          type: string
          nullable: true

    Customer:
      type: object
      required:
        - id
        - name
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        external_customer_id:
          type:
            - string
            - "null"
          description: Your internal customer ID
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CustomerCreateRequest:
      type: object
      required:
        - customer
      properties:
        customer:
          type: object
          required:
            - name
          properties:
            name:
              type: string
            external_customer_id:
              type:
                - string
                - "null"

    CustomerUpdateRequest:
      type: object
      required:
        - customer
      properties:
        customer:
          type: object
          properties:
            name:
              type: string
            external_customer_id:
              type:
                - string
                - "null"

    CustomerResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/Customer"

    CustomerListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Customer"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    PhoneNumberReference:
      type: object
      required:
        - id
        - phone_number
        - status
        - country_iso
        - country_dial_code
      properties:
        id:
          type: string
          format: uuid
        phone_number:
          type: string
        status:
          type: string
        area_code:
          type:
            - string
            - "null"
        country_iso:
          type: string
        country_dial_code:
          type: string
        display_number:
          type:
            - string
            - "null"

    SetupLink:
      type: object
      required:
        - id
        - status
        - created_at
        - url
        - meta_billing_mode
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - active
            - used
            - expired
            - revoked
        created_at:
          type: string
          format: date-time
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: Expires 30 days after creation by default
        url:
          type: string
          format: uri
        success_redirect_url:
          type:
            - string
            - "null"
          format: uri
        failure_redirect_url:
          type:
            - string
            - "null"
          format: uri
        allowed_connection_types:
          type: array
          description: |
            `coexistence` - Use with WhatsApp Business App (5 msg/sec)
            `dedicated` - API-only through Kapso (up to 1000 msg/sec)

            Provide one value to auto-select for customer
          items:
            type: string
            enum:
              - coexistence
              - dedicated
          default:
            - coexistence
            - dedicated
        meta_billing_mode:
          type: string
          description: |
            Policy selected when the setup link was created.

            `customer_managed` uses the WABA's Meta payment method. `partner_managed` requests Kapso billing for the entire WABA after Meta returns the connected account.
          enum:
            - customer_managed
            - partner_managed
          default: customer_managed
        theme_config:
          type:
            - object
            - "null"
          description: Hex colors for hosted onboarding page
          properties:
            primary_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Primary action buttons (default #22c55e)
            primary_foreground_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Text on primary buttons (default #ffffff)
            background_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Page background (default #ffffff)
            text_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Body text (default #0f172a)
            muted_text_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Secondary text (default #64748b)
            card_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Card backgrounds (defaults to background_color)
            muted_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Disabled elements (default #f1f5f9)
            border_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Borders and dividers (default #e2e8f0)
            secondary_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Secondary buttons (default #f1f5f9)
            secondary_foreground_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Text on secondary buttons (default #0f172a)
            destructive_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Error/danger buttons (default #ef4444)
            destructive_foreground_color:
              type: string
              pattern: ^#[0-9A-Fa-f]{6}$
              description: Text on destructive buttons (default #fef2f2)
        provision_phone_number:
          type:
            - boolean
            - "null"
          description: Auto-provision number during onboarding
        phone_number_area_code:
          type:
            - string
            - "null"
          description: US area code for provisioned numbers
        phone_number_country_isos:
          type: array
          description: Allowed countries for provisioning (defaults to ["US"])
          items:
            type: string
            pattern: ^[A-Z]{2}$
        reconnect_phone_number:
          type:
            - string
            - "null"
          description: |
            Normalized E.164 digits of the customer's existing WhatsApp number this link is targeting for reconnect.

            Set this on create to scope the setup flow to a specific existing WhatsApp config for the same customer (token refresh / re-auth). When present, `provision_phone_number` is forced to `false` and `allowed_connection_types` is locked to match the existing config (`["dedicated"]` or `["coexistence"]`).
        language:
          type:
            - string
            - "null"
          description: ISO 639-1 language code for setup link UI (en, es, pt, hi, id, ar).
            Defaults to user's browser language.
          enum:
            - en
            - es
            - pt
            - hi
            - id
            - ar
            - null
        whatsapp_setup_status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
        whatsapp_setup_error:
          type:
            - string
            - "null"
        provisioned_phone_number:
          allOf:
            - $ref: "#/components/schemas/PhoneNumberReference"

    SetupLinkCreateRequest:
      type: object
      required:
        - setup_link
      properties:
        setup_link:
          type: object
          properties:
            success_redirect_url:
              type:
                - string
                - "null"
              format: uri
            failure_redirect_url:
              type:
                - string
                - "null"
              format: uri
            provision_phone_number:
              type: boolean
              description: Requires your own telephony credentials on the project for non-US numbers
            phone_number_area_code:
              type:
                - string
                - "null"
              description: US only
            allowed_connection_types:
              type: array
              description: Single value auto-selects for customer
              items:
                type: string
                enum:
                  - coexistence
                  - dedicated
            meta_billing_mode:
              type: string
              description: |
                Meta billing policy for the WABA. This value cannot be changed after the link is created.

                `partner_managed` requires Kapso-managed Meta credentials. Final WABA eligibility is checked after connection; setup can complete even when billing attachment needs attention.
              enum:
                - customer_managed
                - partner_managed
              default: customer_managed
            theme_config:
              type:
                - object
                - "null"
              properties:
                primary_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                primary_foreground_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                background_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                text_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                muted_text_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                card_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                muted_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                border_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                secondary_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                secondary_foreground_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                destructive_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                destructive_foreground_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
            phone_number_country_isos:
              type: array
              items:
                type: string
                pattern: ^[A-Z]{2}$
            reconnect_phone_number:
              type:
                - string
                - "null"
              description: |
                Existing WhatsApp number for this customer to reconnect. Phone numbers are normalized to E.164 digits.

                When set:
                - The number must match an existing production WhatsApp config on the customer
                - `provision_phone_number` must be `false` (or omitted)
                - `allowed_connection_types` must match the existing config (`["dedicated"]` or `["coexistence"]`)
            language:
              type:
                - string
                - "null"
              description: ISO 639-1 language code for setup link UI
              enum:
                - en
                - es
                - pt
                - hi
                - id
                - ar
                - null

    SetupLinkUpdateRequest:
      type: object
      required:
        - setup_link
      properties:
        setup_link:
          type: object
          properties:
            success_redirect_url:
              type:
                - string
                - "null"
              format: uri
            failure_redirect_url:
              type:
                - string
                - "null"
              format: uri
            provision_phone_number:
              type: boolean
            phone_number_area_code:
              type:
                - string
                - "null"
            expires_at:
              type:
                - string
                - "null"
              format: date-time
            status:
              type: string
              enum:
                - active
                - used
                - expired
                - revoked
              description: Cannot change to `used`
            allowed_connection_types:
              type: array
              items:
                type: string
                enum:
                  - coexistence
                  - dedicated
            theme_config:
              type:
                - object
                - "null"
              properties:
                primary_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                primary_foreground_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                background_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                text_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                muted_text_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                card_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                muted_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                border_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                secondary_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                secondary_foreground_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                destructive_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
                destructive_foreground_color:
                  type: string
                  pattern: ^#[0-9A-Fa-f]{6}$
            phone_number_country_isos:
              type: array
              items:
                type: string
                pattern: ^[A-Z]{2}$
            language:
              type:
                - string
                - "null"
              description: ISO 639-1 language code for setup link UI
              enum:
                - en
                - es
                - pt
                - hi
                - id
                - ar
                - null

    SetupLinkResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/SetupLink"

    SetupLinkListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/SetupLink"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappPhoneNumber:
      type: object
      required:
        - id
        - internal_id
        - phone_number_id
        - name
        - created_at
        - updated_at
      properties:
        id:
          type: string
          description: Meta phone number ID
        internal_id:
          type: string
          format: uuid
          description: Kapso configuration ID
        phone_number_id:
          type: string
          description: Meta phone number ID
        name:
          type: string
          description: Label for this number
        business_account_id:
          type:
            - string
            - "null"
          description: WhatsApp Business Account ID
        is_coexistence:
          type:
            - boolean
            - "null"
          description: Using with WhatsApp Business App
        inbound_processing_enabled:
          type: boolean
          description: Process incoming messages
        calls_enabled:
          type: boolean
          description: Handle voice calls
        webhook_verified_at:
          type:
            - string
            - "null"
          format: date-time
          description: When Meta confirmed webhook
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        customer_id:
          type:
            - string
            - "null"
          format: uuid
        display_name:
          type:
            - string
            - "null"
          description: Customer-facing display name
        display_phone_number:
          type:
            - string
            - "null"
          description: Formatted number from Meta
        display_phone_number_normalized:
          type:
            - string
            - "null"
          description: Digits only
        verified_name:
          type:
            - string
            - "null"
          description: Meta-verified business name
        quality_rating:
          type:
            - string
            - "null"
          description: GREEN, YELLOW, or RED
        code_verification_status:
          type:
            - string
            - "null"
        name_status:
          type:
            - string
            - "null"
          description: Display name review status
        status:
          type:
            - string
            - "null"
          description: Connection status (e.g., CONNECTED)
        throughput_tier:
          type:
            - string
            - "null"
          description: Message rate limit tier
        whatsapp_business_manager_messaging_limit:
          anyOf:
            - type: integer
            - type: string
            - type: "null"
        is_official_business_account:
          type:
            - boolean
            - "null"
          description: Green checkmark verified
        is_pin_enabled:
          type:
            - boolean
            - "null"
          description: Two-factor PIN enabled

    WhatsappPhoneNumberCreateRequest:
      type: object
      required:
        - whatsapp_phone_number
      properties:
        whatsapp_phone_number:
          type: object
          required:
            - name
            - phone_number_id
            - business_account_id
            - access_token
          properties:
            name:
              type: string
            kind:
              type: string
              enum:
                - production
                - sandbox
              default: production
              description: Production uses Meta credentials, sandbox uses test data
            phone_number_id:
              type: string
              description: From Meta
            business_account_id:
              type: string
              description: From Meta
            access_token:
              type: string
              description: Permanent token from Meta
            webhook_verify_token:
              type:
                - string
                - "null"
            webhook_destination_url:
              type:
                - string
                - "null"
              format: uri
              description: Your HTTPS endpoint for webhooks
            inbound_processing_enabled:
              type: boolean
              default: true
            calls_enabled:
              type: boolean
              default: false

    WhatsappPhoneNumberUpdateRequest:
      type: object
      required:
        - whatsapp_phone_number
      properties:
        whatsapp_phone_number:
          type: object
          properties:
            name:
              type: string
            kind:
              type: string
              enum:
                - production
                - sandbox
            phone_number_id:
              type: string
              description: Required when switching to production
            business_account_id:
              type: string
            access_token:
              type: string
            webhook_verify_token:
              type:
                - string
                - "null"
            webhook_destination_url:
              type:
                - string
                - "null"
              format: uri
            inbound_processing_enabled:
              type: boolean
            calls_enabled:
              type: boolean

    WhatsappPhoneNumberResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappPhoneNumber"

    WhatsappPhoneNumberListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappPhoneNumber"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappPhoneNumberHealthResponse:
      type: object
      required:
        - status
        - timestamp
      properties:
        status:
          type: string
          enum:
            - healthy
            - degraded
            - unhealthy
            - error
        timestamp:
          type: string
          format: date-time
        error:
          type:
            - string
            - "null"
        checks:
          type:
            - object
            - "null"
          additionalProperties:
            type: object
            properties:
              passed:
                type: boolean
              details:
                type: object
                additionalProperties: true
              error:
                type: string
          description: |
            Individual check results. Typical checks:
            - phone_number_access: Meta API connectivity
            - messaging_health: Send/receive capability (AVAILABLE/LIMITED/BLOCKED)
            - webhook_subscription: WABA app subscription status
            - webhook_verified: Webhook verification status
            - token_validity: Access token validity (coexistence only)

    WhatsappWebhook:
      type: object
      required:
        - id
        - url
        - kind
        - events
        - active
        - created_at
        - updated_at
        - project_id
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
          description: Webhook delivery endpoint
        kind:
          type: string
          enum: [ kapso, meta ]
          default: kapso
          description: Webhook type - 'kapso' for event-based webhooks, 'meta' for raw
            Meta payload forwarding
        events:
          type: array
          description: Event subscriptions (required for 'kapso' webhooks, empty for 'meta')
          items:
            type: string
        active:
          type: boolean
          description: Pause deliveries without deleting
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        project_id:
          type: string
          format: uuid
        phone_number_id:
          type:
            - string
            - "null"
          description: Meta phone number ID
        secret_key:
          type:
            - string
            - "null"
          description: Secret for signing requests
        headers:
          type:
            - object
            - "null"
          additionalProperties: true
          description: Custom request headers
        buffer_enabled:
          type:
            - boolean
            - "null"
          description: Batch messages before delivery
        buffer_window_seconds:
          type:
            - integer
            - "null"
          description: Seconds to wait before sending batch
        max_buffer_size:
          type:
            - integer
            - "null"
          description: Max messages per batch
        buffer_events:
          type: array
          description: Events to buffer
          items:
            type: string
        inactivity_minutes:
          type:
            - integer
            - "null"
          description: Trigger inactivity event after N minutes
        payload_version:
          type:
            - string
            - "null"
          description: Webhook payload format version (defaults to 'v2')

    WhatsappWebhookRequest:
      type: object
      required:
        - whatsapp_webhook
      properties:
        whatsapp_webhook:
          type: object
          required:
            - url
          properties:
            url:
              type: string
              format: uri
              description: Webhook delivery endpoint
            kind:
              type: string
              enum: [ kapso, meta ]
              default: kapso
              description: Webhook type - 'kapso' for event-based webhooks, 'meta' for raw
                Meta payload forwarding
            secret_key:
              type:
                - string
                - "null"
              description: Secret for request verification
            active:
              type: boolean
              description: Enable deliveries
            buffer_enabled:
              type: boolean
              description: Batch messages before delivery (not supported for 'meta' webhooks)
            buffer_window_seconds:
              type:
                - integer
                - "null"
              description: Seconds to wait (1-60, default 5)
            max_buffer_size:
              type:
                - integer
                - "null"
              description: Max messages per batch (1-100)
            inactivity_minutes:
              type:
                - integer
                - "null"
              description: Minutes before inactivity event
            events:
              type: array
              description: Event subscriptions (e.g., whatsapp.message.received) - required
                for 'kapso' webhooks, empty for 'meta'
              items:
                type: string
            buffer_events:
              type: array
              description: Events to buffer (not supported for 'meta' webhooks)
              items:
                type: string
            headers:
              type:
                - object
                - "null"
              additionalProperties: true
              description: Custom request headers
            payload_version:
              type:
                - string
                - "null"
              description: Webhook payload format version (defaults to 'v2', not applicable
                for 'meta' webhooks)

    WhatsappWebhookUpdateRequest:
      type: object
      required:
        - whatsapp_webhook
      properties:
        whatsapp_webhook:
          type: object
          properties:
            url:
              type: string
              format: uri
              description: Webhook delivery endpoint
            kind:
              type: string
              enum: [ kapso, meta ]
              description: Webhook type (cannot be changed after creation)
            secret_key:
              type:
                - string
                - "null"
              description: Secret for request verification
            active:
              type: boolean
              description: Enable deliveries
            buffer_enabled:
              type: boolean
              description: Batch messages before delivery (not supported for 'meta' webhooks)
            buffer_window_seconds:
              type:
                - integer
                - "null"
              description: Seconds to wait (1-60, default 5)
            max_buffer_size:
              type:
                - integer
                - "null"
              description: Max messages per batch (1-100)
            inactivity_minutes:
              type:
                - integer
                - "null"
              description: Minutes before inactivity event
            events:
              type: array
              description: Event subscriptions (e.g., whatsapp.message.received) - required
                for 'kapso' webhooks, empty for 'meta'
              items:
                type: string
            buffer_events:
              type: array
              description: Events to buffer (not supported for 'meta' webhooks)
              items:
                type: string
            headers:
              type:
                - object
                - "null"
              additionalProperties: true
              description: Custom request headers
            payload_version:
              type:
                - string
                - "null"
              description: Webhook payload format version (defaults to 'v2')

    WhatsappProjectWebhookRequest:
      type: object
      required:
        - whatsapp_webhook
      properties:
        whatsapp_webhook:
          type: object
          required:
            - url
          properties:
            url:
              type: string
              format: uri
              description: Webhook delivery endpoint
            phone_number_id:
              type: string
              description: Optional Meta phone number ID. Omit for project-scoped webhooks
                (project events only). Required for message/conversation events
                and all meta webhooks.
            kind:
              type: string
              enum: [ kapso, meta ]
              default: kapso
              description: |
                Webhook type:
                - **kapso**: Event-based webhooks with filtered events, buffering support, and Kapso payload format (default)
                - **meta**: Raw Meta webhook forwarding - receives the exact payload Meta sends. Requires phone_number_id.
            secret_key:
              type:
                - string
                - "null"
              description: Secret for request verification
            active:
              type: boolean
              description: Enable deliveries
            buffer_enabled:
              type: boolean
              description: Batch messages before delivery (not supported for 'meta' webhooks)
            buffer_window_seconds:
              type:
                - integer
                - "null"
              description: Seconds to wait (1-60, default 5)
            max_buffer_size:
              type:
                - integer
                - "null"
              description: Max messages per batch (1-100)
            inactivity_minutes:
              type:
                - integer
                - "null"
              description: Minutes before inactivity event
            events:
              type: array
              description: Event subscriptions (project events for project-scoped webhooks,
                message/conversation events for number-scoped webhooks)
              items:
                type: string
            buffer_events:
              type: array
              description: Events to buffer (not supported for 'meta' webhooks)
              items:
                type: string
            headers:
              type:
                - object
                - "null"
              additionalProperties: true
              description: Custom request headers
            payload_version:
              type:
                - string
                - "null"
              description: Webhook payload format version (defaults to 'v2')

    WhatsappWebhookResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappWebhook"

    WhatsappWebhookListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappWebhook"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappBroadcastTemplate:
      type: object
      required:
        - id
        - name
        - language_code
        - category
        - status
      properties:
        id:
          type: string
          description: Meta template ID when available, otherwise internal UUID for legacy
            support
        meta_template_id:
          type:
            - string
            - "null"
          description: Meta's external template identifier
        name:
          type: string
        language_code:
          type: string
        category:
          type: string
        status:
          type: string
        components:
          type: array
          description: Template component definitions (BODY, HEADER, BUTTON, etc.)
          items:
            type: object
            additionalProperties: true

    WhatsappBroadcast:
      type: object
      required:
        - id
        - name
        - status
        - created_at
        - updated_at
        - total_recipients
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          description: Campaign label
        status:
          type: string
          enum:
            - draft
            - scheduled
            - sending
            - stopped
            - completed
            - failed
        scheduled_at:
          type:
            - string
            - "null"
          format: date-time
          description: When broadcast is scheduled to send
        started_at:
          type:
            - string
            - "null"
          format: date-time
          description: When sending started
        stopped_at:
          type:
            - string
            - "null"
          format: date-time
          description: When sending was stopped
        completed_at:
          type:
            - string
            - "null"
          format: date-time
          description: When completed
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        sent_count:
          type: integer
          minimum: 0
          description: Messages sent
        failed_count:
          type: integer
          minimum: 0
          description: Failures
        delivered_count:
          type: integer
          minimum: 0
          description: Delivered
        read_count:
          type: integer
          minimum: 0
          description: Read
        pending_count:
          type: integer
          minimum: 0
          description: Pending
        suppressed_count:
          type: integer
          minimum: 0
          description: Recipients skipped because they stopped marketing messages
            on this number. Not sent, not charged, and excluded from sent_count
            and failed_count.
        responded_count:
          type: integer
          minimum: 0
          description: Responses
        response_rate:
          type: number
          format: float
          description: Response rate %
        total_recipients:
          type: integer
          minimum: 0
          description: Total recipients
        phone_number_id:
          type:
            - string
            - "null"
          description: Phone number ID
        whatsapp_template:
          anyOf:
            - $ref: "#/components/schemas/WhatsappBroadcastTemplate"
            - type: "null"

    WhatsappBroadcastCreateRequest:
      type: object
      required:
        - whatsapp_broadcast
      properties:
        whatsapp_broadcast:
          type: object
          required:
            - name
            - phone_number_id
            - whatsapp_template_id
          properties:
            name:
              type: string
              description: Campaign name
            phone_number_id:
              type: string
              description: Phone number ID to send from
            whatsapp_template_id:
              type: string
              description: Meta template ID (preferred) or internal UUID (legacy compatibility
                only). Migrate to Meta IDs for future compatibility.

    WhatsappBroadcastResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappBroadcast"

    WhatsappBroadcastUpdateRequest:
      type: object
      required:
        - whatsapp_broadcast
      properties:
        whatsapp_broadcast:
          type: object
          required:
            - status
          properties:
            status:
              type: string
              enum:
                - draft
                - stopped
              description: Set to `stopped` to stop a sending broadcast, or `draft` to cancel a scheduled broadcast.

    WhatsappBroadcastListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappBroadcast"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappBroadcastRecipient:
      type: object
      required:
        - id
        - phone_number
        - status
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        phone_number:
          type: string
          description: E.164 format, no plus
        status:
          type: string
          enum:
            - pending
            - sent
            - failed
            - suppressed
          description: Delivery status. `suppressed` means the contact stopped
            marketing messages on this number — nothing was sent and nothing
            was charged.
        sent_at:
          type:
            - string
            - "null"
          format: date-time
        failed_at:
          type:
            - string
            - "null"
          format: date-time
        error_message:
          type:
            - string
            - "null"
        delivered_at:
          type:
            - string
            - "null"
          format: date-time
        read_at:
          type:
            - string
            - "null"
          format: date-time
        responded_at:
          type:
            - string
            - "null"
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        template_components:
          type: array
          description: Meta-style template components
          items:
            type: object
            additionalProperties: true
        error_details:
          type:
            - object
            - "null"
          additionalProperties: true
          description: >-
            Meta error payload. For `suppressed` recipients it holds
            `code: marketing_preference_stopped` and a `detail` string instead.

    WhatsappBroadcastRecipientListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappBroadcastRecipient"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappBroadcastRecipientsAddRequest:
      type: object
      required:
        - whatsapp_broadcast
      properties:
        whatsapp_broadcast:
          type: object
          required:
            - recipients
          properties:
            recipients:
              type: array
              minItems: 1
              items:
                type: object
                properties:
                  phone_number:
                    type: string
                    description: E.164 phone (required unless whatsapp_contact_id provided)
                  whatsapp_contact_id:
                    type: string
                    format: uuid
                    description: Existing contact ID
                  components:
                    type: array
                    description: Meta template components (body, header, button)
                    items:
                      type: object
                      required:
                        - type
                      properties:
                        type:
                          type: string
                          enum: [ header, body, button ]
                        sub_type:
                          type: string
                          description: Button sub_type (url, quick_reply)
                        index:
                          type: integer
                          description: Button index (0-based)
                        parameters:
                          type: array
                          items:
                            type: object
                            properties:
                              type:
                                type: string
                                description: Parameter type (text, image, video, document)
                              parameter_name:
                                type: string
                                description: Named parameter from template (e.g., first_name, discount_code)
                              text:
                                type: string
                                description: Text value for text parameters
                              image:
                                type: object
                                description: Image object for header images
                                additionalProperties: true
                              video:
                                type: object
                                description: Video object for header videos
                                additionalProperties: true
                              document:
                                type: object
                                description: Document object for header documents
                                additionalProperties: true
                            additionalProperties: true

    WhatsappBroadcastRecipientsAddResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - added
            - duplicates
            - errors
          properties:
            added:
              type: integer
              minimum: 0
            duplicates:
              type: integer
              minimum: 0
            errors:
              type: array
              items:
                type: string

    WhatsappBroadcastSendResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - id
            - status
          properties:
            id:
              type: string
              format: uuid
            status:
              type: string
            scheduled_at:
              type:
                - string
                - "null"
              format: date-time
            started_at:
              type:
                - string
                - "null"
              format: date-time
            stopped_at:
              type:
                - string
                - "null"
              format: date-time
            completed_at:
              type:
                - string
                - "null"
              format: date-time

    WhatsappConversation:
      type: object
      required:
        - id
        - status
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        phone_number:
          type:
            - string
            - "null"
          description: Contact's WhatsApp number. Can be null for BSUID-only identity.
        business_scoped_user_id:
          type:
            - string
            - "null"
          description: WhatsApp business-scoped user ID
        parent_business_scoped_user_id:
          type:
            - string
            - "null"
          description: Parent business-scoped user ID when provided by Meta
        username:
          type:
            - string
            - "null"
          description: WhatsApp username when available
        status:
          type: string
          enum:
            - active
            - ended
        last_active_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        metadata:
          type: object
          default: {}
          additionalProperties: true
        phone_number_id:
          type:
            - string
            - "null"
        kapso:
          type:
            - object
            - "null"
          description: Conversation metadata
          properties:
            contact_name:
              type:
                - string
                - "null"
            messages_count:
              type: integer
              minimum: 0
            last_message_id:
              type:
                - string
                - "null"
            last_message_type:
              type:
                - string
                - "null"
            last_message_timestamp:
              type: string
              format: date-time
            last_message_text:
              type:
                - string
                - "null"
            last_inbound_at:
              type: string
              format: date-time
            last_outbound_at:
              type: string
              format: date-time

    WhatsappConversationResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappConversation"

    WhatsappConversationUpdateRequest:
      type: object
      required:
        - whatsapp_conversation
      properties:
        whatsapp_conversation:
          type: object
          required:
            - status
          properties:
            status:
              type: string
              enum:
                - active
                - ended

    ConversationAssignment:
      type: object
      required:
        - id
        - user_id
        - active
        - created_at
      properties:
        id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
          description: ID of the user assigned to the conversation
        created_by_user_id:
          type:
            - string
            - "null"
          format: uuid
          description: ID of the user who created the assignment
        notes:
          type:
            - string
            - "null"
          description: Optional notes about the assignment
        active:
          type: boolean
          description: Whether this assignment is currently active
        created_at:
          type: string
          format: date-time

    ConversationAssignmentResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/ConversationAssignment"

    ConversationAssignmentListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ConversationAssignment"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    ConversationAssignmentCreateRequest:
      type: object
      required:
        - assignment
      properties:
        assignment:
          type: object
          required:
            - user_id
          properties:
            user_id:
              type: string
              format: uuid
              description: ID of the user to assign
            notes:
              type: string
              description: Optional notes about the assignment

    ConversationAssignmentUpdateRequest:
      type: object
      required:
        - assignment
      properties:
        assignment:
          type: object
          properties:
            user_id:
              type: string
              format: uuid
              description: ID of the user to reassign to
            notes:
              type: string
              description: Updated notes
            active:
              type: boolean
              description: Set to false to unassign

    WhatsappConversationListResponse:
      type: object
      required:
        - data
        - paging
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappConversation"
        paging:
          $ref: "#/components/schemas/Paging"

    WhatsappMessage:
      type: object
      description: |
        WhatsApp message in Meta-compatible format with Kapso extensions.
        The structure mirrors Meta's webhook payload format.
      required:
        - id
        - timestamp
        - type
        - kapso
      properties:
        id:
          type: string
          description: WhatsApp message ID (WAMID)
          example: wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQzQUY5
        timestamp:
          type: string
          description: Unix timestamp in seconds (as string per Meta convention)
          example: "1705329600"
        type:
          type: string
          description: Message type
          enum:
            - text
            - image
            - video
            - audio
            - document
            - location
            - interactive
            - template
            - reaction
            - contacts
            - sticker
            - button
            - order
        from:
          type:
            - string
            - "null"
          description: Sender phone number when phone-based identity is available
          example: "15551234567"
        from_user_id:
          type:
            - string
            - "null"
          description: Sender business-scoped user ID when available
          example: "US.13491208655302741918"
        from_parent_user_id:
          type:
            - string
            - "null"
          description: Sender parent business-scoped user ID when available
          example: "US.ENT.506847293015824"
        to:
          type:
            - string
            - "null"
          description: Recipient phone number when phone-based identity is available
          example: "15551234567"
        to_user_id:
          type:
            - string
            - "null"
          description: Recipient business-scoped user ID when available
          example: "US.13491208655302741918"
        to_parent_user_id:
          type:
            - string
            - "null"
          description: Recipient parent business-scoped user ID when available
          example: "US.ENT.506847293015824"
        username:
          type:
            - string
            - "null"
          description: WhatsApp username when available
          example: "@testusername"
        context:
          type: object
          description: Reply context when message is a reply
          properties:
            id:
              type: string
              description: ID of the message being replied to
            from:
              type: string
              description: Phone number of original message sender
            referred_product:
              type: object
              description: Product being referenced
              additionalProperties: true
        text:
          type: object
          description: Text message payload (when type=text)
          properties:
            body:
              type: string
        image:
          type: object
          description: Image message payload (when type=image)
          properties:
            id:
              type: string
              description: Media ID from Meta
            link:
              type: string
              format: uri
            caption:
              type: string
        video:
          type: object
          description: Video message payload (when type=video)
          properties:
            id:
              type: string
            link:
              type: string
              format: uri
            caption:
              type: string
        audio:
          type: object
          description: Audio message payload (when type=audio)
          properties:
            id:
              type: string
            link:
              type: string
              format: uri
        document:
          type: object
          description: Document message payload (when type=document)
          properties:
            id:
              type: string
            link:
              type: string
              format: uri
            filename:
              type: string
            caption:
              type: string
        location:
          type: object
          description: Location message payload (when type=location)
          properties:
            latitude:
              type: number
            longitude:
              type: number
            name:
              type: string
            address:
              type: string
        sticker:
          type: object
          description: Sticker message payload (when type=sticker)
          properties:
            id:
              type: string
            link:
              type: string
              format: uri
            mime_type:
              type: string
            animated:
              type: boolean
        interactive:
          type: object
          description: Interactive message payload (when type=interactive)
          additionalProperties: true
        button:
          type: object
          description: Button response payload (when type=button)
          properties:
            text:
              type: string
            payload:
              type: string
        template:
          type: object
          description: Template message payload (when type=template)
          properties:
            name:
              type: string
            language:
              type: object
              properties:
                code:
                  type: string
            components:
              type: array
              items:
                type: object
                additionalProperties: true
        reaction:
          type: object
          description: Reaction message payload (when type=reaction)
          properties:
            emoji:
              type: string
            message_id:
              type: string
        contacts:
          type: array
          description: Contacts message payload (when type=contacts)
          items:
            type: object
            additionalProperties: true
        order:
          type: object
          description: Order message payload (when type=order)
          properties:
            catalog_id:
              type: string
            product_items:
              type: array
              items:
                type: object
                additionalProperties: true
            order_text:
              type: string
        kapso:
          type: object
          description: Kapso-specific extensions
          required:
            - direction
            - status
            - whatsapp_conversation_id
          properties:
            direction:
              type: string
              enum:
                - inbound
                - outbound
            status:
              type: string
              enum:
                - pending
                - sent
                - delivered
                - read
                - failed
            processing_status:
              type: string
              enum:
                - pending
                - processed
                - blocked
            origin:
              type: string
              description: Message origin
              enum:
                - cloud_api
                - business_app
                - history_sync
            phone_number:
              type:
                - string
                - "null"
              description: Contact phone number
            phone_number_id:
              type:
                - string
                - "null"
              description: Meta phone number ID for the WhatsApp number
            has_media:
              type: boolean
            whatsapp_conversation_id:
              type: string
              format: uuid
              description: Kapso conversation ID
            contact_name:
              type:
                - string
                - "null"
              description: Contact name if available
            content:
              type:
                - string
                - "null"
              description: Text content of the message
            statuses:
              type: array
              description: Status event history (raw Meta payloads)
              items:
                type: object
                additionalProperties: true
            transcript:
              type: object
              description: Audio transcript (for audio messages)
              properties:
                text:
                  type: string
            media_data:
              type: object
              description: Media attachment details
              properties:
                url:
                  type: string
                  format: uri
                filename:
                  type: string
                content_type:
                  type: string
                byte_size:
                  type: integer
            media_url:
              type: string
              format: uri
              description: Direct media URL
            message_type_data:
              type: object
              description: Type-specific data (caption, template params, etc.)
              additionalProperties: true
            flow_response:
              type: object
              description: WhatsApp Flow response data
              additionalProperties: true
            flow_token:
              type: string
            flow_name:
              type: string
            order_text:
              type: string
              description: Order text for order messages

    WhatsappMessageResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappMessage"

    WhatsappMessageListResponse:
      type: object
      required:
        - data
        - paging
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappMessage"
        paging:
          $ref: "#/components/schemas/Paging"

    MessageStatusEvent:
      type: object
      description: Raw Meta status event payload
      properties:
        id:
          type: string
          description: Message ID
        status:
          type: string
          enum:
            - sent
            - delivered
            - read
            - failed
        timestamp:
          type: string
          description: Unix timestamp
        recipient_id:
          type: string
        conversation:
          type: object
          properties:
            id:
              type: string
            origin:
              type: object
              properties:
                type:
                  type: string
            expiration_timestamp:
              type: string
        pricing:
          type: object
          properties:
            billable:
              type: boolean
            pricing_model:
              type: string
            category:
              type: string
        errors:
          type: array
          items:
            type: object
            additionalProperties: true

    WhatsappDisplayNameRequest:
      type: object
      required:
        - id
        - requested_display_name
        - status
        - submitted_at
      properties:
        id:
          type: string
          format: uuid
        phone_number_id:
          type:
            - string
            - "null"
          description: Meta phone number ID
        requested_display_name:
          type: string
        previous_display_name:
          type:
            - string
            - "null"
          description: Previous display name
        status:
          type: string
          enum:
            - submitted
            - pending_review
            - approved
            - available_without_review
            - deferred
            - declined
            - expired
            - no_review
            - failed
            - applied
            - cancelled
        submitted_at:
          type: string
          format: date-time
        reviewed_at:
          type:
            - string
            - "null"
          format: date-time
        applied_at:
          type:
            - string
            - "null"
          format: date-time
        meta_error_code:
          type:
            - integer
            - "null"
        meta_error_subcode:
          type:
            - integer
            - "null"
        meta_error_type:
          type:
            - string
            - "null"
        meta_error_message:
          type:
            - string
            - "null"

    WhatsappDisplayNameRequestCreate:
      type: object
      required:
        - display_name_request
      properties:
        display_name_request:
          type: object
          required:
            - new_display_name
          properties:
            new_display_name:
              type: string
              description: Display name to request (max 256 chars)

    WhatsappDisplayNameRequestResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappDisplayNameRequest"

    WhatsappDisplayNameRequestListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappDisplayNameRequest"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    MediaIngest:
      type: object
      required:
        - phone_number_id
        - source
      properties:
        phone_number_id:
          type: string
          description: Meta phone number ID
        source:
          type: string
          format: uri
          description: HTTP/HTTPS URL to media file
        delivery:
          type: string
          enum:
            - meta_media
            - meta_resumable_asset
          default: meta_media
          description: |
            Upload method:
            - meta_media: Standard upload (30-day lifetime)
            - meta_resumable_asset: Resumable upload (profile pictures, etc.)
        filename:
          type: string
          description: Optional filename hint
        mime_type:
          type: string
          description: Optional MIME type hint

    MediaIngestRequest:
      type: object
      required:
        - media_ingest
      properties:
        media_ingest:
          $ref: "#/components/schemas/MediaIngest"

    MediaIngestTarget:
      type: object
      required:
        - kind
      properties:
        kind:
          type: string
          enum:
            - meta_media
            - meta_resumable_asset
          description: Upload method used
        media_id:
          type: string
          description: Meta media ID (present when kind=meta_media)
        handle:
          type: string
          description: Resumable upload handle (present when kind=meta_resumable_asset)

    MediaIngestResource:
      type: object
      required:
        - filename
        - mime_type
        - size_bytes
        - sha256
        - source_url
      properties:
        filename:
          type: string
          description: Detected or provided filename
        mime_type:
          type: string
          description: Detected or provided MIME type
        size_bytes:
          type: integer
          minimum: 0
          description: File size in bytes
        sha256:
          type: string
          description: SHA256 checksum
        source_url:
          type: string
          format: uri
          description: Original source URL

    MediaIngestData:
      type: object
      required:
        - ingest_id
        - target
        - resource
      properties:
        ingest_id:
          type: string
          format: uuid
          description: Unique ingest request ID
        target:
          $ref: "#/components/schemas/MediaIngestTarget"
        resource:
          $ref: "#/components/schemas/MediaIngestResource"

    MediaIngestResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/MediaIngestData"

    User:
      type: object
      required:
        - id
        - user_id
        - email
        - name
        - role
      properties:
        id:
          type: integer
          description: User project membership ID
        user_id:
          type: string
          format: uuid
          description: User account ID
        email:
          type: string
          format: email
          description: User email address
        name:
          type: string
          description: User display name
        role:
          type: string
          enum:
            - owner
            - member
          description: User role in the project

    UsersResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/User"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    ProjectEvent:
      type: object
      required:
        - id
        - name
        - occurred_at
        - conversation_id
        - properties
      properties:
        id:
          type: string
          format: uuid
          description: Unique event ID
        name:
          type: string
          minLength: 1
          maxLength: 128
          pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$'
          description: Event name. Must be lowercase and may contain optional dot-separated segments. Maximum 128 characters.
          example: conversation.csat_scored
        occurred_at:
          type: string
          format: date-time
          description: When the event occurred
        conversation_id:
          type:
            - string
            - "null"
          format: uuid
          description: Linked WhatsApp conversation ID, when provided
        properties:
          type: object
          additionalProperties:
            oneOf:
              - type: string
              - type: number
              - type: boolean
              - type: "null"
          description: Flat event properties object

    ProjectEventCreateRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 128
          pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$'
          description: Event name. Must be lowercase and may contain optional dot-separated segments. Each segment starts with a lowercase letter and may contain lowercase letters, numbers, and underscores. Maximum 128 characters.
          example: conversation.csat_scored
        occurred_at:
          type: string
          format: date-time
          description: Optional event timestamp. Defaults to the current time.
        conversation_id:
          type: string
          format: uuid
          description: Optional WhatsApp conversation ID to link to the event
        properties:
          type: object
          additionalProperties:
            oneOf:
              - type: string
              - type: number
              - type: boolean
              - type: "null"
          description: Optional flat event properties object

    ProjectEventResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/ProjectEvent"

    ProjectEventListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProjectEvent"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
        paging:
          $ref: "#/components/schemas/Paging"

    ProjectEventDefinition:
      type: object
      required:
        - id
        - name
        - property_schema
        - created_at
        - updated_at
        - archived_at
      properties:
        id:
          type: string
          format: uuid
          description: Unique event definition ID
        name:
          type: string
          minLength: 1
          maxLength: 128
          pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$'
          description: Event name. Must be lowercase and may contain optional dot-separated segments. Each segment starts with a lowercase letter and may contain lowercase letters, numbers, and underscores. Maximum 128 characters.
          example: conversation.csat_scored
        description:
          type:
            - string
            - "null"
          description: Human-readable description of what this event means
        property_schema:
          type: object
          additionalProperties:
            type: object
            required:
              - type
            properties:
              type:
                type: string
                enum:
                  - string
                  - number
                  - boolean
          description: Declared properties for this event. Values are keyed by property name.
          example:
            score:
              type: number
            reason:
              type: string
        last_seen_at:
          type:
            - string
            - "null"
          format: date-time
          description: Timestamp of the latest emitted event with this definition, when available.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        archived_at:
          type:
            - string
            - "null"
          format: date-time
          description: Timestamp when the definition was archived, or null when active.

    ProjectEventDefinitionCreateRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 128
          pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$'
          description: Event name. Must be lowercase and may contain optional dot-separated segments. Each segment starts with a lowercase letter and may contain lowercase letters, numbers, and underscores. Maximum 128 characters.
          example: conversation.csat_scored
        description:
          type:
            - string
            - "null"
          description: Human-readable description of what this event means
        property_schema:
          type: object
          additionalProperties:
            type: object
            required:
              - type
            properties:
              type:
                type: string
                enum:
                  - string
                  - number
                  - boolean
          description: Declared properties for this event. Values are keyed by property name.
          example:
            score:
              type: number
            reason:
              type: string

    ProjectEventDefinitionUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 128
          pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$'
          description: Event name. Must be lowercase and may contain optional dot-separated segments. Each segment starts with a lowercase letter and may contain lowercase letters, numbers, and underscores. Maximum 128 characters. Cannot be changed after events have been recorded.
          example: conversation.csat_scored
        description:
          type:
            - string
            - "null"
          description: Human-readable description of what this event means
        property_schema:
          type: object
          additionalProperties:
            type: object
            required:
              - type
            properties:
              type:
                type: string
                enum:
                  - string
                  - number
                  - boolean
          description: Declared properties for this event. Values are keyed by property name.
        archived:
          type: boolean
          description: Archive or restore the definition. Archived definitions retain historical events but are hidden from new tools and workflow selectors.

    ProjectEventDefinitionResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/ProjectEventDefinition"

    ProjectEventDefinitionListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProjectEventDefinition"

    WebhookDelivery:
      type: object
      required:
        - id
        - event
        - status
        - attempt_count
        - created_at
      properties:
        id:
          type: string
          format: uuid
        event:
          type: string
          description: The webhook event type
        status:
          type: string
          enum:
            - pending
            - delivered
            - failed
        response_status:
          type:
            - integer
            - "null"
          description: HTTP response status code from the webhook endpoint
        delivered_at:
          type:
            - string
            - "null"
          format: date-time
        failed_at:
          type:
            - string
            - "null"
          format: date-time
        last_attempt_at:
          type:
            - string
            - "null"
          format: date-time
        attempt_count:
          type: integer
        created_at:
          type: string
          format: date-time
        webhook_id:
          type:
            - string
            - "null"
          format: uuid
        webhook_url:
          type:
            - string
            - "null"
        whatsapp_config_id:
          type:
            - string
            - "null"
          format: uuid
        phone_number_id:
          type:
            - string
            - "null"
          description: Meta phone number ID
        conversation_phone_number:
          type:
            - string
            - "null"
          description: Phone number of the conversation associated with the delivery

    WebhookDeliveryListResponse:
      type: object
      required:
        - data
        - paging
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WebhookDelivery"
        paging:
          $ref: "#/components/schemas/Paging"

    ExternalApiLog:
      type: object
      required:
        - id
        - endpoint
        - http_method
        - response_status
        - response_time_ms
        - created_at
      properties:
        id:
          type: string
          format: uuid
        endpoint:
          type: string
          description: The API endpoint that was called
        http_method:
          type: string
          description: HTTP method used (GET, POST, etc.)
        response_status:
          type: integer
          description: HTTP response status code
        response_time_ms:
          type: number
          description: Response time in milliseconds
        ip_address:
          type:
            - string
            - "null"
        error_message:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
        api_key_id:
          type:
            - string
            - "null"
          format: uuid
        api_key_name:
          type:
            - string
            - "null"
          description: Name of the API key used for the request

    ExternalApiLogListResponse:
      type: object
      required:
        - data
        - paging
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ExternalApiLog"
        paging:
          $ref: "#/components/schemas/Paging"

    LogSearchEvent:
      type: object
      additionalProperties: true
      required:
        - id
        - source
      properties:
        id:
          type: string
          description: Indexed log event ID
          example: evt_1
        source:
          type: string
          enum:
            - external_api_log
            - whatsapp_webhook_event
            - flow_event
            - webhook_delivery
          example: external_api_log
        timestamp:
          type:
            - string
            - "null"
          format: date-time
        occurred_at:
          type:
            - string
            - "null"
          format: date-time
        ingested_at:
          type:
            - string
            - "null"
          format: date-time
        level:
          type:
            - string
            - "null"
          example: info
        status:
          type:
            - string
            - "null"
          example: failed
        message:
          type:
            - string
            - "null"
          example: POST /v1/messages
        resource_type:
          type:
            - string
            - "null"
          example: external_api_log
        resource_id:
          type:
            - string
            - "null"
        http_method:
          type:
            - string
            - "null"
          example: POST
        endpoint:
          type:
            - string
            - "null"
          example: /v1/messages
        api_key_id:
          type:
            - string
            - "null"
        api_key_name:
          type:
            - string
            - "null"
          example: Production key
        response_status:
          type:
            - integer
            - "null"
          example: 422
        response_time_ms:
          type:
            - number
            - "null"
        event_type:
          type:
            - string
            - "null"
          example: agent_iteration_started
        field:
          type:
            - string
            - "null"
        event:
          type:
            - string
            - "null"
        message_type:
          type:
            - string
            - "null"
        phone_number_id:
          type:
            - string
            - "null"
        display_phone_number:
          type:
            - string
            - "null"
        flow_name:
          type:
            - string
            - "null"
          example: Support workflow
        flow_id:
          type:
            - string
            - "null"
        flow_execution_id:
          type:
            - string
            - "null"
        flow_event_id:
          type:
            - string
            - "null"
        step_identifier:
          type:
            - string
            - "null"
          example: agent_1
        step_type:
          type:
            - string
            - "null"
        webhook_id:
          type:
            - string
            - "null"
        webhook_url:
          type:
            - string
            - "null"
        error_class:
          type:
            - string
            - "null"
        error_message:
          type:
            - string
            - "null"
        attributes:
          type:
            - object
            - "null"
          additionalProperties: true
        payload:
          type:
            - object
            - "null"
          additionalProperties: true

    LogSearchWindow:
      type: object
      properties:
        since:
          type: string
          format: date-time
        until:
          type: string
          format: date-time

    LogSearchPagination:
      type: object
      properties:
        has_more:
          type: boolean
          example: false
        next_cursor:
          type:
            - string
            - "null"

    LogSearchResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - available
            - events
          properties:
            available:
              type: boolean
            source:
              type:
                - string
                - array
                - "null"
              items:
                type: string
            window:
              $ref: "#/components/schemas/LogSearchWindow"
            events:
              type: array
              items:
                $ref: "#/components/schemas/LogSearchEvent"
            pagination:
              $ref: "#/components/schemas/LogSearchPagination"
            error:
              type:
                - string
                - "null"
            code:
              type:
                - string
                - "null"

    LogSearchFilterValue:
      anyOf:
        - type: string
        - type: integer
        - type: number
        - type: boolean

    LogSearchFilterEntry:
      type: object
      required:
        - key
        - value
      properties:
        key:
          type: string
          example: flow_execution_id
        value:
          $ref: "#/components/schemas/LogSearchFilterValue"

    LogSearchRequest:
      type: object
      properties:
        query:
          type: string
        period:
          type: string
          enum:
            - 24h
            - 7d
            - context
          default: 24h
        source:
          type: string
          enum:
            - all
            - external_api_log
            - whatsapp_webhook_event
            - flow_event
            - webhook_delivery
          default: all
        problems_only:
          type: boolean
          default: false
        errors_only:
          type: boolean
          default: false
          deprecated: true
          description: Alias for problems_only.
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 50
        cursor:
          type: string
          description: Cursor returned as pagination.next_cursor from a previous response.
        around:
          type: string
          format: date-time
          description: Center a context search around this timestamp. Use with period=context.
        highlight_event_id:
          type: string
          description: Include this event in the page when available.
        highlight_resource_id:
          type: string
          description: Include an event for this resource in the page when available.
        filters:
          anyOf:
            - type: object
              additionalProperties:
                $ref: "#/components/schemas/LogSearchFilterValue"
            - type: array
              items:
                $ref: "#/components/schemas/LogSearchFilterEntry"
          example:
            flow_execution_id: flow-execution-1

    LogSearchCatalogResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - available
          properties:
            available:
              type: boolean
            catalog:
              type: object
              properties:
                sources:
                  type: array
                  items:
                    type: object
                    properties:
                      value:
                        type: string
                      label:
                        type: string
                      color:
                        type: string
                      aliases:
                        type: array
                        items:
                          type: string
                filter_suggestions:
                  type: array
                  items:
                    type: object
                    properties:
                      key:
                        type: string
                      label:
                        type: string
                      description:
                        type: string
                      param_key:
                        type: string
                      sources:
                        type: array
                        items:
                          type: string
                detail_fields:
                  type: array
                  items:
                    type: string

    ProviderModel:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          description: Model name
        provider:
          type:
            - string
            - "null"
          description: Provider name (e.g., OpenAI, Anthropic)
        reasoning_model:
          type: boolean
          description: Whether the model supports reasoning controls.
        thinking_mode:
          type: string
          description: How extended thinking is applied for this model. `disabled`
            means no thinking, `manual` uses a fixed thinking budget, `adaptive`
            lets the model size its own thinking from the reasoning effort.
          enum:
            - disabled
            - manual
            - adaptive
            - always_adaptive
        supports_custom_sampling:
          type: boolean
          description: Whether the model accepts custom sampling parameters such as
            temperature. Sampling parameters are ignored while extended thinking is
            active.
        supported_reasoning_efforts:
          type: array
          description: Reasoning efforts accepted by this model. Empty for non-reasoning
            models.
          items:
            type: string
            enum:
              - none
              - minimal
              - low
              - medium
              - high
              - xhigh
              - max
        default_reasoning_effort:
          type:
            - string
            - "null"
          description: Default reasoning effort to use when no effort is specified.
          enum:
            - none
            - minimal
            - low
            - medium
            - high
            - xhigh
            - max
            - null
        supported_prompt_cache_ttls:
          type: array
          description: Prompt cache TTLs accepted by this model. Empty for models
            without prompt caching.
          items:
            type: string
            enum:
              - 5m
              - 1h
        default_prompt_cache_ttl:
          type:
            - string
            - "null"
          description: Default prompt cache TTL. `null` for models without prompt
            caching.
          enum:
            - 5m
            - 1h
            - null
        api_surface:
          type: string
          description: API surface Kapso uses for the provider model.
          enum:
            - provider_default
            - chat_completions
            - responses

    ProviderModelListResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProviderModel"

    ConfigurePropRequest:
      type: object
      required:
        - prop_name
      properties:
        prop_name:
          type: string
          description: Name of the prop to configure
        configured_props:
          type: object
          additionalProperties: true
          description: Current prop values
        dynamic_props_id:
          type: string
          description: Dynamic props session ID

    ReloadPropsRequest:
      type: object
      properties:
        configured_props:
          type: object
          additionalProperties: true
          description: Current prop values
        dynamic_props_id:
          type: string
          description: Dynamic props session ID

    ConfigurePropResponse:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            additionalProperties: true

    DeleteSuccessResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - success
          properties:
            success:
              type: boolean

    # WhatsApp Flows schemas
    WhatsappFlow:
      type: object
      required:
        - id
        - meta_flow_id
        - name
        - status
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        meta_flow_id:
          type: string
          description: Meta's flow identifier
        name:
          type: string
        status:
          type: string
          enum:
            - draft
            - published
        json_version:
          type:
            - string
            - "null"
          description: WhatsApp Flow JSON version
        data_api_version:
          type:
            - string
            - "null"
          description: Data API version for the flow
        business_account_id:
          type:
            - string
            - "null"
          description: WhatsApp Business Account ID
        preview_url:
          type:
            - string
            - "null"
          format: uri
          description: Meta preview URL (draft flows only)
        published_at:
          type:
            - string
            - "null"
          format: date-time
        last_synced_at:
          type:
            - string
            - "null"
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        data_endpoint_function_id:
          type:
            - string
            - "null"
          format: uuid
          description: ID of the associated data endpoint function
        phone_number_id:
          type:
            - string
            - "null"
          description: Meta phone number ID associated with this flow
        data_endpoint_url:
          type:
            - string
            - "null"
          format: uri
          description: Data endpoint URL when configured
        has_data_endpoint:
          type: boolean
          description: Whether data endpoint is fully configured
        flows_encryption_configured:
          type: boolean
          description: Whether flows encryption is set up

    WhatsappFlowVersion:
      type: object
      required:
        - id
        - version_label
        - status
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        version_label:
          type: string
          description: Version identifier
        status:
          type: string
          enum:
            - draft
            - published
        flow_json_sha:
          type:
            - string
            - "null"
          description: SHA256 hash of flow JSON
        published_at:
          type:
            - string
            - "null"
          format: date-time
        validation_errors:
          type:
            - array
            - "null"
          items:
            type: object
            additionalProperties: true
          description: Meta validation errors
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    WhatsappFlowVersionDetail:
      allOf:
        - $ref: "#/components/schemas/WhatsappFlowVersion"
        - type: object
          properties:
            flow_json:
              type: object
              additionalProperties: true
              description: WhatsApp Flow JSON definition

    WhatsappFlowDataEndpoint:
      type: object
      required:
        - function_id
        - function_name
        - status
      properties:
        function_id:
          type: string
          format: uuid
        function_name:
          type: string
        endpoint_url:
          type:
            - string
            - "null"
          format: uri
        status:
          type: string
          enum:
            - draft
            - deployed
            - error
        last_deployed_at:
          type:
            - string
            - "null"
          format: date-time
        code:
          type: string
          description: Function source code

    WhatsappFlowCreateRequest:
      type: object
      required:
        - phone_number_id
      properties:
        phone_number_id:
          type: string
          description: Meta phone number ID to use
        name:
          type: string
          description: Flow name (auto-generated if omitted)
        flow_json:
          type: object
          additionalProperties: true
          description: WhatsApp Flow JSON (defaults to a basic welcome screen)
        publish:
          type: boolean
          default: false
          description: Publish immediately after creation

    WhatsappFlowVersionCreateRequest:
      type: object
      required:
        - flow_json
      properties:
        flow_json:
          type: object
          additionalProperties: true
          description: WhatsApp Flow JSON definition
        phone_number_id:
          type: string
          description: Override phone number (optional)

    WhatsappFlowDataEndpointCreateRequest:
      type: object
      required:
        - code
      properties:
        code:
          type: string
          description: Cloudflare Worker code for the data endpoint

    WhatsappFlowResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappFlow"

    WhatsappFlowListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappFlow"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappFlowVersionResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappFlowVersion"

    WhatsappFlowVersionDetailResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappFlowVersionDetail"

    WhatsappFlowVersionListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WhatsappFlowVersion"
        meta:
          $ref: "#/components/schemas/PaginationMeta"

    WhatsappFlowDataEndpointResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WhatsappFlowDataEndpoint"

    WhatsappFlowDataEndpointRegisterResponse:
      type: object
      required:
        - data
      properties:
        data:
          allOf:
            - $ref: "#/components/schemas/WhatsappFlowDataEndpoint"
            - type: object
              properties:
                flow_id:
                  type: string
                  format: uuid
                flow_data_endpoint_function_id:
                  type: string
                  format: uuid
                flow_has_encryption:
                  type: boolean

    WhatsappFlowFunctionLog:
      type: object
      properties:
        level:
          type: string
        message:
          type: string
        logged_at:
          type: string
          format: date-time
        stack:
          type:
            - string
            - "null"
        cf_ray:
          type:
            - string
            - "null"
        outcome:
          type:
            - string
            - "null"

    WhatsappFlowFunctionLogsResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - function_id
            - function_name
            - logs
          properties:
            function_id:
              type: string
              format: uuid
            function_name:
              type: string
            logs:
              type: array
              items:
                $ref: "#/components/schemas/WhatsappFlowFunctionLog"

    WhatsappFlowFunctionInvocation:
      type: object
      properties:
        id:
          type: string
          format: uuid
        status_code:
          type: integer
        duration_ms:
          type:
            - number
            - "null"
        request_body:
          type:
            - object
            - "null"
          additionalProperties: true
        response_body:
          type:
            - object
            - "null"
          additionalProperties: true
        error_message:
          type:
            - string
            - "null"
        cf_ray:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time

    WhatsappFlowFunctionInvocationsResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - function_id
            - function_name
            - invocations
          properties:
            function_id:
              type: string
              format: uuid
            function_name:
              type: string
            invocations:
              type: array
              items:
                $ref: "#/components/schemas/WhatsappFlowFunctionInvocation"

    WhatsappFlowEncryptionSetupResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - status
            - message
            - flows_encryption_configured
          properties:
            status:
              type: string
              enum:
                - success
                - already_configured
            message:
              type: string
            flows_encryption_configured:
              type: boolean

  responses:
    UnauthorizedError:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    NotFoundError:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    PaymentRequiredError:
      description: Feature requires a paid plan
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

paths:
  /customers:
    get:
      tags:
        - Customers
      summary: List customers
      description: Returns customers in your project, most recent first.
      operationId: listCustomers
      parameters:
        - name: name_contains
          in: query
          description: Filter by name substring
          schema:
            type: string
        - name: external_customer_id
          in: query
          description: Filter by your customer ID
          schema:
            type: string
        - name: created_after
          in: query
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          schema:
            type: string
            format: date-time
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              description: RFC 5988 pagination links
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerListResponse"
              examples:
                basic:
                  value:
                    data:
                      - id: 550e8400-e29b-41d4-a716-446655440000
                        name: Acme Corp
                        external_customer_id: cus_abc123
                        created_at: "2025-01-15T10:00:00Z"
                        updated_at: "2025-01-15T10:00:00Z"
                      - id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
                        name: TechStart Inc
                        external_customer_id: cus_xyz789
                        created_at: "2025-01-14T15:30:00Z"
                        updated_at: "2025-01-14T15:30:00Z"
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 2
        "401":
          $ref: "#/components/responses/UnauthorizedError"

    post:
      tags:
        - Customers
      summary: Create customer
      operationId: createCustomer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerCreateRequest"
            examples:
              basic:
                value:
                  customer:
                    name: Acme Corp
                    external_customer_id: cus_abc123
      responses:
        "201":
          description: Customer created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
              examples:
                created:
                  value:
                    data:
                      id: 550e8400-e29b-41d4-a716-446655440000
                      name: Acme Corp
                      external_customer_id: cus_abc123
                      created_at: "2025-01-15T10:00:00Z"
                      updated_at: "2025-01-15T10:00:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /customers/{customer_id}:
    parameters:
      - name: customer_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - Customers
      summary: Get customer
      operationId: getCustomer
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

    patch:
      tags:
        - Customers
      summary: Update customer
      operationId: updateCustomer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerUpdateRequest"
            examples:
              update_name:
                value:
                  customer:
                    name: Acme Corporation
      responses:
        "200":
          description: Customer updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

    delete:
      tags:
        - Customers
      summary: Delete customer
      operationId: deleteCustomer
      responses:
        "204":
          description: Customer deleted
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /customers/{customer_id}/setup_links:
    parameters:
      - name: customer_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - Setup Links
      summary: List setup links
      description: Get WhatsApp onboarding links for a customer, most recent first.
      operationId: listCustomerSetupLinks
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - active
              - used
              - expired
              - revoked
        - name: created_after
          in: query
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          schema:
            type: string
            format: date-time
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SetupLinkListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

    post:
      tags:
        - Setup Links
      summary: Create setup link
      description: |
        Generate a hosted onboarding page for customers to connect their WhatsApp number.

        The link guides them through Meta's embedded signup and optionally provisions a new number.
      operationId: createCustomerSetupLink
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SetupLinkCreateRequest"
            examples:
              minimal:
                summary: Basic link
                value:
                  setup_link: {}
              with_redirects:
                summary: Custom success/failure URLs
                value:
                  setup_link:
                    success_redirect_url: https://yourapp.com/whatsapp/success
                    failure_redirect_url: https://yourapp.com/whatsapp/failed
              coexistence_only:
                summary: Force coexistence mode
                value:
                  setup_link:
                    allowed_connection_types:
                      - coexistence
              with_kapso_billing:
                summary: Pay Meta fees with Kapso credits
                value:
                  setup_link:
                    meta_billing_mode: partner_managed
              with_phone:
                summary: Auto-provision number
                value:
                  setup_link:
                    provision_phone_number: true
                    phone_number_area_code: "415"
                    phone_number_country_isos:
                      - US
              branded:
                summary: Custom brand colors
                value:
                  setup_link:
                    theme_config:
                      primary_color: "#3b82f6"
                      primary_foreground_color: "#ffffff"
                      background_color: "#f9fafb"
                      text_color: "#111827"
                      border_color: "#d1d5db"
              reconnect:
                summary: Reconnect an existing customer number
                value:
                  setup_link:
                    reconnect_phone_number: "+14155551234"
      responses:
        "201":
          description: Setup link created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SetupLinkResponse"
              examples:
                created:
                  value:
                    data:
                      id: 7f8a9b1c-2d3e-4f5a-6b7c-8d9e0f1a2b3c
                      status: active
                      created_at: "2025-01-15T10:00:00Z"
                      expires_at: "2025-02-14T10:00:00Z"
                      url: https://app.kapso.ai/whatsapp/setup/aBcD123xyz456def789
                      success_redirect_url: https://yourapp.com/whatsapp/success
                      failure_redirect_url: https://yourapp.com/whatsapp/failed
                      allowed_connection_types:
                        - coexistence
                        - dedicated
                      meta_billing_mode: customer_managed
                      provision_phone_number: false
                      phone_number_area_code: null
                      phone_number_country_isos:
                        - US
                      reconnect_phone_number: null
                      theme_config:
                        primary_color: "#3b82f6"
                        primary_foreground_color: "#ffffff"
                        background_color: "#f9fafb"
                        text_color: "#111827"
                        border_color: "#d1d5db"
                      whatsapp_setup_status: pending
                      whatsapp_setup_error: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /customers/{customer_id}/setup_links/{setup_link_id}:
    parameters:
      - name: customer_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: setup_link_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    patch:
      tags:
        - Setup Links
      summary: Update setup link
      operationId: updateCustomerSetupLink
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SetupLinkUpdateRequest"
            examples:
              revoke:
                summary: Revoke link
                value:
                  setup_link:
                    status: revoked
              extend:
                summary: Extend expiration
                value:
                  setup_link:
                    expires_at: "2025-09-15T10:00:00Z"
              rebrand:
                summary: Update theme
                value:
                  setup_link:
                    theme_config:
                      primary_color: "#10b981"
                      text_color: "#0f172a"
      responses:
        "200":
          description: Setup link updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SetupLinkResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/webhooks:
    get:
      tags:
        - Webhooks
      summary: List project webhooks
      description: Get all webhooks for the project (both project-scoped and phone
        number-scoped), most recent first.
      operationId: listWhatsappProjectWebhooks
      parameters:
        - name: kind
          in: query
          description: Filter by webhook kind
          schema:
            type: string
            enum:
              - kapso
              - meta
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
      responses:
        "200":
          description: OK
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
    post:
      tags:
        - Webhooks
      summary: Create project webhook
      description: |
        Create a webhook for this project.

        Two scoping options:
        - **Project-scoped**: Omit `phone_number_id` to receive project events only
        - **Number-scoped**: Include `phone_number_id` to receive message and conversation events for that number

        Project webhooks do not receive message or conversation events. Use a number-scoped webhook (or
        `POST /whatsapp/phone_numbers/{phone_number_id}/webhooks`) for those.

        Subscribing to `project.event` requires project events to be available on your plan.

        Two webhook types:
        - **kapso**: Event-based webhooks with filtered events, buffering support, and Kapso payload format
        - **meta**: Raw Meta webhook forwarding - receives the exact payload Meta sends (requires `phone_number_id`)
      operationId: createWhatsappProjectWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappProjectWebhookRequest"
            examples:
              project_scoped:
                summary: Project-wide webhook
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp
                    secret_key: wh_sec_3kfj9dmfkg8s2
                    events:
                      - whatsapp.phone_number.created
                      - whatsapp.phone_number.deleted
              number_scoped:
                summary: Number-specific webhook
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp
                    phone_number_id: "1234567890"
                    secret_key: wh_sec_3kfj9dmfkg8s2
                    events:
                      - whatsapp.message.received
                      - whatsapp.message.sent
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "402":
          $ref: "#/components/responses/PaymentRequiredError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/webhooks/{webhook_id}:
    parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Webhooks
      summary: Get project webhook
      operationId: getWhatsappProjectWebhook
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    patch:
      tags:
        - Webhooks
      summary: Update project webhook
      description: |
        Update a project webhook. Subscribing to `project.event` requires project events
        to be available on your plan.
      operationId: updateWhatsappProjectWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappWebhookUpdateRequest"
            examples:
              enable_buffering:
                summary: Enable buffering
                value:
                  whatsapp_webhook:
                    buffer_enabled: true
                    buffer_window_seconds: 5
                    max_buffer_size: 20
                    buffer_events:
                      - whatsapp.message.received
              pause:
                summary: Pause deliveries
                value:
                  whatsapp_webhook:
                    active: false
              change_url:
                summary: Update endpoint
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp-v2
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "402":
          $ref: "#/components/responses/PaymentRequiredError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"
    delete:
      tags:
        - Webhooks
      summary: Delete project webhook
      operationId: deleteWhatsappProjectWebhook
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/webhooks/{webhook_id}/test:
    parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      tags:
        - Webhooks
      summary: Test project webhook
      description: |
        Send a test payload to the webhook endpoint.

        Optionally specify an `event_type` to test with a specific event payload. The event type must be one of the events the webhook is configured to receive.
      operationId: testWhatsappProjectWebhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                event_type:
                  type: string
                  description: Specific event type to test (must be configured on webhook)
            examples:
              default:
                summary: Test with default event
                value: {}
              specific_event:
                summary: Test specific event
                value:
                  event_type: whatsapp.message.received
      responses:
        "200":
          description: Test queued
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      success:
                        type: boolean
                        example: true
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/phone_numbers:
    get:
      tags:
        - Phone Numbers
      summary: List phone numbers
      description: Get WhatsApp numbers in your project, most recent first.
      operationId: listWhatsappPhoneNumbers
      parameters:
        - name: phone_number_id
          in: query
          description: Filter by Meta phone number ID
          schema:
            type: string
        - name: business_account_id
          in: query
          description: Filter by WABA ID
          schema:
            type: string
        - name: customer_id
          in: query
          schema:
            type: string
            format: uuid
        - name: messaging_enabled
          in: query
          schema:
            type: boolean
        - name: name_contains
          in: query
          schema:
            type: string
        - name: created_after
          in: query
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          schema:
            type: string
            format: date-time
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappPhoneNumberListResponse"
              examples:
                multiple:
                  value:
                    data:
                      - id: "1234567890"
                        internal_id: 4a5b6c7d-8e9f-0a1b-2c3d-4e5f6a7b8c9d
                        phone_number_id: "1234567890"
                        name: Support Line
                        business_account_id: "98765432109"
                        is_coexistence: false
                        inbound_processing_enabled: true
                        calls_enabled: false
                        webhook_verified_at: "2025-01-14T15:10:00Z"
                        created_at: "2025-01-14T15:00:00Z"
                        updated_at: "2025-01-14T15:10:00Z"
                        display_name: Support Line
                        display_phone_number: "+1 555-123-4567"
                        display_phone_number_normalized: "15551234567"
                        verified_name: Acme Corp
                        quality_rating: GREEN
                        throughput_tier: TIER_10K
                        whatsapp_business_manager_messaging_limit: "10000"
                        customer_id: 3f2e1d0c-9b8a-7f6e-5d4c-3b2a1f0e9d8c
                        code_verification_status: COMPLETED
                        name_status: APPROVED
                        status: CONNECTED
                        is_official_business_account: false
                        is_pin_enabled: true
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 1
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /customers/{customer_id}/whatsapp/phone_numbers:
    parameters:
      - name: customer_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    post:
      tags:
        - Phone Numbers
      summary: Connect phone number
      description: |
        Connect a WhatsApp number to this customer using Meta credentials.

        Get credentials from Meta's App Dashboard after completing embedded signup or manual setup.
      operationId: createCustomerWhatsappPhoneNumber
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappPhoneNumberCreateRequest"
            examples:
              production:
                summary: Production number
                value:
                  whatsapp_phone_number:
                    name: Support Line
                    kind: production
                    phone_number_id: "1234567890"
                    business_account_id: "98765432109"
                    access_token: EAABsbCS...long-lived-token
                    webhook_destination_url: https://yourapp.com/webhooks/whatsapp
                    webhook_verify_token: your-verify-token
                    inbound_processing_enabled: true
                    calls_enabled: false
              sandbox:
                summary: Test with sandbox
                value:
                  whatsapp_phone_number:
                    name: Test Number
                    kind: sandbox
                    phone_number_id: sandbox
                    business_account_id: sandbox
                    access_token: sandbox
      responses:
        "201":
          description: Phone number connected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappPhoneNumberResponse"
              examples:
                created:
                  value:
                    data:
                      id: "1234567890"
                      internal_id: 4a5b6c7d-8e9f-0a1b-2c3d-4e5f6a7b8c9d
                      phone_number_id: "1234567890"
                      name: Support Line
                      business_account_id: "98765432109"
                      is_coexistence: false
                      inbound_processing_enabled: true
                      calls_enabled: false
                      webhook_verified_at: "2025-01-15T10:10:00Z"
                      created_at: "2025-01-15T10:00:00Z"
                      updated_at: "2025-01-15T10:10:00Z"
                      customer_id: 3f2e1d0c-9b8a-7f6e-5d4c-3b2a1f0e9d8c
                      display_name: Support Line
                      display_phone_number: "+1 555-123-4567"
                      display_phone_number_normalized: "15551234567"
                      verified_name: Acme Corp
                      quality_rating: GREEN
                      code_verification_status: COMPLETED
                      name_status: APPROVED
                      status: CONNECTED
                      throughput_tier: TIER_10K
                      whatsapp_business_manager_messaging_limit: "10000"
                      is_official_business_account: false
                      is_pin_enabled: true
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "409":
          description: Phone limit reached or duplicate
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/phone_numbers/{phone_number_id}:
    parameters:
      - name: phone_number_id
        in: path
        required: true
        description: Meta phone number ID
        schema:
          type: string

    get:
      tags:
        - Phone Numbers
      summary: Get phone number
      operationId: getWhatsappPhoneNumber
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappPhoneNumberResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

    patch:
      tags:
        - Phone Numbers
      summary: Update phone number
      operationId: updateWhatsappPhoneNumber
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappPhoneNumberUpdateRequest"
            examples:
              rotate_token:
                summary: Rotate credentials
                value:
                  whatsapp_phone_number:
                    access_token: EAABsbCS...new-token
                    webhook_destination_url: https://yourapp.com/webhooks/whatsapp
              toggle_calls:
                summary: Enable voice calls
                value:
                  whatsapp_phone_number:
                    calls_enabled: true
      responses:
        "200":
          description: Phone number updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappPhoneNumberResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

    delete:
      tags:
        - Phone Numbers
      summary: Delete phone number
      operationId: deleteWhatsappPhoneNumber
      responses:
        "204":
          description: Phone number deleted
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/phone_numbers/{phone_number_id}/health:
    parameters:
      - name: phone_number_id
        in: path
        required: true
        schema:
          type: string

    get:
      tags:
        - Phone Numbers
      summary: Check phone health
      description: |
        Health check via Meta APIs and Kapso services.

        Results are cached for 3 minutes per phone number, so repeated calls can
        return the same payload. Use `timestamp` to tell when the check actually
        ran. The cache is invalidated early when the number's configuration
        changes.

        If a fresh check is already running for the same number and does not
        finish within 15 seconds, the response is `status: error` with an
        `error` message asking you to retry shortly.
      operationId: getWhatsappPhoneNumberHealth
      responses:
        "200":
          description: Health status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappPhoneNumberHealthResponse"
              examples:
                healthy:
                  summary: All checks pass
                  value:
                    status: healthy
                    timestamp: "2025-01-20T14:25:30Z"
                    checks:
                      phone_number_access:
                        passed: true
                        details:
                          verified_name: My Business
                          display_phone_number: "+1 555-123-4567"
                          quality_rating: GREEN
                          status: CONNECTED
                          throughput_tier: TIER_10K
                          id: "123456789012345"
                      messaging_health:
                        passed: true
                        overall_status: AVAILABLE
                        details:
                          can_send_message: AVAILABLE
                          entities:
                            - entity_type: PHONE_NUMBER
                              id: "123456789012345"
                              can_send_message: AVAILABLE
                            - entity_type: WABA
                              id: "987654321098765"
                              can_send_message: AVAILABLE
                            - entity_type: BUSINESS
                              id: "456789012345678"
                              can_send_message: AVAILABLE
                            - entity_type: APP
                              id: "789012345678901"
                              can_send_message: AVAILABLE
                      webhook_subscription:
                        passed: true
                        details:
                          app_id: "789012345678901"
                          subscribed: true
                          subscribed_fields:
                            - messages
                            - message_template_status_update
                      webhook_verified:
                        passed: true
                        details:
                          verified_at: "2025-01-15T10:30:00Z"
                          message: Webhook successfully verified at 2025-01-15 10:30:00 UTC
                degraded:
                  summary: Limited messaging or webhook issues
                  value:
                    status: degraded
                    timestamp: "2025-01-15T10:00:00Z"
                    checks:
                      phone_number_access:
                        passed: true
                        details:
                          verified_name: Acme Corp
                          display_phone_number: "+1 555-123-4567"
                          quality_rating: GREEN
                          status: CONNECTED
                          throughput_tier: TIER_10K
                      messaging_health:
                        passed: false
                        overall_status: LIMITED
                        details:
                          can_send_message: LIMITED
                          entities:
                            - entity_type: PHONE_NUMBER
                              id: "1234567890"
                              can_send_message: LIMITED
                              additional_info:
                                - There is an issue with the payment method.
                                  Template sending and business-initiated
                                  conversations are blocked.
                                - You can still send and receive messages within
                                  the 24-hour window.
                      webhook_subscription:
                        passed: true
                        details:
                          app_id: "123456789"
                          subscribed: true
                          subscribed_fields:
                            - messages
                      webhook_verified:
                        passed: false
                        details:
                          verified_at: null
                          message: Webhook not yet verified by Meta
                unhealthy_payment_method:
                  summary: Payment method error (Meta billing issue)
                  description: Must be resolved in Meta Business Manager, not in Kapso
                  value:
                    status: unhealthy
                    timestamp: "2025-01-20T16:45:15Z"
                    checks:
                      phone_number_access:
                        passed: true
                        details:
                          verified_name: Demo Company
                          display_phone_number: "+1 555-987-6543"
                          quality_rating: GREEN
                          status: CONNECTED
                          throughput_tier: TIER_10K
                          id: "998877665544332"
                      messaging_health:
                        passed: false
                        overall_status: BLOCKED
                        details:
                          can_send_message: BLOCKED
                          entities:
                            - entity_type: PHONE_NUMBER
                              id: "998877665544332"
                              can_send_message: LIMITED
                              additional_info:
                                - Your display name has not been approved yet.
                                  Your message limit will increase after the
                                  display name is approved.
                            - entity_type: WABA
                              id: "556677889900112"
                              can_send_message: BLOCKED
                              errors:
                                - error_code: "141006"
                                  error_description: There is an error with the payment method. This will block
                                    business initiated conversations.
                                  possible_solution: There was an error with your payment method. Please add a new
                                    payment method to the account.
                            - entity_type: BUSINESS
                              id: "334455667788990"
                              can_send_message: LIMITED
                            - entity_type: APP
                              id: "112233445566778"
                              can_send_message: AVAILABLE
                      webhook_subscription:
                        passed: true
                        details:
                          app_id: "112233445566778"
                          subscribed: true
                          subscribed_fields:
                            - messages
                      webhook_verified:
                        passed: false
                        details:
                          verified_at: null
                          message: Webhook not yet verified by Meta
                unhealthy_invalid_token:
                  summary: Invalid OAuth token
                  description: User needs to reconnect WhatsApp Business Account
                  value:
                    status: unhealthy
                    timestamp: "2025-01-20T18:22:45Z"
                    checks:
                      phone_number_access:
                        passed: false
                        error: 'Meta Graph API error: 400 - {"error" => {"message" => "Invalid OAuth
                          access token - Cannot parse access token", "type" =>
                          "OAuthException", "code" => 190, "fbtrace_id" =>
                          "ABC123xyz456"}}'
                      messaging_health:
                        passed: false
                        error: 'Meta Graph API error: 400 - {"error" => {"message" => "Invalid OAuth
                          access token - Cannot parse access token", "type" =>
                          "OAuthException", "code" => 190, "fbtrace_id" =>
                          "DEF789abc012"}}'
                      webhook_subscription:
                        passed: false
                        error: 'Meta Graph API error: 401 - {"error" => {"message" => "Invalid OAuth
                          access token - Cannot parse access token", "type" =>
                          "OAuthException", "code" => 190, "fbtrace_id" =>
                          "GHI345def678"}}'
                      webhook_verified:
                        passed: true
                        details:
                          verified_at: "2025-01-10T08:15:30Z"
                          message: Webhook successfully verified at 2025-01-10 08:15:30 UTC
                error_response:
                  summary: Health check service error
                  value:
                    status: error
                    error: Service temporarily unavailable
                    timestamp: "2025-01-20T10:00:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/phone_numbers/{phone_number_id}/webhooks:
    parameters:
      - name: phone_number_id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Webhooks
      summary: List webhooks
      description: Get webhooks for this number, most recent first.
      operationId: listWhatsappPhoneNumberWebhooks
      parameters:
        - name: url_contains
          in: query
          description: Filter by URL substring
          schema:
            type: string
        - name: kind
          in: query
          description: Filter by webhook kind
          schema:
            type: string
            enum:
              - kapso
              - meta
        - name: active
          in: query
          description: Filter by active status
          schema:
            type: boolean
        - name: created_after
          in: query
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          schema:
            type: string
            format: date-time
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: OK
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    post:
      tags:
        - Webhooks
      summary: Create webhook
      description: |
        Subscribe to WhatsApp events for this number.

        Two webhook types available:
        - **kapso**: Event-based webhooks with filtered events, buffering support, and Kapso payload format
        - **meta**: Raw Meta webhook forwarding - receives the exact payload Meta sends, with X-Idempotency-Key header

        Use buffering (kapso only) to batch high-volume events like inbound messages. Without buffering, each message triggers an immediate webhook delivery.
      operationId: createWhatsappPhoneNumberWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappWebhookRequest"
            examples:
              basic:
                summary: No buffering
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp
                    secret_key: wh_sec_3kfj9dmfkg8s2
                    events:
                      - whatsapp.message.received
                      - whatsapp.message.sent
                    active: true
              buffered:
                summary: Batch inbound messages
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp
                    secret_key: wh_sec_3kfj9dmfkg8s2
                    events:
                      - whatsapp.message.received
                      - whatsapp.message.sent
                    buffer_enabled: true
                    buffer_window_seconds: 10
                    max_buffer_size: 25
                    buffer_events:
                      - whatsapp.message.received
                    headers:
                      X-Webhook-Source: kapso
              with_inactivity:
                summary: Track conversation inactivity
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp
                    secret_key: wh_sec_3kfj9dmfkg8s2
                    events:
                      - whatsapp.message.received
                      - whatsapp.conversation.inactive
                    inactivity_minutes: 30
              meta_forward:
                summary: Forward raw Meta webhooks
                value:
                  whatsapp_webhook:
                    kind: meta
                    url: https://api.acme.com/webhooks/whatsapp-meta
                    active: true
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookResponse"
              examples:
                created:
                  value:
                    data:
                      id: 9e8d7c6b-5a4f-3e2d-1c0b-9a8f7e6d5c4b
                      url: https://api.acme.com/webhooks/whatsapp
                      events:
                        - whatsapp.message.received
                        - whatsapp.message.sent
                      active: true
                      secret_key: wh_sec_3kfj9dmfkg8s2
                      headers:
                        X-Webhook-Source: kapso
                      buffer_enabled: false
                      buffer_window_seconds: null
                      max_buffer_size: null
                      buffer_events: []
                      inactivity_minutes: 60
                      project_id: 1d6ca0a3-91c2-4f13-8a94-28ddb0d5f2f3
                      phone_number_id: "1234567890"
                      created_at: "2025-07-14T15:00:00Z"
                      updated_at: "2025-07-14T15:00:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/phone_numbers/{phone_number_id}/webhooks/{webhook_id}:
    parameters:
      - name: phone_number_id
        in: path
        required: true
        schema:
          type: string
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Webhooks
      summary: Get webhook
      operationId: getWhatsappPhoneNumberWebhook
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    patch:
      tags:
        - Webhooks
      summary: Update webhook
      operationId: updateWhatsappPhoneNumberWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappWebhookUpdateRequest"
            examples:
              enable_buffering:
                summary: Enable buffering
                value:
                  whatsapp_webhook:
                    buffer_enabled: true
                    buffer_window_seconds: 5
                    max_buffer_size: 20
                    buffer_events:
                      - whatsapp.message.received
              pause:
                summary: Pause deliveries
                value:
                  whatsapp_webhook:
                    active: false
              change_url:
                summary: Update endpoint
                value:
                  whatsapp_webhook:
                    url: https://api.acme.com/webhooks/whatsapp-v2
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappWebhookResponse"
              examples:
                updated:
                  value:
                    data:
                      id: 9e8d7c6b-5a4f-3e2d-1c0b-9a8f7e6d5c4b
                      url: https://api.acme.com/webhooks/whatsapp
                      events:
                        - whatsapp.message.received
                      active: true
                      secret_key: wh_sec_3kfj9dmfkg8s2
                      headers: {}
                      buffer_enabled: true
                      buffer_window_seconds: 5
                      max_buffer_size: 20
                      buffer_events:
                        - whatsapp.message.received
                      inactivity_minutes: 60
                      project_id: 1d6ca0a3-91c2-4f13-8a94-28ddb0d5f2f3
                      phone_number_id: "1234567890"
                      created_at: "2025-07-14T15:00:00Z"
                      updated_at: "2025-07-16T09:00:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"
    delete:
      tags:
        - Webhooks
      summary: Delete webhook
      operationId: deleteWhatsappPhoneNumberWebhook
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/broadcasts:
    get:
      tags:
        - Broadcasts
      summary: List broadcasts
      description: Get broadcast campaigns, most recent first.
      operationId: listWhatsappBroadcasts
      parameters:
        - name: phone_number_id
          in: query
          description: Filter by phone number
          schema:
            type: string
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum:
              - draft
              - sending
              - completed
              - failed
        - name: created_after
          in: query
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          schema:
            type: string
            format: date-time
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: OK
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastListResponse"
              examples:
                campaigns:
                  value:
                    data:
                      - id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                        name: Weekend Sale 2025
                        status: completed
                        started_at: "2025-07-15T10:00:00Z"
                        completed_at: "2025-07-15T11:30:00Z"
                        created_at: "2025-07-14T15:00:00Z"
                        updated_at: "2025-07-15T11:30:00Z"
                        phone_number_id: "1234567890"
                        whatsapp_template:
                          id: "784203120908608"
                          meta_template_id: "784203120908608"
                          name: weekend_sale_2025
                          language_code: en_US
                          category: MARKETING
                          status: approved
                          components:
                            - type: BODY
                              text: "Hi {{name}}! Get {{discount}}% off this weekend only!"
                              example:
                                body_text_named_params:
                                  - param_name: name
                                    example: John
                                  - param_name: discount
                                    example: "25"
                        total_recipients: 1000
                        sent_count: 950
                        failed_count: 50
                        delivered_count: 900
                        read_count: 750
                        responded_count: 120
                        pending_count: 0
                        response_rate: 12.6
                      - id: 3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f
                        name: Product Launch Q3
                        status: sending
                        started_at: "2025-07-16T09:00:00Z"
                        completed_at: null
                        created_at: "2025-07-16T08:00:00Z"
                        updated_at: "2025-07-16T09:15:00Z"
                        phone_number_id: "0987654321"
                        whatsapp_template:
                          id: "891234567890123"
                          meta_template_id: "891234567890123"
                          name: product_launch_q3
                          language_code: en_US
                          category: MARKETING
                          status: approved
                          components:
                            - type: BODY
                              text: "New product alert! Check out our Q3 launch."
                        total_recipients: 500
                        sent_count: 250
                        failed_count: 10
                        delivered_count: 240
                        read_count: 100
                        responded_count: 15
                        pending_count: 240
                        response_rate: 6.0
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 2
        "401":
          $ref: "#/components/responses/UnauthorizedError"
    post:
      tags:
        - Broadcasts
      summary: Create broadcast
      description: |
        Create a broadcast campaign in draft mode.

        Workflow: create broadcast → add recipients → send. Broadcasts stay in draft until you call the send endpoint.
      operationId: createWhatsappBroadcast
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappBroadcastCreateRequest"
            examples:
              meta_id:
                summary: Create with Meta template ID (preferred)
                value:
                  whatsapp_broadcast:
                    name: Weekend Sale 2025
                    phone_number_id: "1234567890"
                    whatsapp_template_id: "784203120908608"
              legacy:
                summary: Legacy UUID (backward compatibility)
                value:
                  whatsapp_broadcast:
                    name: Weekend Sale 2025
                    phone_number_id: "1234567890"
                    whatsapp_template_id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastResponse"
              examples:
                draft:
                  value:
                    data:
                      id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      name: Weekend Sale 2025
                      status: draft
                      started_at: null
                      completed_at: null
                      created_at: "2025-07-14T15:00:00Z"
                      updated_at: "2025-07-14T15:00:00Z"
                      phone_number_id: "1234567890"
                      whatsapp_template:
                        id: "784203120908608"
                        meta_template_id: "784203120908608"
                        name: weekend_sale_2025
                        language_code: en_US
                        category: MARKETING
                        status: approved
                        components:
                          - type: BODY
                            text: "Hi {{name}}! Get {{discount}}% off this weekend only!"
                            example:
                              body_text_named_params:
                                - param_name: name
                                  example: John
                                - param_name: discount
                                  example: "25"
                      total_recipients: 0
                      sent_count: 0
                      failed_count: 0
                      delivered_count: 0
                      read_count: 0
                      responded_count: 0
                      pending_count: 0
                      response_rate: 0.0
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          description: Phone number or template not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/broadcasts/{broadcast_id}:
    parameters:
      - name: broadcast_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Broadcasts
      summary: Get broadcast
      operationId: getWhatsappBroadcast
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastResponse"
              examples:
                sending:
                  value:
                    data:
                      id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      name: Weekend Sale 2025
                      status: sending
                      started_at: "2025-07-15T10:00:00Z"
                      stopped_at: null
                      completed_at: null
                      created_at: "2025-07-14T15:00:00Z"
                      updated_at: "2025-07-15T10:30:00Z"
                      phone_number_id: "1234567890"
                      whatsapp_template:
                        id: "784203120908608"
                        meta_template_id: "784203120908608"
                        name: weekend_sale_2025
                        language_code: en_US
                        category: MARKETING
                        status: approved
                        components:
                          - type: BODY
                            text: "Hi {{name}}! Get {{discount}}% off this weekend only!"
                            example:
                              body_text_named_params:
                                - param_name: name
                                  example: John
                                - param_name: discount
                                  example: "25"
                      total_recipients: 1000
                      sent_count: 650
                      failed_count: 50
                      delivered_count: 600
                      read_count: 320
                      responded_count: 45
                      pending_count: 300
                      response_rate: 6.9
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    patch:
      tags:
        - Broadcasts
      summary: Update broadcast status
      description: |
        Update a broadcast status.

        Set `status` to `stopped` to stop a broadcast that is currently sending. Pending recipients remain pending and no new sends are started.

        Set `status` to `draft` to cancel a scheduled broadcast and clear its schedule.
      operationId: updateWhatsappBroadcastStatus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappBroadcastUpdateRequest"
            examples:
              stop:
                summary: Stop a sending broadcast
                value:
                  whatsapp_broadcast:
                    status: stopped
              return_to_draft:
                summary: Return a scheduled broadcast to draft
                value:
                  whatsapp_broadcast:
                    status: draft
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastResponse"
              examples:
                stopped:
                  value:
                    data:
                      id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      name: Weekend Sale 2025
                      status: stopped
                      scheduled_at: null
                      started_at: "2025-07-15T10:00:00Z"
                      stopped_at: "2025-07-15T10:12:30Z"
                      completed_at: null
                      created_at: "2025-07-14T15:00:00Z"
                      updated_at: "2025-07-15T10:12:30Z"
                      phone_number_id: "1234567890"
                      whatsapp_template: null
                      total_recipients: 1000
                      sent_count: 650
                      failed_count: 50
                      delivered_count: 600
                      read_count: 320
                      responded_count: 45
                      pending_count: 300
                      response_rate: 6.9
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/broadcasts/{broadcast_id}/recipients:
    parameters:
      - name: broadcast_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Broadcasts
      summary: List recipients
      description: Get recipients for this broadcast with delivery status.
      operationId: listWhatsappBroadcastRecipients
      parameters:
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: OK
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastRecipientListResponse"
              examples:
                recipients:
                  value:
                    data:
                      - id: 8c9d0e1f-2a3b-4c5d-6e7f-8a9b0c1d2e3f
                        phone_number: "14155550123"
                        status: sent
                        sent_at: "2025-07-15T10:05:23Z"
                        delivered_at: "2025-07-15T10:05:30Z"
                        read_at: "2025-07-15T10:12:45Z"
                        responded_at: "2025-07-15T10:15:00Z"
                        created_at: "2025-07-15T10:00:00Z"
                        updated_at: "2025-07-15T10:15:00Z"
                        template_components:
                          - type: body
                            parameters:
                              - type: text
                                parameter_name: first_name
                                text: Alicia
                              - type: text
                                parameter_name: discount_code
                                text: SAVE25
                      - id: 7b8c9d0e-1f2a-3b4c-5d6e-7f8a9b0c1d2e
                        phone_number: "14155550124"
                        status: failed
                        sent_at: "2025-07-15T10:05:30Z"
                        failed_at: "2025-07-15T10:05:32Z"
                        error_message: Invalid phone number
                        error_details:
                          error_code: "131047"
                          error_subcode: "2494055"
                        created_at: "2025-07-15T10:00:10Z"
                        updated_at: "2025-07-15T10:05:32Z"
                        template_components:
                          - type: body
                            parameters:
                              - type: text
                                parameter_name: first_name
                                text: Jane
                              - type: text
                                parameter_name: discount
                                text: SAVE40
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 2
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    post:
      tags:
        - Broadcasts
      summary: Add recipients
      description: |
        Add up to 1000 recipients to a draft broadcast. Duplicates are skipped.

        Recipients use Meta's component syntax with body, header, and button components.
      operationId: addWhatsappBroadcastRecipients
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappBroadcastRecipientsAddRequest"
            examples:
              body_params:
                summary: Body text parameters
                value:
                  whatsapp_broadcast:
                    recipients:
                      - phone_number: "+14155550123"
                        components:
                          - type: body
                            parameters:
                              - type: text
                                parameter_name: first_name
                                text: John
                              - type: text
                                parameter_name: discount
                                text: SAVE50
                      - phone_number: "+14155550124"
                        components:
                          - type: body
                            parameters:
                              - type: text
                                parameter_name: first_name
                                text: Jane
                              - type: text
                                parameter_name: discount
                                text: SAVE40
              with_buttons:
                summary: Body params with dynamic button URL
                value:
                  whatsapp_broadcast:
                    recipients:
                      - phone_number: "+14155550123"
                        components:
                          - type: body
                            parameters:
                              - type: text
                                parameter_name: first_name
                                text: Alicia
                              - type: text
                                parameter_name: discount_code
                                text: SAVE25
                          - type: button
                            sub_type: url
                            index: 0
                            parameters:
                              - type: text
                                text: promo-code-12345
              with_media:
                summary: Media header with body params
                value:
                  whatsapp_broadcast:
                    recipients:
                      - phone_number: "+14155550123"
                        components:
                          - type: header
                            parameters:
                              - type: image
                                image:
                                  link: https://cdn.acme.com/promo-banner.jpg
                          - type: body
                            parameters:
                              - type: text
                                parameter_name: first_name
                                text: Alicia
                              - type: text
                                parameter_name: discount_code
                                text: SAVE25
      responses:
        "201":
          description: Added
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastRecipientsAddResponse"
              examples:
                summary:
                  value:
                    data:
                      added: 495
                      duplicates: 5
                      errors:
                        - "Recipient 3: template parameters invalid - requires 2
                          parameters but got 1"
                        - "Recipient 7: invalid phone number format"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"
    delete:
      tags:
        - Broadcasts
      summary: Clear recipients
      description: |
        Remove all recipients from a draft or scheduled broadcast.

        Clearing recipients from a scheduled broadcast also returns it to draft and clears `scheduled_at`.
      operationId: clearWhatsappBroadcastRecipients
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/broadcasts/{broadcast_id}/send:
    parameters:
      - name: broadcast_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      tags:
        - Broadcasts
      summary: Send broadcast
      description: Start sending messages immediately. This is asynchronous - use GET
        /broadcasts/{id} to monitor progress.
      operationId: sendWhatsappBroadcast
      responses:
        "202":
          description: Accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastSendResponse"
              examples:
                accepted:
                  value:
                    data:
                      id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      status: sending
                      scheduled_at: null
                      started_at: "2025-10-12T17:03:21Z"
                      stopped_at: null
                      completed_at: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/broadcasts/{broadcast_id}/schedule:
    parameters:
      - name: broadcast_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      tags:
        - Broadcasts
      summary: Schedule broadcast
      description: Schedule a broadcast to send at a future time. The broadcast must
        be in draft status and have recipients.
      operationId: scheduleWhatsappBroadcast
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - scheduled_at
              properties:
                scheduled_at:
                  type: string
                  format: date-time
                  description: ISO-8601 timestamp with timezone. Must be in the future.
            examples:
              schedule:
                value:
                  scheduled_at: "2025-10-12T17:00:00Z"
      responses:
        "202":
          description: Accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastSendResponse"
              examples:
                scheduled:
                  value:
                    data:
                      id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      status: scheduled
                      scheduled_at: "2025-10-12T17:00:00Z"
                      started_at: null
                      stopped_at: null
                      completed_at: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/broadcasts/{broadcast_id}/cancel:
    parameters:
      - name: broadcast_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      tags:
        - Broadcasts
      summary: Cancel scheduled broadcast
      description: Cancel a scheduled broadcast and return it to draft status. Only
        works for broadcasts in scheduled status.
      operationId: cancelWhatsappBroadcast
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappBroadcastSendResponse"
              examples:
                cancelled:
                  value:
                    data:
                      id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      status: draft
                      scheduled_at: null
                      started_at: null
                      stopped_at: null
                      completed_at: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/messages:
    get:
      tags:
        - Messages
      summary: List messages
      description: >
        Query WhatsApp messages across all conversations. Results are returned
        newest first. Use cursor pagination (`limit`, `after`, `before`) to
        traverse large message histories efficiently.
      operationId: listWhatsappMessages
      parameters:
        - name: phone_number_id
          in: query
          description: Filter by WhatsApp phone number ID
          schema:
            type: string
        - name: conversation_id
          in: query
          description: Filter by conversation ID
          schema:
            type: string
            format: uuid
        - name: phone_number
          in: query
          description: Filter by contact phone number (partial match)
          schema:
            type: string
        - name: business_scoped_user_id
          in: query
          description: Filter by exact WhatsApp business-scoped user ID
          schema:
            type: string
        - name: direction
          in: query
          description: Filter by message direction
          schema:
            type: string
            enum:
              - inbound
              - outbound
        - name: status
          in: query
          description: Filter by message status
          schema:
            type: string
            enum:
              - pending
              - sent
              - delivered
              - read
              - failed
        - name: message_type
          in: query
          description: Filter by message type
          schema:
            type: string
        - name: has_media
          in: query
          description: Filter messages with media attachments
          schema:
            type: boolean
        - name: limit
          in: query
          description: Maximum number of results per cursor-paginated page (default 20,
            max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: before
          in: query
          description: Cursor for the previous page (Base64 encoded).
          schema:
            type: string
        - name: after
          in: query
          description: Cursor for the next page (Base64 encoded).
          schema:
            type: string
      responses:
        "200":
          description: List of messages
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappMessageListResponse"
              examples:
                messages:
                  value:
                    data:
                      - id: wamid.HBgMMTIzNDU2Nzg5MBUCABIYGTA5RTlCQkI2NTI3
                        timestamp: "1705395000"
                        type: text
                        from: "14155550123"
                        text:
                          body: Hello, I need help with my order
                        kapso:
                          direction: inbound
                          status: delivered
                          processing_status: processed
                          origin: cloud_api
                          phone_number: "14155550123"
                          phone_number_id: "123456789012345"
                          has_media: false
                          whatsapp_conversation_id: c63ced48-1283-4d55-8c8d-930f525aa0e5
                          contact_name: Alicia
                          content: Hello, I need help with my order
                    paging:
                      cursors:
                        before: eyJ2YWx1ZXMiOlsiMjAyNi0wMy0yNlQxNTowNTowMC4wMDAwMDBaIiwiMzQzMCJdLCJjb2x1bW5zIjpbImNyZWF0ZWRfYXQiLCJpZCJdfQ==
                        after: eyJ2YWx1ZXMiOlsiMjAyNi0wMy0yNlQxNTowMDowMC4wMDAwMDBaIiwiMzQyMSJdLCJjb2x1bW5zIjpbImNyZWF0ZWRfYXQiLCJpZCJdfQ==
                      next: eyJ2YWx1ZXMiOlsiMjAyNi0wMy0yNlQxNTowMDowMC4wMDAwMDBaIiwiMzQyMSJdLCJjb2x1bW5zIjpbImNyZWF0ZWRfYXQiLCJpZCJdfQ==
                      previous:
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /whatsapp/messages/{message_id}:
    parameters:
      - name: message_id
        in: path
        required: true
        description: WhatsApp message ID (WAMID)
        schema:
          type: string
        example: wamid.HBgMMTIzNDU2Nzg5MBUCABIYGTA5RTlCQkI2NTI3
    get:
      tags:
        - Messages
      summary: Get message
      description: Retrieve a single message by its WhatsApp message ID (WAMID).
      operationId: getWhatsappMessage
      responses:
        "200":
          description: Message details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappMessageResponse"
              examples:
                message:
                  value:
                    data:
                      id: wamid.HBgMMTIzNDU2Nzg5MBUCABIYGTA5RTlCQkI2NTI3
                      timestamp: "1705395000"
                      type: text
                      from: "14155550123"
                      text:
                        body: Hello, I need help with my order
                      kapso:
                        direction: inbound
                        status: read
                        processing_status: processed
                        origin: cloud_api
                        phone_number: "14155550123"
                        phone_number_id: "123456789012345"
                        has_media: false
                        whatsapp_conversation_id: c63ced48-1283-4d55-8c8d-930f525aa0e5
                        contact_name: Alicia
                        content: Hello, I need help with my order
                        statuses:
                          - id: wamid.HBgMMTIzNDU2Nzg5MBUCABIYGTA5RTlCQkI2NTI3
                            status: delivered
                            timestamp: "1705395005"
                            recipient_id: "14155550123"
                          - id: wamid.HBgMMTIzNDU2Nzg5MBUCABIYGTA5RTlCQkI2NTI3
                            status: read
                            timestamp: "1705395300"
                            recipient_id: "14155550123"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/conversations:
    get:
      tags:
        - Conversations
      summary: List conversations
      description: Query WhatsApp conversations with filters. Results are returned by
        latest activity first and use cursor pagination.
      operationId: listWhatsappConversations
      parameters:
        - name: phone_number_id
          in: query
          description: Filter by WhatsApp phone number ID
          schema:
            type: string
        - name: phone_number
          in: query
          description: Filter by contact phone number (partial match)
          schema:
            type: string
        - name: status
          in: query
          description: Filter by conversation status
          schema:
            type: string
            enum:
              - active
              - ended
        - name: assigned_user_id
          in: query
          description: Filter by active assignee user ID (must be a project member)
          schema:
            type: string
            format: uuid
        - name: unassigned
          in: query
          description: Filter conversations with no active assignment. Cannot be combined
            with `assigned_user_id`.
          schema:
            type: boolean
        - name: created_after
          in: query
          description: Filter conversations created after this timestamp
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          description: Filter conversations created before this timestamp
          schema:
            type: string
            format: date-time
        - name: last_active_after
          in: query
          description: Filter conversations active after this timestamp
          schema:
            type: string
            format: date-time
        - name: last_active_before
          in: query
          description: Filter conversations active before this timestamp
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results per cursor-paginated page (default 20,
            max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: before
          in: query
          description: Cursor for the previous page (Base64 encoded).
          schema:
            type: string
        - name: after
          in: query
          description: Cursor for the next page (Base64 encoded).
          schema:
            type: string
      responses:
        "200":
          description: List of conversations
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappConversationListResponse"
              examples:
                conversations:
                  value:
                    data:
                      - id: c63ced48-1283-4d55-8c8d-930f525aa0e5
                        phone_number: "14155550123"
                        status: active
                        last_active_at: "2025-07-16T09:45:00Z"
                        created_at: "2025-06-01T12:00:00Z"
                        updated_at: "2025-07-16T09:45:00Z"
                        metadata: {}
                        phone_number_id: "1234567890"
                        kapso:
                          contact_name: Alicia
                          messages_count: 42
                          last_message_id: wamid.HBgMMTIzNDU2
                          last_message_type: text
                          last_message_timestamp: "2025-07-16T09:40:00Z"
                          last_message_text: Thanks!
                          last_inbound_at: "2025-07-16T09:35:10Z"
                          last_outbound_at: "2025-07-16T09:40:00Z"
                    paging:
                      cursors:
                        before: eyJ2YWx1ZXMiOlsiMjAyNi0wNS0xNVQxMzowNTowMC4wMDAwMDBaIiwiYzYzY2VkNDgtMTI4My00ZDU1LThjOGQtOTMwZjUyNWFhMGU1Il0sImNvbHVtbnMiOlsibGFzdF9hY3RpdmVfYXQiLCJpZCJdfQ==
                        after: eyJ2YWx1ZXMiOlsiMjAyNi0wNS0xNVQxMjowNTowMC4wMDAwMDBaIiwiYzYzY2VkNDgtMTI4My00ZDU1LThjOGQtOTMwZjUyNWFhMGU1Il0sImNvbHVtbnMiOlsibGFzdF9hY3RpdmVfYXQiLCJpZCJdfQ==
                      next:
                      previous:
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/conversations/{conversation_id}:
    parameters:
      - name: conversation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Conversations
      summary: Get conversation
      description: Retrieve a single conversation with metadata.
      operationId: getWhatsappConversation
      responses:
        "200":
          description: Conversation details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappConversationResponse"
              examples:
                conversation:
                  value:
                    data:
                      id: c63ced48-1283-4d55-8c8d-930f525aa0e5
                      phone_number: "14155550123"
                      status: active
                      last_active_at: "2025-07-16T09:45:00Z"
                      created_at: "2025-06-01T12:00:00Z"
                      updated_at: "2025-07-16T09:45:00Z"
                      metadata: {}
                      phone_number_id: "1234567890"
                      kapso:
                        contact_name: Alicia
                        messages_count: 42
                        last_message_id: wamid.HBgMMTIzNDU2
                        last_message_type: text
                        last_message_timestamp: "2025-07-16T09:40:00Z"
                        last_message_text: Thanks!
                        last_inbound_at: "2025-07-16T09:35:10Z"
                        last_outbound_at: "2025-07-16T09:40:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    patch:
      tags:
        - Conversations
      summary: Update conversation status
      description: Close completed conversations or reopen them for follow-ups.
      operationId: updateWhatsappConversation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappConversationUpdateRequest"
            examples:
              close:
                summary: Close conversation
                value:
                  whatsapp_conversation:
                    status: ended
              reopen:
                summary: Reopen conversation
                value:
                  whatsapp_conversation:
                    status: active
      responses:
        "200":
          description: Conversation updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappConversationResponse"
              examples:
                ended:
                  summary: Closed conversation
                  value:
                    data:
                      id: c63ced48-1283-4d55-8c8d-930f525aa0e5
                      phone_number: "14155550123"
                      status: ended
                      last_active_at: "2025-07-16T09:45:00Z"
                      created_at: "2025-06-01T12:00:00Z"
                      updated_at: "2025-07-16T09:45:00Z"
                      metadata: {}
                      phone_number_id: "1234567890"
                      kapso:
                        contact_name: Alicia
                        messages_count: 42
                        last_message_id: wamid.HBgMMTIzNDU2
                        last_message_type: text
                        last_message_timestamp: "2025-07-16T09:40:00Z"
                        last_message_text: Thanks!
                        last_inbound_at: "2025-07-16T09:35:10Z"
                        last_outbound_at: "2025-07-16T09:40:00Z"
                active:
                  summary: Reopened conversation
                  value:
                    data:
                      id: c63ced48-1283-4d55-8c8d-930f525aa0e5
                      phone_number: "14155550123"
                      status: active
                      last_active_at: "2025-07-16T10:15:00Z"
                      created_at: "2025-06-01T12:00:00Z"
                      updated_at: "2025-07-16T10:15:00Z"
                      metadata: {}
                      phone_number_id: "1234567890"
                      kapso:
                        contact_name: Alicia
                        messages_count: 43
                        last_message_id: wamid.HBgMMTIzNDU3
                        last_message_type: text
                        last_message_timestamp: "2025-07-16T10:15:00Z"
                        last_message_text: Hey, one more thing...
                        last_inbound_at: "2025-07-16T10:15:00Z"
                        last_outbound_at: "2025-07-16T09:40:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/conversations/{conversation_id}/assignments:
    parameters:
      - name: conversation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Conversations
      summary: List conversation assignments
      description: Get all assignments for a conversation, most recent first.
      operationId: listConversationAssignments
      parameters:
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Assignments retrieved
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConversationAssignmentListResponse"
              examples:
                success:
                  value:
                    data:
                      - id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                        user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                        created_by_user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                        notes: "Handling customer inquiry about pricing"
                        active: true
                        created_at: "2026-01-19T10:30:00Z"
                      - id: b2c3d4e5-f6a7-8901-bcde-f12345678901
                        user_id: e2d3c4b5-a697-8076-edcb-a09876543210
                        created_by_user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                        notes: null
                        active: false
                        created_at: "2026-01-18T14:20:00Z"
                    meta:
                      page: 1
                      per_page: 25
                      total_pages: 1
                      total_count: 2
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    post:
      tags:
        - Conversations
      summary: Create conversation assignment
      description: |
        Assign a conversation to a team member. Only one active assignment is allowed per conversation.

        The user must be a member of the project.
      operationId: createConversationAssignment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConversationAssignmentCreateRequest"
            examples:
              with_notes:
                summary: Assign with notes
                value:
                  assignment:
                    user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                    notes: "Customer needs help with integration"
              simple:
                summary: Simple assignment
                value:
                  assignment:
                    user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
      responses:
        "201":
          description: Assignment created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConversationAssignmentResponse"
              examples:
                success:
                  value:
                    data:
                      id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                      user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                      created_by_user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                      notes: "Customer needs help with integration"
                      active: true
                      created_at: "2026-01-19T10:30:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                user_not_found:
                  summary: User not in project
                  value:
                    error: "User not found or not a member of this project"
                already_assigned:
                  summary: Conversation already assigned
                  value:
                    error: "Conversation already has an active assignment"

  /whatsapp/conversations/{conversation_id}/assignments/{id}:
    parameters:
      - name: conversation_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Conversations
      summary: Get conversation assignment
      description: Retrieve a specific assignment by ID.
      operationId: getConversationAssignment
      responses:
        "200":
          description: Assignment retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConversationAssignmentResponse"
              examples:
                success:
                  value:
                    data:
                      id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                      user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                      created_by_user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                      notes: "Handling customer inquiry"
                      active: true
                      created_at: "2026-01-19T10:30:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    patch:
      tags:
        - Conversations
      summary: Update conversation assignment
      description: |
        Update an assignment's notes, reassign to another user, or deactivate (unassign).

        Set `active: false` to unassign without deleting the assignment record.
      operationId: updateConversationAssignment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConversationAssignmentUpdateRequest"
            examples:
              update_notes:
                summary: Update notes
                value:
                  assignment:
                    notes: "Customer issue resolved"
              unassign:
                summary: Unassign conversation
                value:
                  assignment:
                    active: false
              reassign:
                summary: Reassign to different user
                value:
                  assignment:
                    user_id: e2d3c4b5-a697-8076-edcb-a09876543210
      responses:
        "200":
          description: Assignment updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConversationAssignmentResponse"
              examples:
                updated:
                  value:
                    data:
                      id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                      user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                      created_by_user_id: f1e2d3c4-b5a6-9870-fedc-ba0987654321
                      notes: "Customer issue resolved"
                      active: true
                      created_at: "2026-01-19T10:30:00Z"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                user_not_found:
                  summary: User not in project
                  value:
                    error: "User not found or not a member of this project"

  /whatsapp/phone_numbers/{phone_number_id}/display_name_requests:
    parameters:
      - name: phone_number_id
        in: path
        required: true
        schema:
          type: string
    get:
      tags:
        - Display Names
      summary: List display name requests
      description: View all display name change requests for this number, most recent first.
      operationId: listWhatsappDisplayNameRequests
      parameters:
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Display name requests retrieved
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappDisplayNameRequestListResponse"
              examples:
                multiple_requests:
                  summary: Recent requests with different statuses
                  value:
                    data:
                      - id: 2b0f4a1e-7a58-4a15-b0c9-0d7f1a2b3c4d
                        phone_number_id: "1234567890"
                        requested_display_name: Acme Support
                        previous_display_name: "+1 555-123-4567"
                        status: applied
                        submitted_at: "2025-07-14T15:00:00Z"
                        reviewed_at: "2025-07-15T12:34:00Z"
                        applied_at: "2025-07-16T09:00:00Z"
                        meta_error_code: null
                        meta_error_subcode: null
                        meta_error_type: null
                        meta_error_message: null
                      - id: 8c9d0e1f-2a3b-4c5d-6e7f-8a9b0c1d2e3f
                        phone_number_id: "1234567890"
                        requested_display_name: TechCorp Customer Service
                        previous_display_name: Acme Support
                        status: pending_review
                        submitted_at: "2025-07-20T10:30:00Z"
                        reviewed_at: null
                        applied_at: null
                        meta_error_code: null
                        meta_error_subcode: null
                        meta_error_type: null
                        meta_error_message: null
                      - id: 3f4a5b6c-7d8e-9f0a-1b2c-3d4e5f6a7b8c
                        phone_number_id: "1234567890"
                        requested_display_name: ACME Inc
                        previous_display_name: "+1 555-123-4567"
                        status: declined
                        submitted_at: "2025-06-15T08:00:00Z"
                        reviewed_at: "2025-06-16T14:22:00Z"
                        applied_at: null
                        meta_error_code: 100
                        meta_error_subcode: 2388135
                        meta_error_type: OAuthException
                        meta_error_message: "Display name does not match business verification details"
                    meta:
                      current_page: 1
                      total_pages: 1
                      total_count: 3
                      per_page: 20
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
    post:
      tags:
        - Display Names
      summary: Submit display name request
      description: |
        Request a display name change. Meta reviews most changes within 24-48 hours. Some names may be approved instantly.
      operationId: createWhatsappDisplayNameRequest
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappDisplayNameRequestCreate"
            examples:
              basic:
                summary: Business name
                value:
                  display_name_request:
                    new_display_name: Acme Support
              instant:
                summary: Instant approval scenario
                value:
                  display_name_request:
                    new_display_name: "Bella's Boutique"
      responses:
        "201":
          description: Display name request submitted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappDisplayNameRequestResponse"
              examples:
                pending:
                  summary: Awaiting Meta review
                  value:
                    data:
                      id: 2b0f4a1e-7a58-4a15-b0c9-0d7f1a2b3c4d
                      phone_number_id: "1234567890"
                      requested_display_name: Acme Support
                      previous_display_name: "+1 555-123-4567"
                      status: pending_review
                      submitted_at: "2025-07-14T15:00:00Z"
                      reviewed_at: null
                      applied_at: null
                      meta_error_code: null
                      meta_error_subcode: null
                      meta_error_type: null
                      meta_error_message: null
                instant:
                  summary: Approved instantly
                  value:
                    data:
                      id: 9d0e1f2a-3b4c-5d6e-7f8a-9b0c1d2e3f4a
                      phone_number_id: "1234567890"
                      requested_display_name: "Bella's Boutique"
                      previous_display_name: "+1 555-987-6543"
                      status: available_without_review
                      submitted_at: "2025-07-20T14:30:00Z"
                      reviewed_at: null
                      applied_at: null
                      meta_error_code: null
                      meta_error_subcode: null
                      meta_error_type: null
                      meta_error_message: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/phone_numbers/{phone_number_id}/display_name_requests/{request_id}:
    parameters:
      - name: phone_number_id
        in: path
        required: true
        schema:
          type: string
      - name: request_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Display Names
      summary: Retrieve display name request
      description: Check status of a display name change request. Poll this endpoint
        to monitor Meta's review progress.
      operationId: getWhatsappDisplayNameRequest
      responses:
        "200":
          description: Display name request details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappDisplayNameRequestResponse"
              examples:
                approved:
                  summary: Approved and applied
                  value:
                    data:
                      id: 2b0f4a1e-7a58-4a15-b0c9-0d7f1a2b3c4d
                      phone_number_id: "1234567890"
                      requested_display_name: Acme Support
                      previous_display_name: "+1 555-123-4567"
                      status: approved
                      submitted_at: "2025-07-14T15:00:00Z"
                      reviewed_at: "2025-07-15T12:34:00Z"
                      applied_at: "2025-07-16T09:00:00Z"
                      meta_error_code: null
                      meta_error_subcode: null
                      meta_error_type: null
                      meta_error_message: null
                declined:
                  summary: Rejected with error details
                  value:
                    data:
                      id: 3f4a5b6c-7d8e-9f0a-1b2c-3d4e5f6a7b8c
                      phone_number_id: "1234567890"
                      requested_display_name: ACME Inc
                      previous_display_name: "+1 555-123-4567"
                      status: declined
                      submitted_at: "2025-06-15T08:00:00Z"
                      reviewed_at: "2025-06-16T14:22:00Z"
                      applied_at: null
                      meta_error_code: 100
                      meta_error_subcode: 2388135
                      meta_error_type: OAuthException
                      meta_error_message: "Display name does not match business verification details"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/media:
    post:
      tags:
        - Media
      summary: Upload media
      description: |
        Upload media files for WhatsApp messaging from public URLs.

        Supports two delivery methods:
        - `meta_media`: Standard upload to Meta's media endpoint (30-day lifetime)
        - `meta_resumable_asset`: Resumable upload flow for profile pictures and large files

        **Security**: SSRF-protected - blocks private IPs, localhost, and metadata endpoints

        **Size limits**:
        - Images: 5 MB
        - Audio/Video: 16 MB
        - Documents: 100 MB

        Requests exceeding these limits fail immediately.
      operationId: uploadWhatsappMedia
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MediaIngestRequest"
            examples:
              meta_media:
                summary: Standard media upload
                value:
                  media_ingest:
                    phone_number_id: "713452918527238"
                    source: "https://upload.wikimedia.org/wikipedia/commons/2/2f/Example.png"
                    delivery: meta_media
              meta_resumable_asset:
                summary: Resumable upload for profile picture
                value:
                  media_ingest:
                    phone_number_id: "713452918527238"
                    source: "https://cdn.acme.com/profile.jpg"
                    delivery: meta_resumable_asset
              with_hints:
                summary: Upload with optional filename and MIME hints
                value:
                  media_ingest:
                    phone_number_id: "713452918527238"
                    source: "https://cdn.acme.com/documents/invoice-2025-01.pdf"
                    delivery: meta_media
                    filename: "Invoice-January-2025.pdf"
                    mime_type: "application/pdf"
              minimal:
                summary: Minimal request (defaults to meta_media)
                value:
                  media_ingest:
                    phone_number_id: "713452918527238"
                    source: "https://cdn.acme.com/photo.jpg"
      responses:
        "200":
          description: Media uploaded successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaIngestResponse"
              examples:
                meta_media_success:
                  summary: Standard upload successful
                  value:
                    data:
                      ingest_id: 8a9b0c1d-2e3f-4a5b-6c7d-8e9f0a1b2c3d
                      target:
                        kind: meta_media
                        media_id: "1234567890123456"
                      resource:
                        filename: Example.png
                        mime_type: image/png
                        size_bytes: 2335
                        sha256: "69da8b7d9c0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
                        source_url: "https://upload.wikimedia.org/wikipedia/commons/2/2f/Example.png"
                meta_resumable_asset_success:
                  summary: Resumable upload successful
                  value:
                    data:
                      ingest_id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      target:
                        kind: meta_resumable_asset
                        handle: "h:resumable-upload-handle-abc123xyz789"
                      resource:
                        filename: profile.jpg
                        mime_type: image/jpeg
                        size_bytes: 45678
                        sha256: "3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a"
                        source_url: "https://cdn.acme.com/profile.jpg"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                invalid_mime:
                  summary: Unsupported MIME type
                  value:
                    error: "Invalid MIME type: application/x-msdownload not supported by Meta"
                malformed_url:
                  summary: Invalid source URL
                  value:
                    error: "Source URL must be a valid HTTP or HTTPS URL"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                sandbox_rejected:
                  summary: Sandbox configs not allowed
                  value:
                    error: "Sandbox WhatsApp configurations cannot upload media"
                private_ip_blocked:
                  summary: SSRF protection triggered
                  value:
                    error: "Cannot fetch from private IP addresses or localhost"
        "404":
          description: Phone number not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                not_found:
                  value:
                    error: "WhatsApp phone number not found or not accessible in this project"
        "413":
          description: Payload too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                size_exceeded:
                  summary: File exceeds size limit
                  value:
                    error: "File size 6291456 bytes exceeds limit of 5242880 bytes for image/png"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                rate_limited:
                  value:
                    error: "Meta API rate limit exceeded. Retry after 60 seconds"
        "502":
          description: Meta API error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                meta_error:
                  summary: Meta rejected the upload
                  value:
                    error: "Meta API error: Invalid OAuth access token"

  /whatsapp/contacts:
    get:
      tags:
        - Contacts
      summary: List contacts
      description: Retrieve a paginated list of WhatsApp contacts for your project.
      operationId: listWhatsappContactsPlatform
      parameters:
        - name: customer_id
          in: query
          description: Filter by associated customer ID
          schema:
            type: string
        - name: customer_external_id
          in: query
          description: Filter by customer external ID
          schema:
            type: string
        - name: has_customer
          in: query
          description: Filter by customer association (true/false)
          schema:
            type: boolean
        - name: profile_name_contains
          in: query
          description: Filter by profile name (case-insensitive substring match)
          schema:
            type: string
        - name: wa_id_contains
          in: query
          description: Filter by WhatsApp ID (substring match)
          schema:
            type: string
        - name: business_scoped_user_id
          in: query
          description: Filter by exact WhatsApp business-scoped user ID
          schema:
            type: string
        - name: created_after
          in: query
          description: Filter contacts created after this ISO 8601 timestamp
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          description: Filter contacts created before this ISO 8601 timestamp
          schema:
            type: string
            format: date-time
        - name: page
          in: query
          description: >
            Page number for legacy offset pagination. Deprecated — use cursor
            pagination (`limit`, `after`, `before`) instead. Maximum page: 50.
            Maximum window (`page` × `per_page`): 5,000. Requests exceeding
            these limits return a 400 error.
          deprecated: true
          schema:
            type: integer
            default: 1
            maximum: 50
        - name: per_page
          in: query
          description: Items per page for legacy offset pagination.
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: limit
          in: query
          description: Maximum number of results per cursor-paginated page (default 20,
            max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: before
          in: query
          description: Cursor for the previous page (Base64 encoded).
          schema:
            type: string
        - name: after
          in: query
          description: Cursor for the next page (Base64 encoded).
          schema:
            type: string
      responses:
        "200":
          description: Successfully retrieved contacts
          headers:
            X-Total:
              description: Present only for legacy offset pagination responses.
              schema:
                type: string
            X-Total-Pages:
              description: Present only for legacy offset pagination responses.
              schema:
                type: string
            X-Per-Page:
              description: Present only for legacy offset pagination responses.
              schema:
                type: string
            X-Page:
              description: Present only for legacy offset pagination responses.
              schema:
                type: string
            Link:
              description: RFC 5988 pagination links for legacy offset pagination responses.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappContactListResponse"
              examples:
                offsetPagination:
                  value:
                    data:
                      - id: "123e4567-e89b-12d3-a456-426614174000"
                        wa_id: "15551234567"
                        profile_name: "John Doe"
                        display_name: "John (VIP)"
                        customer_id: "550e8400-e29b-41d4-a716-446655440000"
                        metadata:
                          segment: "vip"
                        created_at: "2025-03-26T15:05:00.000000Z"
                        updated_at: "2025-03-26T15:05:00.000000Z"
                      - id: "223e4567-e89b-12d3-a456-426614174001"
                        wa_id: "15557654321"
                        profile_name: "Jane Smith"
                        display_name: null
                        customer_id: null
                        metadata:
                          source: "sandbox"
                        created_at: "2025-03-26T15:00:00.000000Z"
                        updated_at: "2025-03-26T15:00:00.000000Z"
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 2
                cursorPagination:
                  value:
                    data:
                      - id: "123e4567-e89b-12d3-a456-426614174000"
                        wa_id: "15551234567"
                        profile_name: "John Doe"
                        display_name: "John (VIP)"
                        customer_id: "550e8400-e29b-41d4-a716-446655440000"
                        metadata:
                          segment: "vip"
                        created_at: "2025-03-26T15:05:00.000000Z"
                        updated_at: "2025-03-26T15:05:00.000000Z"
                      - id: "223e4567-e89b-12d3-a456-426614174001"
                        wa_id: "15557654321"
                        profile_name: "Jane Smith"
                        display_name: null
                        customer_id: null
                        metadata:
                          source: "sandbox"
                        created_at: "2025-03-26T15:00:00.000000Z"
                        updated_at: "2025-03-26T15:00:00.000000Z"
                    paging:
                      cursors:
                        before: eyJ2YWx1ZXMiOlsiMjAyNS0wMy0yNlQxNTowNTowMC4wMDAwMDBaIiwiMTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDAwIl0sImNvbHVtbnMiOlsiY3JlYXRlZF9hdCIsImlkIl19
                        after: eyJ2YWx1ZXMiOlsiMjAyNS0wMy0yNlQxNTowMDowMC4wMDAwMDBaIiwiMjIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDAxIl0sImNvbHVtbnMiOlsiY3JlYXRlZF9hdCIsImlkIl19
                      next: eyJ2YWx1ZXMiOlsiMjAyNS0wMy0yNlQxNTowMDowMC4wMDAwMDBaIiwiMjIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDAxIl0sImNvbHVtbnMiOlsiY3JlYXRlZF9hdCIsImlkIl19
                      previous:
        "400":
          description: Legacy pagination limit exceeded
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: legacy_offset_pagination_limit_exceeded
                  detail:
                    type: string
                    example: "Page 51 exceeds the maximum supported legacy offset page (50). Use
                      cursor pagination with limit, after, and before."
                  requested_page:
                    type: integer
                    example: 51
                  requested_per_page:
                    type: integer
                    example: 100
                  requested_window:
                    type: integer
                    example: 5100
                  max_page:
                    type: integer
                    example: 50
                  max_window:
                    type: integer
                    example: 5000
                  cursor_example:
                    type: string
                    example: "https://api.kapso.ai/platform/v1/whatsapp/contacts?limit=100"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    post:
      tags:
        - Contacts
      summary: Create contact
      description: Create a new WhatsApp contact.
      operationId: createWhatsappContactPlatform
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - contact
              properties:
                contact:
                  type: object
                  required:
                    - wa_id
                  properties:
                    wa_id:
                      type: string
                      description: WhatsApp ID (phone number in E.164 format)
                      example: "+15551234567"
                    profile_name:
                      type: string
                    display_name:
                      type: string
                    customer_id:
                      type: string
                      format: uuid
                    metadata:
                      type: object
                      additionalProperties: true
      responses:
        "201":
          description: Contact created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappContactResponse"
        "422":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /whatsapp/contacts/{identifier}:
    parameters:
      - name: identifier
        in: path
        required: true
        description: |
          Contact UUID, WhatsApp phone number, business-scoped user ID
          (`US.13491208655302741918`), or parent business-scoped user ID
          (`US.ENT.11815799212886844830`).

          BSUIDs are matched verbatim, including the country prefix and period.
          A parent BSUID can be shared by several contacts in a project; the most
          recently created match is returned.
        schema:
          type: string
        example: "123e4567-e89b-12d3-a456-426614174000"

    get:
      tags:
        - Contacts
      summary: Get contact
      description: Retrieve a WhatsApp contact by UUID, phone number, or business-scoped user ID.
      operationId: getWhatsappContactPlatform
      responses:
        "200":
          description: Contact retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappContactResponse"
        "404":
          description: Contact not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    patch:
      tags:
        - Contacts
      summary: Update contact
      description: Update a WhatsApp contact's profile or metadata.
      operationId: updateWhatsappContactPlatform
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                contact:
                  type: object
                  properties:
                    wa_id:
                      type: string
                    profile_name:
                      type: string
                    display_name:
                      type: string
                    customer_id:
                      type: string
                      format: uuid
                    metadata:
                      type: object
                      additionalProperties: true
      responses:
        "200":
          description: Contact updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappContactResponse"
        "404":
          description: Contact not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    delete:
      tags:
        - Contacts
      summary: Erase contact
      description: |
        Permanently erase a WhatsApp contact and all associated data (conversations, messages, media).

        The erasure is processed asynchronously. A `204 No Content` response confirms the erasure job was queued.

        The `identifier` can be the contact UUID, the WhatsApp phone number (E.164 format), or a business-scoped user ID.
      operationId: eraseWhatsappContact
      responses:
        "204":
          description: Erasure queued successfully
        "404":
          description: Contact not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          description: Invalid phone number
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /whatsapp/contacts/{identifier}/marketing_preferences:
    parameters:
      - name: identifier
        in: path
        required: true
        description: Contact UUID or WhatsApp phone number
        schema:
          type: string
        example: "15551234567"
    get:
      tags:
        - Contacts
      summary: List marketing preferences
      description: |
        List the contact's marketing message preference on each of your WhatsApp
        numbers. One entry per number the contact has stopped or resumed
        marketing on; an empty list means the contact never changed their
        preference and is subscribed everywhere.

        Preferences are read-only. Only the contact can change them, inside
        WhatsApp. While a preference is `stopped`, marketing template sends to
        the contact on that number are refused with error code
        `marketing_preference_stopped`.
      operationId: listWhatsappContactMarketingPreferences
      responses:
        "200":
          description: Marketing preferences retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappContactMarketingPreferenceListResponse"
        "404":
          description: Contact not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /whatsapp/contacts/{identifier}/marketing_preferences/{phone_number_id}:
    parameters:
      - name: identifier
        in: path
        required: true
        description: Contact UUID or WhatsApp phone number
        schema:
          type: string
        example: "15551234567"
      - name: phone_number_id
        in: path
        required: true
        description: Meta phone number ID of one of your WhatsApp numbers
        schema:
          type: string
        example: "123456789012345"
    get:
      tags:
        - Contacts
      summary: Get marketing preference
      description: |
        Get the contact's marketing message preference on one WhatsApp number.

        A 404 with `"Marketing preference not found"` means the contact has no
        recorded preference on that number — subscribed. Check the error string
        before treating a 404 as subscribed: a mistyped identifier returns
        `"WhatsApp contact not found"` and an unknown number returns
        `"WhatsApp configuration not found"`.
      operationId: getWhatsappContactMarketingPreference
      responses:
        "200":
          description: Marketing preference retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappContactMarketingPreferenceResponse"
        "404":
          description: Contact, configuration, or preference not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /users:
    get:
      tags:
        - Users
      summary: List project users
      description: Returns all users who are members of your project.
      operationId: listUsers
      parameters:
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsersResponse"
              examples:
                users:
                  value:
                    data:
                      - id: 1
                        user_id: "f1e2d3c4-b5a6-9870-fedc-ba0987654321"
                        email: "owner@example.com"
                        name: "Owner User"
                        role: "owner"
                      - id: 2
                        user_id: "e2d3c4b5-a697-8076-edcb-a09876543210"
                        email: "member@example.com"
                        name: "Member User"
                        role: "member"
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 2
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /webhook_deliveries:
    get:
      tags:
        - Webhook Deliveries
      summary: List webhook deliveries
      description: Returns webhook delivery attempts for your project, most recent first.
      operationId: listWebhookDeliveries
      parameters:
        - name: status
          in: query
          description: Filter by delivery status
          schema:
            type: string
            enum:
              - pending
              - delivered
              - failed
        - name: event
          in: query
          description: Filter by event type
          schema:
            type: string
        - name: webhook_id
          in: query
          description: Filter by webhook ID
          schema:
            type: string
            format: uuid
        - name: errors_only
          in: query
          description: Only return failed deliveries
          schema:
            type: boolean
            default: false
        - name: period
          in: query
          description: Time period filter
          schema:
            type: string
            enum:
              - 24h
              - 7d
              - 30d
            default: 24h
        - name: limit
          in: query
          description: Maximum number of results per cursor-paginated page (default 20,
            max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: before
          in: query
          description: Cursor for the previous page (Base64 encoded).
          schema:
            type: string
        - name: after
          in: query
          description: Cursor for the next page (Base64 encoded).
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /event-definitions:
    get:
      tags:
        - Events
      summary: List project event definitions
      description: |
        Returns the event definitions registered for the project associated with
        your API key. Definitions describe event names, meanings, and property
        schemas. Emitting a new event through `/events` can create a minimal
        definition automatically, but use this endpoint when you want to manage
        definition metadata directly.
      operationId: listProjectEventDefinitions
      parameters:
        - name: include_archived
          in: query
          description: Include archived event definitions in the response. Archived definitions are hidden by default.
          required: false
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventDefinitionListResponse"
              examples:
                definitions:
                  summary: Project event definitions found
                  value:
                    data:
                      - id: "880e8400-e29b-41d4-a716-446655440003"
                        name: conversation.csat_scored
                        description: Customer satisfaction score for a conversation
                        property_schema:
                          score:
                            type: number
                          reason:
                            type: string
                        last_seen_at: "2026-06-27T14:30:00Z"
                        created_at: "2026-06-20T10:00:00Z"
                        updated_at: "2026-06-20T10:00:00Z"
                        archived_at: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"

    post:
      tags:
        - Events
      summary: Create or update a project event definition
      description: |
        Creates a definition for an event type. If a definition with the same
        name already exists, Kapso updates its editable metadata and returns it.
        If the existing definition is archived, this request restores it.
        Use `/events` when you want to emit an actual event occurrence.
      operationId: createProjectEventDefinition
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectEventDefinitionCreateRequest"
            examples:
              csat_definition:
                summary: Conversation CSAT definition
                value:
                  name: conversation.csat_scored
                  description: Customer satisfaction score for a conversation
                  property_schema:
                    score:
                      type: number
                    reason:
                      type: string
      responses:
        "200":
          description: Existing definition updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventDefinitionResponse"
        "201":
          description: Definition created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventDefinitionResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "402":
          $ref: "#/components/responses/PaymentRequiredError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /event-definitions/{id}:
    get:
      tags:
        - Events
      summary: Retrieve a project event definition
      operationId: getProjectEventDefinition
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventDefinitionResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

    patch:
      tags:
        - Events
      summary: Update a project event definition
      description: |
        Updates editable metadata for an event definition. Event names cannot be
        changed after events have been recorded for the definition. Set `archived`
        to true to hide the definition from event tools and workflow selectors while
        retaining historical events; set it to false to restore it.
      operationId: updateProjectEventDefinition
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectEventDefinitionUpdateRequest"
            examples:
              update_schema:
                summary: Update event schema
                value:
                  description: Customer satisfaction score for a conversation
                  property_schema:
                    score:
                      type: number
                    resolved:
                      type: boolean
                  archived: false
      responses:
        "200":
          description: Definition updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventDefinitionResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "402":
          $ref: "#/components/responses/PaymentRequiredError"
        "422":
          $ref: "#/components/responses/ValidationError"

    delete:
      tags:
        - Events
      summary: Delete a project event definition and its events
      description: |
        Permanently deletes the event definition and all recorded event emissions
        associated with it. This action cannot be undone. The endpoint returns
        `202 Accepted` while a background job performs the deletion; new events
        with the same name are rejected while deletion is pending. To retain
        historical events while hiding the definition from new tools and workflow
        selectors, archive it with the PATCH endpoint instead.
      operationId: deleteProjectEventDefinition
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "202":
          description: Deletion queued for the definition and associated event emissions
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /events:
    get:
      tags:
        - Events
      summary: List project events
      description: |
        Returns project-scoped events for the project associated with your API key,
        newest first. Use filters to narrow by event name, linked WhatsApp
        conversation, or occurrence time.

        Use `limit`, `after`, and `before` for cursor pagination. Cursor-paginated
        responses include `paging`; legacy offset responses include `meta`.
      operationId: listProjectEvents
      parameters:
        - name: name
          in: query
          description: Filter by event name.
          schema:
            type: string
            maxLength: 128
            pattern: '^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$'
          example: conversation.csat_scored
        - name: conversation_id
          in: query
          description: Filter by linked WhatsApp conversation ID.
          schema:
            type: string
            format: uuid
        - name: occurred_after
          in: query
          description: Only return events at or after this timestamp.
          schema:
            type: string
            format: date-time
        - name: occurred_before
          in: query
          description: Only return events at or before this timestamp.
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Maximum number of results per cursor-paginated page (default 20, max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: before
          in: query
          description: Cursor for the previous page (Base64 encoded).
          schema:
            type: string
        - name: after
          in: query
          description: Cursor for the next page (Base64 encoded).
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventListResponse"
              examples:
                events:
                  summary: Project events found
                  value:
                    data:
                      - id: "990e8400-e29b-41d4-a716-446655440004"
                        name: conversation.csat_scored
                        occurred_at: "2026-06-27T14:30:00Z"
                        conversation_id: "770e8400-e29b-41d4-a716-446655440002"
                        properties:
                          score: 4
                          reason: Issue resolved in one reply
                    paging:
                      cursors:
                        before: null
                        after: null
                      next: null
                      previous: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "422":
          $ref: "#/components/responses/ValidationError"

    post:
      tags:
        - Events
      summary: Emit a project event
      description: |
        Stores one timestamped project event. Event names must be lowercase and
        may contain optional dot-separated segments; each segment starts with a
        lowercase letter and may contain lowercase letters, numbers, and
        underscores. `conversation_id` is optional; include it when the
        event belongs to a WhatsApp conversation.
      operationId: createProjectEvent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectEventCreateRequest"
            examples:
              csat_score:
                summary: Conversation CSAT score
                value:
                  name: conversation.csat_scored
                  occurred_at: "2026-06-27T14:30:00Z"
                  conversation_id: "770e8400-e29b-41d4-a716-446655440002"
                  properties:
                    score: 4
                    reason: Issue resolved in one reply
                    source: workflow
      responses:
        "201":
          description: Event created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectEventResponse"
              examples:
                created:
                  summary: Created project event
                  value:
                    data:
                      id: "990e8400-e29b-41d4-a716-446655440004"
                      name: conversation.csat_scored
                      occurred_at: "2026-06-27T14:30:00Z"
                      conversation_id: "770e8400-e29b-41d4-a716-446655440002"
                      properties:
                        score: 4
                        reason: Issue resolved in one reply
                        source: workflow
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "402":
          $ref: "#/components/responses/PaymentRequiredError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api_logs:
    get:
      tags:
        - API Logs
      summary: List API logs
      description: Returns logs of external API calls made by your project, most
        recent first.
      operationId: listApiLogs
      parameters:
        - name: endpoint
          in: query
          description: Filter by endpoint (partial match)
          schema:
            type: string
        - name: status_code
          in: query
          description: Filter by HTTP status code
          schema:
            type: integer
        - name: errors_only
          in: query
          description: Only return logs with status >= 400
          schema:
            type: boolean
            default: false
        - name: period
          in: query
          description: Time period filter
          schema:
            type: string
            enum:
              - 24h
              - 7d
              - 30d
            default: 24h
        - name: limit
          in: query
          description: Maximum number of results per cursor-paginated page (default 20,
            max 100).
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: before
          in: query
          description: Cursor for the previous page (Base64 encoded).
          schema:
            type: string
        - name: after
          in: query
          description: Cursor for the next page (Base64 encoded).
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExternalApiLogListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /log_search:
    get:
      tags:
        - Log Search
      summary: Search log events
      description: Search log events for the project associated with your API key. Returns the same event payload shape used by the logs UI. Use POST when sending filters.
      operationId: searchLogs
      parameters:
        - name: query
          in: query
          description: Free-text search query. Blank queries are allowed for browsing narrowed by source, filters, or problems_only.
          schema:
            type: string
        - name: period
          in: query
          description: Time window to search. Use context with around to fetch nearby events.
          schema:
            type: string
            enum:
              - 24h
              - 7d
              - context
            default: 24h
        - name: source
          in: query
          description: Log source to search. Use all to search every source.
          schema:
            type: string
            enum:
              - all
              - external_api_log
              - whatsapp_webhook_event
              - flow_event
              - webhook_delivery
            default: all
        - name: problems_only
          in: query
          description: Only return problem or error events.
          schema:
            type: boolean
            default: false
        - name: errors_only
          in: query
          description: Alias for problems_only.
          deprecated: true
          schema:
            type: boolean
            default: false
        - name: limit
          in: query
          description: Maximum number of events returned.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: cursor
          in: query
          description: Cursor returned as pagination.next_cursor from a previous response.
          schema:
            type: string
        - name: around
          in: query
          description: Center a context search around this timestamp. Use with period=context.
          schema:
            type: string
            format: date-time
        - name: highlight_event_id
          in: query
          description: Include this event in the page when available.
          schema:
            type: string
        - name: highlight_resource_id
          in: query
          description: Include an event for this resource in the page when available.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LogSearchResponse"
              examples:
                found:
                  summary: Log events found
                  value:
                    data:
                      available: true
                      source: external_api_log
                      window:
                        since: "2026-07-06T12:00:00Z"
                        until: "2026-07-07T12:00:00Z"
                      events:
                        - id: evt_1
                          source: external_api_log
                          timestamp: "2026-07-07T11:59:00Z"
                          message: POST /v1/messages
                          http_method: POST
                          endpoint: /v1/messages
                          api_key_name: Production key
                          response_status: 422
                          payload:
                            error_message: Graph API failed
                      pagination:
                        has_more: false
                        next_cursor: null
                unavailable:
                  summary: Log search unavailable
                  value:
                    data:
                      available: false
                      events: []
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "422":
          description: Invalid filters or search failure
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ErrorResponse"
                  - $ref: "#/components/schemas/LogSearchResponse"
              examples:
                invalid_filters:
                  summary: Invalid filters
                  value:
                    error: filters must be an array of {key, value} entries or an object
                search_failed:
                  summary: Search failure
                  value:
                    data:
                      available: true
                      events: []
                      error: log search failed
                      code: elastic_search_failed
    post:
      tags:
        - Log Search
      summary: Search log events with filters
      description: Search log events with the same project-scoped data and filter catalog used by the logs UI.
      operationId: searchLogsWithFilters
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LogSearchRequest"
            examples:
              workflow_execution:
                summary: Search a workflow execution
                value:
                  query: flow-execution-1
                  period: 7d
                  source: flow_event
                  filters:
                    flow_execution_id: flow-execution-1
                    event_type: step_failed
              api_errors:
                summary: Search failed API calls
                value:
                  query: /messages
                  period: 24h
                  source: external_api_log
                  problems_only: true
                  filters:
                    endpoint_contains: /messages
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LogSearchResponse"
              examples:
                found:
                  summary: Log events found
                  value:
                    data:
                      available: true
                      source: flow_event
                      window:
                        since: "2026-07-01T12:00:00Z"
                        until: "2026-07-07T12:00:00Z"
                      events:
                        - id: evt_2
                          source: flow_event
                          timestamp: "2026-07-07T11:58:00Z"
                          message: Flow Event step_failed agent_1
                          flow_execution_id: flow-execution-1
                          flow_event_id: flow-event-1
                          step_identifier: agent_1
                          step_type: FlowAgentStep
                          error_message: Tool call failed
                      pagination:
                        has_more: false
                        next_cursor: null
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "422":
          description: Invalid filters or search failure
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ErrorResponse"
                  - $ref: "#/components/schemas/LogSearchResponse"

  /log_search/catalog:
    get:
      tags:
        - Log Search
      summary: Get log search catalog
      description: Returns the log source list, supported filter keys, and detail fields for the project associated with your API key.
      operationId: getLogSearchCatalog
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LogSearchCatalogResponse"
              examples:
                catalog:
                  summary: Available catalog
                  value:
                    data:
                      available: true
                      catalog:
                        sources:
                          - value: all
                            label: All sources
                            color: transparent
                            aliases:
                              - all
                          - value: external_api_log
                            label: API
                            color: "#2563eb"
                            aliases:
                              - api
                              - external_api_log
                        filter_suggestions:
                          - key: endpoint
                            label: "endpoint:"
                            description: API endpoint contains text
                            param_key: endpoint_contains
                            sources:
                              - external_api_log
                        detail_fields:
                          - flow_execution_id
                          - event_type
                          - endpoint
                          - response_status
                unavailable:
                  summary: Log search unavailable
                  value:
                    data:
                      available: false
                      events: []
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /provider_models:
    get:
      tags:
        - Provider Models
      summary: List provider models
      description: Returns available AI provider models.
      operationId: listProviderModels
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderModelListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"

  /inbox_embeds:
    get:
      tags:
        - Inbox Embeds
      summary: List inbox embeds
      description: Returns inbox embed access links for your project, most recent
        first. Raw tokens and embed URLs are not returned after creation.
      operationId: listInboxEmbeds
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - active
              - revoked
        - name: scope_type
          in: query
          schema:
            type: string
            enum:
              - project
              - customer
              - phone_number
        - name: scope_id
          in: query
          description: Customer UUID for `customer`, WhatsApp `phone_number_id` for
            `phone_number`
          schema:
            type: string
        - name: assigned_user_id
          in: query
          schema:
            type: string
            format: uuid
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InboxEmbedListResponse"
              examples:
                phone_number:
                  value:
                    data:
                      - id: 550e8400-e29b-41d4-a716-446655440000
                        name: Support embed
                        scope_type: phone_number
                        scope_id: "1234567890"
                        scope_name: "+1 415 555 1234"
                        assigned_user_id: null
                        assigned_user_name: null
                        status: active
                        allowed_origins:
                          - https://app.example.com
                        default_mode: system
                        language: en
                        expires_at: null
                        last_used_at: null
                        created_at: "2025-01-15T10:00:00Z"
                        updated_at: "2025-01-15T10:00:00Z"
                    meta:
                      page: 1
                      per_page: 20
                      total_pages: 1
                      total_count: 1
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "422":
          $ref: "#/components/responses/ValidationError"

    post:
      tags:
        - Inbox Embeds
      summary: Create inbox embed
      description: |
        Create an embeddable inbox access link.

        The response includes `token` and `embed_url` once. Store the embed URL when you create it; list, get, and update responses omit the secret.
      operationId: createInboxEmbed
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InboxEmbedCreateRequest"
            examples:
              project:
                value:
                  inbox_embed:
                    name: Project inbox
                    scope_type: project
                    allowed_origins:
                      - https://app.example.com
                    language: es
              customer:
                value:
                  inbox_embed:
                    name: Customer inbox
                    scope_type: customer
                    scope_id: 550e8400-e29b-41d4-a716-446655440000
              phone_number:
                value:
                  inbox_embed:
                    name: Support number inbox
                    scope_type: phone_number
                    scope_id: "1234567890"
                    default_mode: system
                    language: es
      responses:
        "201":
          description: Inbox embed created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InboxEmbedCreatedResponse"
              examples:
                created:
                  value:
                    data:
                      id: 550e8400-e29b-41d4-a716-446655440000
                      name: Support number inbox
                      scope_type: phone_number
                      scope_id: "1234567890"
                      scope_name: "+1 415 555 1234"
                      assigned_user_id: null
                      assigned_user_name: null
                      status: active
                      allowed_origins:
                        - https://app.example.com
                      default_mode: system
                      language: es
                      expires_at: null
                      last_used_at: null
                      created_at: "2025-01-15T10:00:00Z"
                      updated_at: "2025-01-15T10:00:00Z"
                      token: inbox_token_example
                      embed_url: https://inbox.kapso.ai/embed/inbox_token_example
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /inbox_embeds/{inbox_embed_id}:
    parameters:
      - name: inbox_embed_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - Inbox Embeds
      summary: Get inbox embed
      description: Returns an inbox embed without the raw token or embed URL.
      operationId: getInboxEmbed
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InboxEmbedResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

    patch:
      tags:
        - Inbox Embeds
      summary: Update inbox embed
      description: Updates mutable settings. Scope cannot be changed; create a new
        embed for a different scope.
      operationId: updateInboxEmbed
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InboxEmbedUpdateRequest"
            examples:
              update_origins:
                value:
                  inbox_embed:
                    allowed_origins:
                      - https://app.example.com
                    default_mode: dark
                    language: es
      responses:
        "200":
          description: Inbox embed updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InboxEmbedResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

    delete:
      tags:
        - Inbox Embeds
      summary: Revoke inbox embed
      description: Revokes the embed token immediately.
      operationId: deleteInboxEmbed
      responses:
        "204":
          description: Inbox embed revoked
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  # WhatsApp Flows endpoints
  /whatsapp/flows:
    get:
      tags:
        - WhatsApp Flows
      summary: List flows
      description: Returns WhatsApp flows in your project, most recent first.
      operationId: listWhatsappFlows
      parameters:
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum:
              - draft
              - published
        - name: business_account_id
          in: query
          description: Filter by WABA ID
          schema:
            type: string
        - name: phone_number_id
          in: query
          description: Filter by phone number
          schema:
            type: string
        - name: name_contains
          in: query
          description: Filter by name substring
          schema:
            type: string
        - name: created_after
          in: query
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          schema:
            type: string
            format: date-time
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"

    post:
      tags:
        - WhatsApp Flows
      summary: Create flow
      description: Create a new WhatsApp Flow. By default creates a draft flow with a
        basic welcome screen.
      operationId: createWhatsappFlow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappFlowCreateRequest"
      responses:
        "201":
          description: Flow created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowResponse"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/flows/{flow_id}:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - WhatsApp Flows
      summary: Get flow
      operationId: getWhatsappFlow
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/flows/{flow_id}/publish:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    post:
      tags:
        - WhatsApp Flows
      summary: Publish flow
      description: Publish a draft flow to make it available for use. Published flows
        cannot be unpublished.
      operationId: publishWhatsappFlow
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                phone_number_id:
                  type: string
                  description: Override phone number (optional)
      responses:
        "200":
          description: Flow published
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: Cannot publish
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /whatsapp/flows/{flow_id}/setup_encryption:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    post:
      tags:
        - WhatsApp Flows
      summary: Setup encryption
      description: Set up flows encryption for the WABA associated with this flow.
        Required for data endpoints.
      operationId: setupWhatsappFlowEncryption
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                phone_number_id:
                  type: string
                  description: Override phone number (optional)
      responses:
        "200":
          description: Encryption configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowEncryptionSetupResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/flows/{flow_id}/versions:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - WhatsApp Flows
      summary: List flow versions
      description: Returns versions for a flow, most recent first.
      operationId: listWhatsappFlowVersions
      parameters:
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: Success
          headers:
            X-Total:
              schema:
                type: integer
            X-Total-Pages:
              schema:
                type: integer
            X-Per-Page:
              schema:
                type: integer
            X-Page:
              schema:
                type: integer
            Link:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowVersionListResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

    post:
      tags:
        - WhatsApp Flows
      summary: Create flow version
      description: Upload new flow JSON to create a new version. Syncs with Meta's API.
      operationId: createWhatsappFlowVersion
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappFlowVersionCreateRequest"
      responses:
        "201":
          description: Version created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowVersionDetailResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: Validation failed
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  validation_errors:
                    type: array
                    items:
                      type: object
                      additionalProperties: true

  /whatsapp/flows/{flow_id}/versions/{version_id}:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: version_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - WhatsApp Flows
      summary: Get flow version
      description: Returns version details including the flow JSON.
      operationId: getWhatsappFlowVersion
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowVersionDetailResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"

  /whatsapp/flows/{flow_id}/data_endpoint:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - WhatsApp Flows
      summary: Get data endpoint
      description: Get the data endpoint function configuration for a flow.
      operationId: getWhatsappFlowDataEndpoint
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowDataEndpointResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: No data endpoint configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    post:
      tags:
        - WhatsApp Flows
      summary: Create/update data endpoint
      description: Create or update the data endpoint function code. The function
        handles dynamic data for your flow.
      operationId: createWhatsappFlowDataEndpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhatsappFlowDataEndpointCreateRequest"
      responses:
        "200":
          description: Data endpoint saved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowDataEndpointResponse"
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/flows/{flow_id}/data_endpoint/deploy:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    post:
      tags:
        - WhatsApp Flows
      summary: Deploy data endpoint
      description: Deploy the data endpoint function to Cloudflare Workers.
      operationId: deployWhatsappFlowDataEndpoint
      responses:
        "200":
          description: Deployed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowDataEndpointResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          $ref: "#/components/responses/ValidationError"

  /whatsapp/flows/{flow_id}/data_endpoint/register:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    post:
      tags:
        - WhatsApp Flows
      summary: Register data endpoint with Meta
      description: Register the deployed data endpoint URL with Meta. Requires flows
        encryption to be configured.
      operationId: registerWhatsappFlowDataEndpoint
      responses:
        "200":
          description: Registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowDataEndpointRegisterResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: Encryption not configured or other error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /whatsapp/flows/{flow_id}/function_logs:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - WhatsApp Flows
      summary: Get function logs
      description: Get logs from the data endpoint function.
      operationId: getWhatsappFlowFunctionLogs
      parameters:
        - name: limit
          in: query
          description: Max logs to return (default 20, max 50)
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowFunctionLogsResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: No data endpoint configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /whatsapp/flows/{flow_id}/function_invocations:
    parameters:
      - name: flow_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags:
        - WhatsApp Flows
      summary: Get function invocations
      description: Get recent invocations of the data endpoint function.
      operationId: getWhatsappFlowFunctionInvocations
      parameters:
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum:
              - success
              - failed
        - name: limit
          in: query
          description: Max invocations to return (default 10, max 20)
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 10
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsappFlowFunctionInvocationsResponse"
        "401":
          $ref: "#/components/responses/UnauthorizedError"
        "404":
          $ref: "#/components/responses/NotFoundError"
        "422":
          description: No data endpoint configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
