openapi: 3.0.3
info:
  title: WhatsApp Multi-Instance API
  description: |
    RESTful API for managing multiple WhatsApp instances.
    
    ## Authentication
    All endpoints require authentication when `API_KEY` is configured.
    
    Use one of:
    - Header: `X-API-Key: your-api-key`
    - Header: `Authorization: Bearer your-api-key`
    
    ## WebSocket
    Real-time updates available at `ws://host/ws`
    
    Events: `init`, `instance_created`, `instance_status`, `instance_updated`, `instance_deleted`, `message`, `log`
  version: 1.0.0
  contact:
    name: API Support
  license:
    name: MIT

servers:
  - url: http://localhost:3000
    description: Local development server

tags:
  - name: Instances
    description: Instance management (CRUD)
  - name: Connection
    description: WhatsApp connection control
  - name: Messaging
    description: Send messages
  - name: Webhook
    description: Inbound webhook configuration
  - name: Behavior
    description: Behavior profiles, typing simulation, delays, and phone notification preferences
  - name: Anti-Ban
    description: Rate limiting and anti-ban settings
  - name: Logs
    description: Activity logs
  - name: System
    description: Health and status

security:
  - ApiKeyAuth: []
  - BearerAuth: []

paths:
  # ==========================================
  # INSTANCE MANAGEMENT
  # ==========================================
  /api/instances:
    get:
      tags: [Instances]
      summary: List all instances
      description: Get a list of all WhatsApp instances with their current status
      responses:
        '200':
          description: List of instances
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  count:
                    type: integer
                    example: 2
                  instances:
                    type: array
                    items:
                      $ref: '#/components/schemas/Instance'
    
    post:
      tags: [Instances]
      summary: Create new instance
      description: Create a new WhatsApp instance
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
                  description: Custom instance ID (auto-generated if not provided)
                  example: my_custom_id
                name:
                  type: string
                  description: Display name for the instance
                  example: Customer Support
                webhookUrl:
                  type: string
                  format: uri
                  description: URL to forward incoming messages to
                  example: https://your-api.com/webhook
                behaviorSettings:
                  $ref: '#/components/schemas/BehaviorSettings'
                antiBanSettings:
                  $ref: '#/components/schemas/AntiBanSettings'
      responses:
        '201':
          description: Instance created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instance:
                    $ref: '#/components/schemas/Instance'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/instances/{instanceId}:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    
    get:
      tags: [Instances]
      summary: Get instance details
      description: Get detailed information about a specific instance
      responses:
        '200':
          description: Instance details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instance:
                    $ref: '#/components/schemas/Instance'
        '404':
          $ref: '#/components/responses/NotFound'
    
    put:
      tags: [Instances]
      summary: Update instance
      description: Update instance settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: Updated Name
                webhookUrl:
                  type: string
                  format: uri
                  example: https://new-webhook.com/endpoint
                behaviorSettings:
                  $ref: '#/components/schemas/BehaviorSettings'
                antiBanSettings:
                  $ref: '#/components/schemas/AntiBanSettings'
      responses:
        '200':
          description: Instance updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instance:
                    $ref: '#/components/schemas/Instance'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    
    delete:
      tags: [Instances]
      summary: Delete instance
      description: Delete an instance and all its data
      responses:
        '200':
          description: Instance deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Instance wa_xxx deleted
        '400':
          $ref: '#/components/responses/BadRequest'

  # ==========================================
  # CONNECTION
  # ==========================================
  /api/instances/{instanceId}/connect:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    post:
      tags: [Connection]
      summary: Connect instance
      description: Start WhatsApp connection. Poll `/qr` for QR code to scan.
      responses:
        '200':
          description: Connection started
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Connection started
                  instance:
                    $ref: '#/components/schemas/Instance'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/instances/{instanceId}/disconnect:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    post:
      tags: [Connection]
      summary: Disconnect instance
      description: Disconnect WhatsApp locally and keep credentials by default. Pass `revoke: true` to log out from WhatsApp and invalidate the linked session.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                revoke:
                  type: boolean
                  default: false
                  description: Log out from WhatsApp and revoke the linked device session. Reconnecting will require QR scan or pairing again.
      responses:
        '200':
          description: Disconnected
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Disconnected
                  instance:
                    $ref: '#/components/schemas/Instance'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/instances/{instanceId}/clear-auth:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    post:
      tags: [Connection]
      summary: Clear auth
      description: Stop any active connection attempt and delete local saved credentials. Requires a new QR scan or pairing code.
      responses:
        '200':
          description: Auth cleared
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Auth cleared
                  instance:
                    $ref: '#/components/schemas/Instance'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/instances/{instanceId}/qr:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    get:
      tags: [Connection]
      summary: Get QR code
      description: Get QR code for scanning (only available when status is 'connecting')
      responses:
        '200':
          description: QR code response
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  status:
                    type: string
                    enum: [disconnected, connecting, connected]
                    example: connecting
                  qrCode:
                    type: string
                    nullable: true
                    description: Base64 data URL of QR code image
                    example: data:image/png;base64,iVBORw0KGgo...
                  phone:
                    type: string
                    nullable: true
                    description: Connected phone number (when status is 'connected')
                  message:
                    type: string
                    example: QR code not yet generated. Call /connect first.
        '404':
          $ref: '#/components/responses/NotFound'

  # ==========================================
  # MESSAGING
  # ==========================================
  /api/instances/{instanceId}/send:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    post:
      tags: [Messaging]
      summary: Send message via instance
      description: |
        Send a WhatsApp message through a specific instance.

        Plain text remains backward compatible. Link previews use Baileys text messages with URL preview support.
        Quick reply buttons and URL/CTA buttons default to a clear text fallback because this Baileys v7 build does not expose native interactive sends through the public `sendMessage` content type. `buttonMode: legacy` can attempt raw legacy quick reply buttons, but current WhatsApp MD clients may ignore or reject them.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, message]
              properties:
                to:
                  type: string
                  description: Phone number (with country code, no + or spaces)
                  example: '60123456789'
                message:
                  type: string
                  description: Message text to send. Required unless link, ctaUrl, or buttons are supplied.
                  example: Hello! How can I help you today?
                link:
                  $ref: '#/components/schemas/SendLink'
                ctaUrl:
                  $ref: '#/components/schemas/CtaUrl'
                buttons:
                  $ref: '#/components/schemas/QuickReplyButtons'
                buttonMode:
                  $ref: '#/components/schemas/ButtonMode'
                linkPreview:
                  type: boolean
                  description: Enable Baileys URL preview generation for text messages that contain a URL
                  default: true
                  example: true
                footer:
                  type: string
                  description: Footer text for legacy button attempts
                  example: Wasup API
                behaviorProfile:
                  $ref: '#/components/schemas/BehaviorProfile'
                typingSimulation:
                  type: boolean
                  description: Override instance setting for this message
                  example: true
                delayEnabled:
                  type: boolean
                  description: Override instance setting for this message
                  example: true
                phoneNotificationsEnabled:
                  type: boolean
                  description: Override whether this message should favor phone push notifications before read/typing activity
                  example: false
                notificationGraceMs:
                  type: integer
                  minimum: 0
                  description: Override notification-first wait time in milliseconds
                  example: 2500
      responses:
        '200':
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  result:
                    $ref: '#/components/schemas/SendResult'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/instances/{instanceId}/send/interactive:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    post:
      tags: [Messaging]
      summary: Send interactive-capable message via instance
      description: |
        Alias for `/api/instances/{instanceId}/send` with examples focused on links, CTA links, and quick reply button fallbacks.

        Native WhatsApp URL/CTA buttons are not exposed by the installed Baileys v7 public send API, so URL buttons are sent as labeled links with previews. Quick replies are sent as numbered fallback text by default. Set `buttonMode: legacy` only for safe real-device testing of raw legacy quick reply buttons.
      requestBody:
        required: true
        content:
          application/json:
            examples:
              linkPreview:
                summary: Text with URL preview
                value:
                  to: '60123456789'
                  message: Here is the booking page.
                  link:
                    url: https://example.com/booking
                  linkPreview: true
              quickReplyFallback:
                summary: Quick reply fallback
                value:
                  to: '60123456789'
                  message: Choose an option and reply with the number.
                  buttons:
                    - id: pricing
                      text: Pricing
                    - id: demo
                      text: Demo
                    - id: human
                      text: Human
                  buttonMode: fallback
              ctaFallback:
                summary: CTA URL fallback
                value:
                  to: '60123456789'
                  message: Ready to book?
                  ctaUrl:
                    label: Book now
                    url: https://example.com/book
            schema:
              $ref: '#/components/schemas/InteractiveSendRequest'
      responses:
        '200':
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  result:
                    $ref: '#/components/schemas/SendResult'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/send:
    post:
      tags: [Messaging]
      summary: Send message (auto-select instance)
      description: Send a text, link, or fallback interactive message using a matching connected phone number or the first connected instance.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, message]
              properties:
                instanceId:
                  type: string
                  description: Instance ID (optional, auto-selects if not provided)
                  example: wa_xxx
                to:
                  type: string
                  description: Phone number (with country code)
                  example: '60123456789'
                message:
                  type: string
                  example: Hello!
                link:
                  $ref: '#/components/schemas/SendLink'
                ctaUrl:
                  $ref: '#/components/schemas/CtaUrl'
                buttons:
                  $ref: '#/components/schemas/QuickReplyButtons'
                buttonMode:
                  $ref: '#/components/schemas/ButtonMode'
                linkPreview:
                  type: boolean
                  default: true
                behaviorProfile:
                  $ref: '#/components/schemas/BehaviorProfile'
                typingSimulation:
                  type: boolean
                  description: Override instance setting for this message
                delayEnabled:
                  type: boolean
                  description: Override instance setting for this message
                phoneNotificationsEnabled:
                  type: boolean
                  description: Override whether this message should favor phone push notifications before read/typing activity
                notificationGraceMs:
                  type: integer
                  minimum: 0
                  description: Override notification-first wait time in milliseconds
      responses:
        '200':
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instanceId:
                    type: string
                    description: Instance that was used
                    example: wa_xxx
                  result:
                    $ref: '#/components/schemas/SendResult'
        '400':
          $ref: '#/components/responses/BadRequest'

  # ==========================================
  # BEHAVIOR SETTINGS
  # ==========================================
  /api/instances/{instanceId}/behavior:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    
    get:
      tags: [Behavior]
      summary: Get behavior settings
      description: Get behavior profile, typing simulation, delay, and phone notification settings for an instance.
      responses:
        '200':
          description: Behavior settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  behaviorSettings:
                    $ref: '#/components/schemas/BehaviorSettings'
        '404':
          $ref: '#/components/responses/NotFound'
    
    put:
      tags: [Behavior]
      summary: Update behavior settings
      description: |
        Update behavior settings for an instance.

        Profiles:
        - `bot-native`: full anti-ban behavior with typing, read timing, and natural delays.
        - `notification-balanced`: lets phone push notifications arrive first, then allows typing.
        - `notification-max`: prioritizes phone notifications and disables typing simulation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BehaviorSettings'
      responses:
        '200':
          description: Settings updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  behaviorSettings:
                    $ref: '#/components/schemas/BehaviorSettings'
                  message:
                    type: string
                    example: Behavior settings updated
        '400':
          $ref: '#/components/responses/BadRequest'

  # ==========================================
  # ANTI-BAN
  # ==========================================
  /api/instances/{instanceId}/anti-ban:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    
    get:
      tags: [Anti-Ban]
      summary: Get anti-ban status
      description: Get current anti-ban settings and usage statistics
      responses:
        '200':
          description: Anti-ban status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  settings:
                    $ref: '#/components/schemas/AntiBanSettings'
                  health:
                    $ref: '#/components/schemas/AntiBanHealth'
        '404':
          $ref: '#/components/responses/NotFound'
    
    put:
      tags: [Anti-Ban]
      summary: Update anti-ban settings
      description: Update rate limits using preset or custom values
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                preset:
                  type: string
                  enum: [new, balanced, higher, custom]
                  description: Use a preset configuration
                  example: balanced
                messagesPerHour:
                  type: integer
                  description: Max messages per hour (for custom preset)
                  example: 50
                messagesPerDay:
                  type: integer
                  description: Max messages per day (for custom preset)
                  example: 300
                uniqueChatsPerHour:
                  type: integer
                  description: Max unique chats per hour (for custom preset)
                  example: 25
                uniqueChatsPerDay:
                  type: integer
                  description: Max unique chats per day (for custom preset)
                  example: 100
      responses:
        '200':
          description: Settings updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  settings:
                    $ref: '#/components/schemas/AntiBanSettings'
                  health:
                    $ref: '#/components/schemas/AntiBanHealth'
        '400':
          $ref: '#/components/responses/BadRequest'

  # ==========================================
  # WEBHOOK CONFIGURATION
  # ==========================================
  /api/webhook:
    get:
      tags: [Webhook]
      summary: Get global default webhook
      description: Get the global default webhook URL (from environment)
      responses:
        '200':
          description: Webhook configuration
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  defaultWebhookUrl:
                    type: string
                    nullable: true
                    example: https://n8n.example.com/webhook/whatsapp
                  message:
                    type: string

  /api/instances/{instanceId}/webhook:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    
    get:
      tags: [Webhook]
      summary: Get instance webhook
      description: Get webhook configuration for a specific instance
      responses:
        '200':
          description: Webhook configuration
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instanceWebhookUrl:
                    type: string
                    nullable: true
                    description: Instance-specific webhook (null if using global)
                  effectiveWebhookUrl:
                    type: string
                    nullable: true
                    description: The actual webhook URL that will be used
                  usingGlobalDefault:
                    type: boolean
                    description: Whether using the global default webhook
        '404':
          $ref: '#/components/responses/NotFound'
    
    put:
      tags: [Webhook]
      summary: Set instance webhook
      description: Set a custom webhook URL for this instance, or clear to use global default
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                webhookUrl:
                  type: string
                  nullable: true
                  description: Webhook URL or null/empty to use global default
                  example: https://your-api.com/webhook/instance-1
      responses:
        '200':
          description: Webhook updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instanceWebhookUrl:
                    type: string
                    nullable: true
                  effectiveWebhookUrl:
                    type: string
                    nullable: true
                  usingGlobalDefault:
                    type: boolean
                  message:
                    type: string
        '400':
          $ref: '#/components/responses/BadRequest'

  # ==========================================
  # LOGS
  # ==========================================
  /api/instances/{instanceId}/logs:
    parameters:
      - $ref: '#/components/parameters/instanceId'
    get:
      tags: [Logs]
      summary: Get activity logs
      description: Get recent activity logs for instance
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
          description: Number of log entries to return
      responses:
        '200':
          description: Activity logs
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  logs:
                    type: array
                    items:
                      $ref: '#/components/schemas/LogEntry'
        '404':
          $ref: '#/components/responses/NotFound'

  # ==========================================
  # SYSTEM
  # ==========================================
  /api/health:
    get:
      tags: [System]
      summary: Health check
      description: Check server health and instance counts
      security: []
      responses:
        '200':
          description: Server healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  uptime:
                    type: number
                    description: Server uptime in seconds
                    example: 3600.5
                  instances:
                    type: object
                    properties:
                      total:
                        type: integer
                        example: 3
                      connected:
                        type: integer
                        example: 2

  /api/status:
    get:
      tags: [System]
      summary: System status
      description: Get overall system status with all instances
      responses:
        '200':
          description: System status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instanceCount:
                    type: integer
                    example: 2
                  instances:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          example: wa_xxx
                        name:
                          type: string
                          example: Customer Support
                        status:
                          type: string
                          enum: [disconnected, connecting, connected]
                        phone:
                          type: string
                          nullable: true
                          example: '60123456789'

  /api/generate-api-key:
    post:
      tags: [System]
      summary: Generate API key
      description: Generate a new random API key (for reference only, add to .env manually)
      responses:
        '200':
          description: API key generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  apiKey:
                    type: string
                    example: a1b2c3d4e5f6...
                  message:
                    type: string
                    example: Add this to your .env file as API_KEY=<key>

# ==========================================
# COMPONENTS
# ==========================================
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
    BearerAuth:
      type: http
      scheme: bearer

  parameters:
    instanceId:
      name: instanceId
      in: path
      required: true
      schema:
        type: string
      description: Instance ID
      example: wa_lxyz123_abc45

  schemas:
    Instance:
      type: object
      properties:
        id:
          type: string
          example: wa_lxyz123_abc45
        name:
          type: string
          example: Customer Support
        status:
          type: string
          enum: [disconnected, connecting, connected]
          example: connected
        qrCode:
          type: string
          nullable: true
          description: Base64 QR code (only when connecting)
        connectedPhone:
          type: string
          nullable: true
          example: '60123456789'
        connectedAt:
          type: string
          format: date-time
          nullable: true
        hasSavedCredentials:
          type: boolean
          description: True when the instance has a local Baileys credential file. Does not expose credential contents.
          example: true
        webhookUrl:
          type: string
          example: https://your-api.com/webhook
        behaviorSettings:
          $ref: '#/components/schemas/BehaviorSettings'
        antiBanSettings:
          $ref: '#/components/schemas/AntiBanSettings'
        antiBanHealth:
          $ref: '#/components/schemas/AntiBanHealth'
        autoReconnect:
          type: object
          properties:
            enabled:
              type: boolean
              example: true
            attempts:
              type: integer
              example: 1
            maxAttempts:
              type: integer
              example: 8
            nextAttemptAt:
              type: string
              format: date-time
              nullable: true
        createdAt:
          type: string
          format: date-time

    BehaviorProfile:
      type: string
      enum: [bot-native, notification-balanced, notification-max]
      description: |
        Behavior profile preset.

        `bot-native` keeps full anti-ban behavior active.
        `notification-balanced` gives phone notifications a short head start before typing.
        `notification-max` prioritizes phone notifications and disables typing simulation.
      example: notification-balanced

    BehaviorSettings:
      type: object
      properties:
        behaviorProfile:
          $ref: '#/components/schemas/BehaviorProfile'
        typingSimulation:
          type: boolean
          description: Show "typing..." indicator before sending. Defaults to false for `notification-max`.
          default: true
          example: true
        delayEnabled:
          type: boolean
          description: Add human-like delays before responding
          default: true
          example: true
        phoneNotificationsEnabled:
          type: boolean
          description: Keep the socket unavailable briefly so the user's phone is more likely to receive a push notification before read/typing activity.
          default: false
          example: true
        notificationGraceMs:
          type: integer
          minimum: 0
          description: Milliseconds to wait in notification-first profiles before read/typing/send behavior starts.
          default: 0
          example: 2500

    AntiBanSettings:
      type: object
      properties:
        preset:
          type: string
          enum: [conservative, balanced, aggressive, custom]
          example: balanced
        messagesPerHour:
          type: integer
          example: 200
        messagesPerDay:
          type: integer
          example: 5000
        uniqueChatsPerHour:
          type: integer
          example: 50
        uniqueChatsPerDay:
          type: integer
          example: 500

    AntiBanHealth:
      type: object
      properties:
        status:
          type: string
          enum: [healthy, warning, limited]
          example: healthy
        hourlyUsage:
          type: integer
          description: Percentage of hourly message limit used
          example: 25
        dailyUsage:
          type: integer
          description: Percentage of daily message limit used
          example: 10
        hourlyChatsUsage:
          type: integer
          example: 20
        dailyChatsUsage:
          type: integer
          example: 8
        warnings:
          type: array
          items:
            type: string
          example: []
        stats:
          type: object
          properties:
            messagesThisHour:
              type: integer
              example: 12
            messagesThisDay:
              type: integer
              example: 30
            uniqueChatsThisHour:
              type: integer
              example: 5
            uniqueChatsThisDay:
              type: integer
              example: 8

    SendLink:
      oneOf:
        - type: string
          format: uri
          example: https://example.com/booking
        - type: object
          required: [url]
          properties:
            url:
              type: string
              format: uri
              example: https://example.com/booking
            label:
              type: string
              description: Optional label used when the URL must be appended to fallback text
              example: Booking page

    CtaUrl:
      oneOf:
        - type: string
          format: uri
          example: https://example.com/book
        - type: object
          required: [url]
          properties:
            url:
              type: string
              format: uri
              example: https://example.com/book
            label:
              type: string
              maxLength: 25
              description: Label used in the fallback link line
              example: Book now

    QuickReplyButtons:
      type: array
      maxItems: 3
      description: Quick reply style buttons. Defaults to numbered fallback text; `buttonMode: legacy` attempts raw legacy Baileys buttons.
      items:
        type: object
        required: [id, text]
        properties:
          id:
            type: string
            maxLength: 256
            example: pricing
          text:
            type: string
            maxLength: 20
            example: Pricing

    ButtonMode:
      type: string
      enum: [fallback, legacy]
      default: fallback
      description: |
        `fallback` sends clear text options and is recommended. `legacy` attempts raw legacy Baileys quick reply buttons and may not render on current WhatsApp MD clients.

    InteractiveSendRequest:
      type: object
      required: [to]
      properties:
        to:
          type: string
          description: Phone number with country code
          example: '60123456789'
        message:
          type: string
          description: Message text. Required unless link, ctaUrl, or buttons are supplied.
          example: Choose an option.
        link:
          $ref: '#/components/schemas/SendLink'
        ctaUrl:
          $ref: '#/components/schemas/CtaUrl'
        buttons:
          $ref: '#/components/schemas/QuickReplyButtons'
        buttonMode:
          $ref: '#/components/schemas/ButtonMode'
        linkPreview:
          type: boolean
          default: true
        footer:
          type: string
          example: Wasup API

    SendResult:
      type: object
      properties:
        sent:
          type: boolean
          example: true
        delay:
          type: integer
          description: Delay applied in milliseconds
          example: 3500
        typingSimulation:
          type: boolean
          example: true
        delayEnabled:
          type: boolean
          example: true
        reason:
          type: string
          description: Reason if message was blocked
        waitTime:
          type: integer
          description: Time to wait before retry (if rate limited)
        messageText:
          type: string
          description: Final text sent to Baileys after fallback formatting
        interactive:
          type: object
          properties:
            requestedTypes:
              type: array
              items:
                type: string
              example: [link, buttons]
            mode:
              type: string
              example: text_fallback
            limitations:
              type: array
              items:
                type: string

    LogEntry:
      type: object
      properties:
        id:
          type: string
          example: '1704825600000'
        timestamp:
          type: string
          format: date-time
        message:
          type: string
          example: Connected as 60123456789
        level:
          type: string
          enum: [info, success, warning, error]
          example: success

    Error:
      type: object
      properties:
        error:
          type: string
          example: Instance not found

  responses:
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: Unauthorized
              message:
                type: string
                example: Valid API key required. Use X-API-Key header or Authorization Bearer
