openapi: 3.0.3
info:
  title: WhatsApp Multi-Instance API
  description: |-
    Wasup lets you run one or more WhatsApp numbers from a single deployment. Each **instance** is an isolated number with its own pairing state, webhook, send limits, and activity logs.

    ## Who this API is for
    Integrators building bots, CRMs, booking flows, or support desks that need to **create numbers**, **pair them**, **send messages**, and **receive inbound events** over HTTPS.

    ## Authentication
    When an API key is configured, send it on every `/api/...` request:
    - Header `X-API-Key: your-key`
    - or `Authorization: Bearer your-key`

    Org-scoped keys from the Wasup dashboard Connection page are recommended. Open deployments without `API_KEY` accept unauthenticated calls (development only).

    ## Typical flow
    1. **Create** an instance (`POST /api/instances`)
    2. **Connect** and scan QR or enter a pairing code (`POST .../connect`, poll `GET .../qr`)
    3. **Set webhook** for inbound messages (`PUT .../webhook` or at create time)
    4. **Send** outbound messages (`POST .../send`)
    5. **Monitor** health and logs (`GET /api/health`, `GET .../logs`)

    ## Interactive messages
    Use the same send endpoints for plain text, link previews, CTA URL buttons, quick-reply buttons, media, locations, and reactions. See **Messaging** and **Webhook** sections for payload shapes.

    ## Inbound webhooks
    When someone messages your linked number, Wasup POSTs JSON to your `webhookUrl`. Media messages include `media_id` and `media.downloadUrl` so you can fetch the file with `GET /api/instances/{id}/media/{mediaId}`.
  version: 1.0.0
  contact:
    name: API Support
  license:
    name: MIT
servers:
  - url: https://bashir-s-workspace-hlzpr2.wasup.co
    description: This deployment
tags:
  - name: Whitelabel
    description: Single-call helpers that create, configure, and connect a number in one request — ideal for onboarding flows.
  - name: Instances
    description: Create, read, update, and delete WhatsApp instances. An instance is one linked phone number plus its settings.
  - name: Connection
    description: Start or stop the live WhatsApp session, fetch QR/pairing codes, and check whether a number is linked.
  - name: Messaging
    description: Send text, media, buttons, lists, and reactions to customers. Auto-select routes pick the first connected instance when you omit an instance id.
  - name: Reactions
    description: Attach or remove emoji reactions on existing messages using the message id from webhooks or send responses.
  - name: Handoff
    description: Pause automated replies for specific chats while a human agent takes over the conversation.
  - name: Profile
    description: Read or update the WhatsApp display name, profile photo, and About text for a linked instance.
  - name: Webhook
    description: Configure where inbound messages are delivered and inspect the JSON shape your receiver should expect.
  - name: Behavior
    description: Control typing indicators, human-like delays, and whether the linked phone should receive push notifications before the API marks messages read.
  - name: Anti-Ban
    description: Legacy rate-limit presets and usage counters that protect a number from sending too fast.
  - name: Wasup Anti-Ban
    description: Advanced per-instance protection modules (warm-up, rate limits, health monitoring, presence timing). State is stored on disk per instance.
  - name: Logs
    description: Read recent connection and messaging activity for troubleshooting.
  - name: System
    description: Deployment health, instance roll-ups, and API key utilities.
security:
  - ApiKeyAuth: []
  - BearerAuth: []
paths:
  /api/onboard:
    post:
      tags:
        - Whitelabel
      summary: Onboard a new WhatsApp number
      description: "**Onboard a new WhatsApp number in one call.** Creates an instance (or reuses an existing one for the same phone), sets optional webhook and profile fields, starts pairing, and returns a pairing code when applicable. Use this when your product wizard should not make separate create/connect/webhook calls."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - phone
              properties:
                phone:
                  type: string
                  description: Phone number with country code (+ prefix optional)
                  example: "447393002183"
                name:
                  type: string
                  description: Friendly name for the instance
                  example: Acme Corp
                webhookUrl:
                  type: string
                  format: uri
                  description: URL to forward inbound messages to
                  example: https://acme.com/webhook
                profileName:
                  type: string
                  description: WhatsApp display name (applied once connected)
                  example: Acme Support
                profileStatus:
                  type: string
                  description: WhatsApp "About" text (applied once connected)
                  example: We reply within minutes
                behaviorSettings:
                  $ref: "#/components/schemas/BehaviorSettings"
                  description: |
                    Optional behavior overrides at onboarding time. For
                    human-monitored deployments (clinics, support desks), use
                    `behaviorProfile: notification-balanced` so inbound
                    webhooks stay immediate while handset alerts are prioritized.
            examples:
              minimal:
                summary: Phone only
                value:
                  phone: "447393002183"
              full:
                summary: Full onboarding
                value:
                  phone: "447393002183"
                  name: Acme Corp
                  webhookUrl: https://acme.com/webhook
                  profileName: Acme Support
                  profileStatus: We reply within minutes
              clinic:
                summary: Clinic / human-monitored (phone notifications on)
                value:
                  phone: "447393002183"
                  name: Smile Dental
                  webhookUrl: https://wasup.co/webhook/...
                  behaviorSettings:
                    behaviorProfile: notification-balanced
                    notificationGraceMs: 12000
                    typingSimulation: true
      responses:
        "200":
          description: Instance already exists for this phone
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OnboardResponse"
              example:
                success: true
                instanceId: wa_abc123
                pairingCode: null
                status: connected
                message: Instance already exists for this phone number
        "201":
          description: Instance created and pairing code generated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OnboardResponse"
              example:
                success: true
                instanceId: wa_abc123
                pairingCode: A1B2-C3D4
                status: connecting
                message: Enter code A1B2-C3D4 in WhatsApp > Linked Devices > Link a Device
        "400":
          $ref: "#/components/responses/BadRequest"
  /api/instances:
    get:
      tags:
        - Instances
      summary: List all instances
      description: "**List every instance on this deployment.** Returns id, display name, connection status (`disconnected`, `connecting`, `connected`), linked phone when available, webhook URL, behavior settings, and anti-ban health. Use this to populate admin dashboards or to pick an instance id before sending."
      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 empty instance.** Optionally set a custom id, friendly name, inbound webhook URL, behavior profile, and anti-ban preset. The number is not linked until you call `POST .../connect` and complete QR or pairing. Does not send any messages."
      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: "**Fetch full details for one instance.** Same fields as the list endpoint plus QR state hints, connected-at timestamp, saved-credentials flag, and effective anti-ban usage. Use before sending or when showing a single number in your UI."
      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 without deleting it.** Change name, webhook URL, behavior profile (typing, delays, notification grace), and anti-ban limits. Does not disconnect an active session unless a setting requires reconnect (for example proxy changes)."
      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: "**Permanently remove an instance.** Stops the session, deletes local credentials, logs, and stored media for that id. This cannot be undone — create a new instance to link the same phone again."
      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"
  /api/instances/{instanceId}/connect:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    post:
      tags:
        - Connection
      summary: Connect instance
      description: "**Start linking this instance to WhatsApp.** Opens a pairing session. By default returns QR mode — poll `GET .../qr` and show the code to the user. Pass `pairingPhone` to receive a numeric pairing code instead of QR. Safe to retry while status is `connecting`."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                pairingPhone:
                  type: string
                  description: Phone number with country code (no + prefix). If provided, uses pairing code instead of QR.
                  example: "447393002183"
      responses:
        "200":
          description: Connection started
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Connection started (QR mode)
                  pairingCode:
                    type: string
                    nullable: true
                    description: Pairing code (only when pairingPhone was provided)
                    example: A1B2-C3D4
                  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: "**Close the live session locally.** Stops receiving and sending until you connect again. Saved credentials remain on disk so reconnect usually does not require a new QR. Pass `revoke: true` only when you intentionally want WhatsApp to invalidate the linked device everywhere."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                revoke:
                  type: boolean
                  default: false
                  description: If true, revoke the session on WhatsApp servers (optional).
      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: "**Wipe saved pairing data.** Disconnects if needed and deletes credential files. The next connect always requires a fresh QR scan or pairing code. Use when a number was unlinked from the phone or credentials are corrupted."
      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}/pair:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    post:
      tags:
        - Connection
      summary: Connect via pairing code
      description: "**Connect using a pairing code instead of QR.** Requires the phone number in the body. Returns a short code the user enters under WhatsApp → Linked devices. Automatically starts the connection if not already connecting."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - phoneNumber
              properties:
                phoneNumber:
                  type: string
                  description: Phone number with country code, no + prefix
                  example: "447393002183"
      responses:
        "200":
          description: Pairing code generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  pairingCode:
                    type: string
                    example: A1B2-C3D4
                  message:
                    type: string
                    example: Enter code A1B2-C3D4 in WhatsApp > Linked Devices > Link a Device
                  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 / pairing code
      description: "**Get the current QR image or pairing code.** Poll while status is `connecting`. Append `?format=image` for a raw PNG suitable for mobile apps. Returns connected phone when status is `connected`. Returns 204 when no code is ready yet — call `/connect` first."
      parameters:
        - name: format
          in: query
          schema:
            type: string
            enum:
              - image
          description: Set to `image` to receive a raw PNG instead of JSON
      responses:
        "200":
          description: QR / pairing code (JSON)
          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...
                  pairingCode:
                    type: string
                    nullable: true
                    description: Pairing code (if using pairing code mode)
                    example: A1B2-C3D4
                  phone:
                    type: string
                    nullable: true
                    description: Connected phone (when status is 'connected')
                  message:
                    type: string
                    example: Not yet generated. Call /connect or /pair first.
            image/png:
              schema:
                type: string
                format: binary
                description: Raw QR code PNG (when ?format=image)
        "204":
          description: No QR code available (already connected or not yet generated)
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/connection:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Whitelabel
        - Connection
      summary: Poll connection status
      description: "**Lightweight connection poll.** Returns only status, phone, uptime, and pairing/QR hints — faster than fetching the full instance object. Ideal for frontends that refresh every few seconds during onboarding."
      responses:
        "200":
          description: Connection status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConnectionStatus"
              example:
                success: true
                status: connected
                phone: "447393002183"
                connectedAt: 2026-01-29T12:00:00.000Z
                uptime: 3600
                pairingCode: null
                qrCode: null
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/send:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    post:
      tags:
        - Messaging
      summary: Send message via instance
      description: "**Send an outbound WhatsApp message from this instance.** Accepts plain text, link previews, CTA URL buttons, quick replies, images, documents, audio, video, and location pins in one payload. Requires `to` (customer phone, country code, no +) and content fields. Respects anti-ban delays; blocked sends return a reason and suggested wait time."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendMessageBody"
            examples:
              text:
                summary: Plain text
                value:
                  to: "60123456789"
                  message: Hello from Wasup
              buttons:
                summary: Interactive quick-reply buttons
                value:
                  to: "60123456789"
                  text: Choose an option
                  buttons:
                    - id: yes
                      text: Yes
                    - id: no
                      text: No
                  footer: Wasup
      responses:
        "200":
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  messageType:
                    type: string
                    example: text
                  result:
                    $ref: "#/components/schemas/SendResult"
        "400":
          $ref: "#/components/responses/BadRequest"
  /api/send:
    post:
      tags:
        - Messaging
      summary: Send message (auto-select instance)
      description: "**Send without specifying an instance id.** Wasup picks a connected instance automatically — usually matching `from_phone` when provided, otherwise the first connected instance. Same body as instance send. Fails with a clear error if no instance is connected."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/SendMessageBody"
                - type: object
                  properties:
                    from_phone:
                      type: string
                      description: Your connected phone number to send from (optional, auto-selects if omitted)
                      example: "60123456789"
                    to_phone:
                      type: string
                      description: Alias for `to`
                      example: "60198765432"
            examples:
              autoSelectButtons:
                summary: Auto-select connected instance and send buttons
                value:
                  to_phone: "60123456789"
                  text: Choose an option
                  buttons:
                    - id: yes
                      text: Yes
                    - id: no
                      text: No
                  footer: Wasup
              fromPhoneButtons:
                summary: Send buttons from a specific connected phone
                value:
                  from_phone: "60111111111"
                  to_phone: "60123456789"
                  message: Choose an option
                  messageType: buttons
                  buttons:
                    - id: yes
                      text: Yes
                    - id: no
                      text: No
      responses:
        "200":
          description: Message sent
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    message_id:
                      type: string
                      format: uuid
                    created_at:
                      type: string
                      format: date-time
                    from_phone:
                      type: string
                    to_phone:
                      type: string
                    message:
                      type: string
                    message_type:
                      type: string
                      example: text
                    status:
                      type: string
                      enum:
                        - sent
                        - rate_limited
                        - failed
        "400":
          $ref: "#/components/responses/BadRequest"
  /api/instances/{instanceId}/messages:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Whitelabel
        - Messaging
      summary: Get message history
      description: "**Search recent message history stored on the worker.** Filter by phone, direction, or text search. Useful for support consoles; primary automation should still rely on webhooks for real-time inbound events."
      parameters:
        - name: direction
          in: query
          schema:
            type: string
            enum:
              - inbound
              - outbound
          description: Filter by message direction (omit for both)
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
          description: Max messages to return
        - name: since
          in: query
          schema:
            type: string
            format: date-time
          description: Only return messages after this ISO timestamp
          example: 2026-01-29T00:00:00.000Z
      responses:
        "200":
          description: Message history
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  count:
                    type: integer
                    example: 12
                  messages:
                    type: array
                    items:
                      $ref: "#/components/schemas/Message"
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/react:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    post:
      tags:
        - Reactions
        - Messaging
      summary: React to a message (instance)
      description: "**Add or remove a reaction emoji on a message.** Provide `to`, `messageId` from a webhook or send response, and `emoji` (empty string removes the reaction). Set `fromMe: true` when reacting to a message this instance sent."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReactionBody"
      responses:
        "200":
          description: Reaction sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  emoji:
                    type: string
                    example: 🔥
                  messageId:
                    type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
  /api/react:
    post:
      tags:
        - Reactions
        - Messaging
      summary: React to a message (auto-select instance)
      description: "**React using auto-selected instance.** Same body as instance react plus optional `from_phone` to choose which linked number performs the reaction."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/ReactionBody"
                - type: object
                  properties:
                    from_phone:
                      type: string
                      description: Sender phone number to match an instance (optional)
                      example: "358942721757"
                  required: []
      responses:
        "200":
          description: Reaction sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  instance_id:
                    type: string
                  emoji:
                    type: string
                  messageId:
                    type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/handoff:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Handoff
      summary: Get active human-handoff chats
      description: "**List chats currently in human handoff mode.** While handoff is active for a contact, automated replies are suppressed so agents can respond manually from the phone or another tool."
      responses:
        "200":
          description: Handoff list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  chats:
                    type: array
                    items:
                      type: object
                      properties:
                        chatId:
                          type: string
                          example: 447835156367@s.whatsapp.net
                        since:
                          type: string
                          format: date-time
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      tags:
        - Handoff
      summary: Tag or untag a chat for human handoff
      description: "**Mark a chat as human-controlled.** Provide customer phone or chat id. Optional resume keywords and message control when the bot should start again."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - phone
                - active
              properties:
                phone:
                  type: string
                  description: Phone number or phone or chat id to tag
                  example: "447835156367"
                active:
                  type: boolean
                  description: "`true` to pause bot, `false` to resume"
                  example: true
      responses:
        "200":
          description: Handoff updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  chatId:
                    type: string
                  active:
                    type: boolean
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      tags:
        - Handoff
      summary: Clear all human handoff tags
      description: "**Clear handoff for one contact or all contacts.** Automation resumes for the cleared chats on the next inbound message."
      responses:
        "200":
          description: All handoffs cleared
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  cleared:
                    type: integer
                    example: 3
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/handoff/settings:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Handoff
      summary: Get handoff settings
      description: "**Read handoff keywords, resume message, and managed numbers** configured for this instance."
      responses:
        "200":
          description: Handoff settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  settings:
                    $ref: "#/components/schemas/HandoffSettings"
        "404":
          $ref: "#/components/responses/NotFound"
    put:
      tags:
        - Handoff
      summary: Update handoff settings
      description: "**Update handoff rules** such as keywords that return control to the bot and default resume text."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                resumeKeywords:
                  type: array
                  items:
                    type: string
                  description: Keywords that resume the AI agent (e.g. `["#ai", "#resume"]`)
                  example:
                    - "#ai"
                    - "#assistant"
                    - "#bot"
                    - "#resume"
                resumeMessage:
                  type: string
                  description: |
                    Auto-reply sent to the chat when the AI agent resumes.
                    Set to empty string `""` for silent resume.
                  example: AI assistant is back online. How can I help?
      responses:
        "200":
          description: Updated handoff settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  settings:
                    $ref: "#/components/schemas/HandoffSettings"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
  /api/storage/status:
    get:
      tags:
        - System
      summary: Check Azure Blob Storage status
      description: "**Report media storage backend** (local disk vs cloud) and capacity hints for this deployment."
      responses:
        "200":
          description: Storage status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  enabled:
                    type: boolean
                    example: true
                  container:
                    type: string
                    example: whatsapp-media
  /api/upload:
    post:
      tags:
        - Messaging
      summary: Upload media to Azure Blob Storage
      description: "**Upload a file to the worker** for later use in outbound messages. Returns a URL or handle referenced by send payloads."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - data
                - mimeType
              properties:
                data:
                  type: string
                  format: byte
                  description: Base64-encoded file data
                mimeType:
                  type: string
                  description: MIME type of the file
                  example: image/jpeg
                fileName:
                  type: string
                  description: Original file name (used for extension detection)
                  example: photo.jpg
                instanceId:
                  type: string
                  description: Instance ID to use as storage folder prefix
                  example: wa_abc123
      responses:
        "200":
          description: File uploaded
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  url:
                    type: string
                    description: Public URL of the uploaded file
                    example: https://whatsappmediastore.blob.core.windows.net/whatsapp-media/wa_abc/uploads/1706000000-a1b2c3d4.jpg
                  blobName:
                    type: string
                    example: wa_abc/uploads/1706000000-a1b2c3d4.jpg
        "400":
          $ref: "#/components/responses/BadRequest"
        "503":
          description: Azure Blob Storage not configured
  /api/instances/{instanceId}/profile:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Profile
      summary: Get profile info
      description: "**Read WhatsApp profile metadata** — display name, About text, and profile picture URL when linked."
      responses:
        "200":
          description: Profile info
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  profile:
                    type: object
                    properties:
                      phone:
                        type: string
                        nullable: true
                        example: "447393002183"
                      connected:
                        type: boolean
                        example: true
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/profile/name:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    put:
      tags:
        - Profile
      summary: Update display name
      description: "**Change the WhatsApp display name** shown to customers who have not saved your contact."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  example: Acme Support
      responses:
        "200":
          description: Name updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Display name updated to "Acme Support"
        "400":
          $ref: "#/components/responses/BadRequest"
  /api/instances/{instanceId}/profile/picture:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    put:
      tags:
        - Profile
      summary: Set profile picture
      description: "**Upload or replace the profile photo** for the linked number. Accepts image URL or base64 depending on deployment."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - imageUrl
              properties:
                imageUrl:
                  type: string
                  format: uri
                  example: https://example.com/logo.png
      responses:
        "200":
          description: Picture updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Profile picture updated
        "400":
          $ref: "#/components/responses/BadRequest"
    delete:
      tags:
        - Profile
      summary: Remove profile picture
      description: "**Remove the profile photo** and revert to WhatsApp default avatar."
      responses:
        "200":
          description: Picture removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: Profile picture removed
        "400":
          $ref: "#/components/responses/BadRequest"
  /api/instances/{instanceId}/profile/status:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    put:
      tags:
        - Profile
      summary: Update "About" text
      description: "**Update the About / status line** under the profile name."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - status
              properties:
                status:
                  type: string
                  example: We reply within minutes!
      responses:
        "200":
          description: About text updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: About text updated to "We reply within minutes!"
        "400":
          $ref: "#/components/responses/BadRequest"
  /api/instances/{instanceId}/behavior:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Behavior
      summary: Get behavior settings
      description: "**Read behavior settings** — profile preset (`bot-native`, `notification-balanced`, `notification-max`), typing simulation, response delays, and phone notification grace period."
      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 how human-like the instance acts when replying.** For clinics or desks where staff read messages on their phone first, use `notification-balanced` with a grace period so push notifications arrive before read receipts."
      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"
  /api/instances/{instanceId}/anti-ban:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Anti-Ban
      summary: Get anti-ban status
      description: "**Read legacy anti-ban limits and current usage percentages.** Shows hourly/daily message counts, unique chat limits, and warning state."
      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: "**Change legacy rate limits** using presets (`conservative`, `balanced`, `aggressive`) or custom hourly/daily caps."
      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"
  /api/webhook:
    get:
      tags:
        - Webhook
      summary: Get global default webhook
      description: "**Read the deployment-wide default webhook URL** from environment configuration. Instances without their own webhook inherit this URL."
      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: "**See which webhook URL will receive inbound messages** for this instance — instance-specific override or global default."
      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 or clear the instance webhook.** Pass a URL to receive JSON POSTs for every inbound message, or null/empty to fall back to the 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"
  /api/instances/{instanceId}/webhook/test:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    post:
      tags:
        - Whitelabel
        - Webhook
      summary: Test webhook delivery
      description: "**Send a sample inbound payload** to the configured webhook URL so you can verify your receiver without waiting for a real message."
      responses:
        "200":
          description: Webhook test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: true if webhook returned 2xx
                    example: true
                  webhookUrl:
                    type: string
                    example: https://acme.com/webhook
                  responseStatus:
                    type: integer
                    description: HTTP status returned by the webhook
                    example: 200
                  responseBody:
                    description: First 500 chars of the webhook's response body
                  message:
                    type: string
                    example: Webhook test delivered successfully
              examples:
                success:
                  summary: Webhook reachable
                  value:
                    success: true
                    webhookUrl: https://acme.com/webhook
                    responseStatus: 200
                    responseBody:
                      ok: true
                    message: Webhook test delivered successfully
                failure:
                  summary: Webhook unreachable
                  value:
                    success: false
                    webhookUrl: https://acme.com/webhook
                    error: connect ECONNREFUSED
                    message: Failed to deliver test webhook
        "400":
          description: No webhook configured
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: No webhook URL configured for this instance
                  hint:
                    type: string
                    example: Set webhookUrl via PUT /api/instances/:id or during onboarding
        "404":
          $ref: "#/components/responses/NotFound"
  /api/instances/{instanceId}/logs:
    parameters:
      - $ref: "#/components/parameters/instanceId"
    get:
      tags:
        - Logs
      summary: Get activity logs
      description: "**Fetch recent activity log lines** — connects, disconnects, sends, receives, and errors. Use the `limit` query param (max 200) for pagination."
      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"
  /api/health:
    get:
      tags:
        - System
      summary: Health check
      description: "**Quick deployment health check.** Returns ok/error, process uptime, and count of total vs connected instances. No authentication required — suitable for load balancers."
      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: "**Authenticated roll-up of all instances** with id, name, status, and phone. Similar to `GET /api/instances` but optimized for monitoring dashboards."
      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/system/reload-behavior-from-disk:
    post:
      tags:
        - System
      summary: Hot-reload behavior settings from disk
      description: "**Reload saved behavior settings from disk** for already-running instances without restarting the whole process. Does not disconnect active numbers."
      responses:
        "200":
          description: Reload outcome per instance id
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  count:
                    type: integer
                  results:
                    type: array
                    items:
                      type: object
        "503":
          description: Server still starting
  /api/generate-api-key:
    post:
      tags:
        - System
      summary: Generate API key
      description: "**Generate a random API key string** for reference. You must manually add it to server environment — this endpoint does not persist keys by itself."
      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>
  /api/instances/{instanceId}/antiban-v2:
    get:
      tags:
        - Wasup Anti-Ban
      summary: Get full v2 status
      description: "**Read Wasup Anti-Ban v2 module status** — which protection modules are active and their health summary."
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: V2 status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  antibanV2:
                    type: object
                    properties:
                      enabled:
                        type: boolean
                      running:
                        type: boolean
                      preset:
                        type: string
                        enum:
                          - conservative
                          - moderate
                          - aggressive
                      health:
                        type: object
                        properties:
                          risk:
                            type: string
                            enum:
                              - low
                              - medium
                              - high
                              - critical
                          score:
                            type: integer
                            minimum: 0
                            maximum: 100
                          recommendation:
                            type: string
                          isPaused:
                            type: boolean
                      warmup:
                        type: object
                        properties:
                          phase:
                            type: string
                            enum:
                              - warming
                              - graduated
                          day:
                            type: integer
                          totalDays:
                            type: integer
                          todayLimit:
                            type: integer
                          todaySent:
                            type: integer
                          progress:
                            type: integer
                          complete:
                            type: boolean
  /api/instances/{instanceId}/antiban-v2/config:
    get:
      tags:
        - Wasup Anti-Ban
      summary: Get v2 config block
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Current config
      description: "**Read detailed Anti-Ban v2 configuration** per module (warm-up, rate limiter, presence timing, etc.)."
    put:
      tags:
        - Wasup Anti-Ban
      summary: Update v2 config
      description: "**Update Anti-Ban v2 module settings** such as warm-up stage, rate multipliers, or pause thresholds."
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                  description: Master switch. Set false to fall back to legacy AntiBanManager.
                preset:
                  type: string
                  enum:
                    - conservative
                    - moderate
                    - aggressive
                overrides:
                  type: object
                  description: Override individual rate limits + delays
                  properties:
                    maxPerMinute:
                      type: integer
                      example: 12
                    maxPerHour:
                      type: integer
                      example: 250
                    maxPerDay:
                      type: integer
                      example: 4000
                    minDelayMs:
                      type: integer
                      example: 1500
                    maxDelayMs:
                      type: integer
                      example: 5000
                modules:
                  type: object
                  description: Per-module enable/disable flags
                  properties:
                    warmup:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    replyRatio:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    contactGraph:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    presence:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                        circadianProfile:
                          type: string
                          enum:
                            - default
                            - nightOwl
                            - earlyBird
                            - always_on
                        timezone:
                          type: string
                          example: Europe/London
                    retryTracker:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    reconnectThrottle:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    contactMapping:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    sessionStability:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                    stealthConnect:
                      type: object
                      properties:
                        enabled:
                          type: boolean
                alertsWebhook:
                  type: string
                  nullable: true
                  description: Optional webhook URL fired on risk transitions (Telegram-/Discord-compatible JSON)
      responses:
        "200":
          description: Updated
  /api/instances/{instanceId}/antiban-v2/health:
    get:
      tags:
        - Wasup Anti-Ban
      summary: Compact health (risk + score)
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Risk
          content:
            application/json:
              schema:
                type: object
                properties:
                  health:
                    type: object
                    properties:
                      risk:
                        type: string
                        enum:
                          - low
                          - medium
                          - high
                          - critical
                      score:
                        type: integer
                      recommendation:
                        type: string
                      isPaused:
                        type: boolean
      description: "**Health scores and recent warnings** from Anti-Ban v2 monitors for this instance."
  /api/instances/{instanceId}/antiban-v2/warmup:
    get:
      tags:
        - Wasup Anti-Ban
      summary: Warmup state (day x/7)
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Warmup
          content:
            application/json:
              schema:
                type: object
                properties:
                  warmup:
                    type: object
                    properties:
                      phase:
                        type: string
                        enum:
                          - warming
                          - graduated
                      day:
                        type: integer
                        example: 4
                      totalDays:
                        type: integer
                        example: 7
                      todayLimit:
                        type: integer
                        example: 117
                      todaySent:
                        type: integer
                        example: 23
                      progress:
                        type: integer
                        example: 57
                      complete:
                        type: boolean
  /api/instances/{instanceId}/antiban-v2/lid-mappings:
    get:
      tags:
        - Wasup Anti-Ban
      summary: LID↔PN cache snapshot
      description: "**Inspect contact id mappings** maintained for reliable delivery (internal troubleshooting; most integrations can ignore)."
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Mappings
  /api/instances/{instanceId}/antiban-v2/pause:
    post:
      tags:
        - Wasup Anti-Ban
      summary: Emergency pause
      description: "**Temporarily pause Anti-Ban v2 enforcement** for maintenance or testing — use with caution."
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Paused
  /api/instances/{instanceId}/antiban-v2/resume:
    post:
      tags:
        - Wasup Anti-Ban
      summary: Resume after pause
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Resumed
      description: "**Resume Anti-Ban v2 enforcement** after a manual pause."
  /api/instances/{instanceId}/antiban-v2/reset:
    post:
      tags:
        - Wasup Anti-Ban
      summary: Nuclear reset
      description: "**Reset Anti-Ban v2 counters and state files** for this instance to defaults."
      parameters:
        - name: instanceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Reset
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:
    SendMessageBody:
      type: object
      required:
        - to
      properties:
        to:
          type: string
          description: Recipient phone number (with country code, no + or spaces) or phone or chat id
          example: "60123456789"
        message:
          type: string
          description: Text body or caption (required for text type, optional caption for media)
          example: Hello! How can I help you today?
        text:
          type: string
          description: Alias for `message`; useful for concise interactive button payloads
          example: Choose an option
        messageType:
          type: string
          description: Type of message to send. If omitted, `buttons` implies `buttons` and `sections` implies `list`.
          enum:
            - text
            - image
            - video
            - document
            - audio
            - buttons
            - list
            - location
            - contact
          default: text
          example: text
        mediaUrl:
          type: string
          description: Direct URL to media file (required for image/video/document/audio)
          example: https://example.com/photo.jpg
        mimeType:
          type: string
          description: MIME type for document/audio
          example: application/pdf
        fileName:
          type: string
          description: File name for document messages
          example: report.pdf
        ptt:
          type: boolean
          description: Send audio as voice note (push-to-talk). Default true.
          default: true
        footer:
          type: string
          description: Footer text for button and list messages
          example: Powered by WhatsApp AI
        buttons:
          type: array
          description: |
            Quick-reply buttons. Set `messageType=buttons`, or omit `messageType`
            and include this array. Native WhatsApp clients generally support up
            to 3 quick-reply buttons; button titles are capped at 20 characters.
          items:
            type: object
            properties:
              id:
                type: string
                example: btn_yes
              text:
                type: string
                example: Yes
          example:
            - id: yes
              text: Yes, proceed
            - id: no
              text: No thanks
            - id: later
              text: Maybe later
        title:
          type: string
          description: Title for list messages
          example: Our Services
        buttonText:
          type: string
          description: Menu button label for list messages
          default: Menu
          example: View Options
        sections:
          type: array
          description: |
            List sections (messageType=list). Rendered as a single-select native flow
            via Wasup's interactive helper.
          items:
            type: object
            properties:
              title:
                type: string
                example: Services
              rows:
                type: array
                items:
                  type: object
                  properties:
                    title:
                      type: string
                      example: Consulting
                    id:
                      type: string
                      example: consulting
                    description:
                      type: string
                      example: Expert advice
        latitude:
          type: number
          format: double
          description: Latitude for location messages
          example: 3.1577
        longitude:
          type: number
          format: double
          description: Longitude for location messages
          example: 101.7117
        locationName:
          type: string
          description: Location name for location messages
          example: Petronas Twin Towers
        locationAddress:
          type: string
          description: Address for location messages
          example: KLCC, Kuala Lumpur
        contactCard:
          type: object
          description: Contact card details (for messageType=contact)
          properties:
            displayName:
              type: string
              example: John Doe
            phoneNumber:
              type: string
              example: "+60123456789"
        typingSimulation:
          type: boolean
          description: Override instance typing simulation setting for this message
        delayEnabled:
          type: boolean
          description: Override instance human-delay setting for this message
        contactName:
          type: string
          description: Name for auto-saving contact before sending
          example: Unknown User
        skipContactSave:
          type: boolean
          description: Skip auto-saving recipient as a contact
          default: false
    ReactionBody:
      type: object
      required:
        - to
        - messageId
        - emoji
      properties:
        to:
          type: string
          description: Recipient phone number or phone or chat id (for instance-scoped endpoint)
          example: "447835156367"
        to_phone:
          type: string
          description: Recipient phone number (for auto-select endpoint)
          example: "447835156367"
        messageId:
          type: string
          description: |
            WhatsApp message id to react to. Obtain from the `message_id` field
            returned by the send endpoints or from inbound webhook payloads.
          example: 3EB0DAC5F4A2E1B7
        emoji:
          type: string
          description: Emoji to react with. Pass an empty string `""` to remove a reaction.
          example: 🔥
        fromMe:
          type: boolean
          description: |
            Set to `true` if the target message was sent by the connected account.
            Defaults to `false` (reacting to a received message).
          default: false
    HandoffSettings:
      type: object
      properties:
        resumeKeywords:
          type: array
          items:
            type: string
          description: |
            Keywords a human agent types from the phone to reactivate the AI bot
            for that chat. Matched case-insensitively at the start of the message.
          default:
            - "#ai"
            - "#assistant"
            - "#bot"
            - "#resume"
          example:
            - "#ai"
            - "#assistant"
            - "#bot"
            - "#resume"
        resumeMessage:
          type: string
          description: |
            Optional auto-reply sent when the AI agent resumes on a chat.
            Empty string means silent resume.
          default: ""
          example: AI assistant is back online. How can I help?
    OnboardResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        instanceId:
          type: string
          example: wa_abc123
        pairingCode:
          type: string
          nullable: true
          description: Pairing code to enter in WhatsApp (null if already connected)
          example: A1B2-C3D4
        status:
          type: string
          enum:
            - disconnected
            - connecting
            - connected
          example: connecting
        message:
          type: string
          example: Enter code A1B2-C3D4 in WhatsApp > Linked Devices > Link a Device
    ConnectionStatus:
      type: object
      properties:
        success:
          type: boolean
          example: true
        status:
          type: string
          enum:
            - disconnected
            - connecting
            - connected
          example: connected
        phone:
          type: string
          nullable: true
          example: "447393002183"
        connectedAt:
          type: string
          format: date-time
          nullable: true
        uptime:
          type: integer
          nullable: true
          description: Seconds since connection was established
          example: 3600
        pairingCode:
          type: string
          nullable: true
        qrCode:
          type: string
          nullable: true
    Message:
      type: object
      properties:
        id:
          type: string
          example: 3EB0B430A7F9B1D2
        direction:
          type: string
          enum:
            - inbound
            - outbound
          example: inbound
        from:
          type: string
          description: Sender phone number
          example: "447393002183"
        to:
          type: string
          description: Recipient phone number
          example: "60123456789"
        text:
          type: string
          example: Hello, I need help with my order
        mediaUrl:
          type: string
          nullable: true
          description: |
            Public URL of the media file (image, audio, video, document, sticker)
            stored in Azure Blob Storage. Only present for media messages when
            `AZURE_STORAGE_CONNECTION_STRING` is configured.
          example: https://whatsappmediastore.blob.core.windows.net/whatsapp-media/wa_abc/image/1706000000-a1b2c3d4.jpg
        mediaType:
          type: string
          nullable: true
          enum:
            - image
            - video
            - audio
            - document
            - sticker
            - null
          description: Type of media attachment
        mimeType:
          type: string
          nullable: true
          description: MIME type of the media file
          example: image/jpeg
        fileName:
          type: string
          nullable: true
          description: Original file name (documents only)
        timestamp:
          type: string
          format: date-time
        status:
          type: string
          example: delivered
    WebhookPayload:
      type: object
      description: |
        Payload sent to the per-instance webhook URL when a message is received.
        Your webhook should return `{ reply: "text" }` to auto-respond, or
        `{ skip: true }` to suppress the bot reply.
      properties:
        message_id:
          type: string
        created_at:
          type: string
          format: date-time
        from_phone:
          type: string
          example: "447835156367"
        to_phone:
          type: string
          example: "358942721757"
        message:
          type: string
        media_type:
          type: string
          enum:
            - text
            - image
            - video
            - audio
            - document
            - sticker
            - location
            - contact
        media_url:
          type: string
          nullable: true
          description: Public Azure Blob Storage URL of the media file (null for text)
        mime_type:
          type: string
          nullable: true
        file_name:
          type: string
          nullable: true
        status:
          type: string
          example: received
        webhook_id:
          type: string
          description: Instance ID
        event:
          type: string
          example: message
        quoted_message:
          type: string
          nullable: true
    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)
        pairingCode:
          type: string
          nullable: true
          description: Pairing code (only when connecting via pairing)
        connectedPhone:
          type: string
          nullable: true
          example: "60123456789"
        connectedAt:
          type: string
          format: date-time
          nullable: 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"
        createdAt:
          type: string
          format: date-time
    BehaviorSettings:
      type: object
      properties:
        behaviorProfile:
          type: string
          enum:
            - bot-native
            - notification-balanced
            - notification-max
          description: |
            Top-level behavior mode.

            `bot-native` uses active-device automation with typing, reads, delays,
            and presence cycling.

            `notification-balanced` prioritizes handset alerts without promising
            delivery: inbound messages are logged/webhooked immediately, then
            reads/typing/replies wait for `notificationGraceMs`.

            `notification-max` maximizes handset alerts: typing is disabled and
            automated read receipts are skipped.
          default: bot-native
          example: notification-balanced
        typingSimulation:
          type: boolean
          description: |
            Show "typing..." before sending. In `notification-balanced`, typing
            happens only after the grace window. In `notification-max`, this is
            forced off.
          default: true
          example: true
        delayEnabled:
          type: boolean
          description: Add human-like delays before responding
          default: true
          example: true
        notificationGraceMs:
          type: integer
          minimum: 0
          maximum: 120000
          description: |
            Milliseconds to wait after an inbound message before read receipt,
            typing, or reply in notification profiles. The webhook/log event is
            still sent immediately.
          default: 12000
          example: 12000
        phoneNotificationsEnabled:
          type: boolean
          description: |
            Legacy compatibility flag. New clients should set `behaviorProfile`.
            When true, maps to `notification-balanced` unless typing is false,
            in which case it maps to `notification-max`.
          default: false
          example: false
    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
    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)
    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
