openapi: 3.0.3
info:
  title: WAMCP — WhatsApp MCP Platform API
  description: |
    **WAMCP** — Production WhatsApp infrastructure for AI agents.

    Pair WhatsApp, auto-provision dedicated MCP credentials (named per-agent), run 33+ sync-aware MCP tools, audited sessions. Owners get a minimal settings portal (`/portal`) for health, named MCP accesses (nicknames + send/read/provision toggles per credential), security — **not** a chat UI. Use official WhatsApp app for conversations.

    **Machine-readable playbook:** `GET /docs/agent-playbook` · **LLM index:** `GET /llms.txt`

    ---

    ## For AI agents — start here

    **Important for agents & hosts:** This OpenAPI + the playbook are machine-readable. For full operating instructions, patterns, and examples **also read the human guides**:

    - [/agents](/agents) — complete 9-step onboarding
    - [/skills](/skills) — production-ready skills, reactive loops, approval flows, traces, guardrails
    - [/mcp](/mcp) — tool reference

    This spec is **self-contained** for API calls. An agent can onboard a user end-to-end:

    1. **Discover** — read this page, `GET /docs/agent-playbook` (structured JSON), or `GET /docs/openapi.yaml`. Consult the human guides above (/agents, /skills, /mcp) for patterns and examples.
    2. **Authenticate** — use `X-Tenant-Key: tnt_...` (white-label) or platform admin token to start pairing
    3. **Pair WhatsApp** — `POST /api/session/connect/code` → tell the user the `pairing_code`
    4. **Poll** — `GET /api/session/connect/status?uuid=...` every **3 seconds** until `status=success`
       → save `api_key` (`wbot_...`) and `mcp_connection` (MCP is **auto-enabled** on success)
    5. **Configure host** — paste `mcp_connection.cursor_mcp_example` into Grok, Cursor, or Claude Desktop
       (optional: `POST /api/mcp/enable` only if you need `allow_provision=true` or custom permissions)
    6. **Wait for sync** — MCP `whatsapp_wait_for_sync` then `whatsapp_health_check`
    7. **Manage** — `POST /mcp` with Bearer `mcp_...` — 33 tools documented in `GET /docs/agent-playbook` + skills guide above.

    ---

    ## User PWA portal (`/api/portal/*`)

    Authenticated with portal bearer token (`prt_...`) from install/setup/login.
    Full portal path list: see `api/portal_openapi.yaml` in the repo.
    Key routes: `GET /api/portal/health`, `GET /api/portal/search`, `POST /api/portal/messages/send`,
    `POST /api/portal/session/reconnect`, `POST /api/portal/session/logout` (pairing code required),
    `GET /api/portal/history/export`, `POST /api/portal/mcp/rotate`, `POST /api/portal/mcp/invite`,
    `GET /api/portal/notifications?category=messages|session|mcp`, `GET /api/portal/sync/stream` (SSE).

    **Machine-readable playbook:** `GET /docs/agent-playbook` (v2 — full tool catalog with parameters)

    **MCP auto-enable:** On `status=success`, the poll response includes `mcp_connection` with token and
    `cursor_mcp_example`. Call `POST /api/mcp/enable` / `whatsapp_enable_mcp` only to rotate token or
    enable `allow_provision` for provisioning other phones.

    ---

    ### White-label multi-tenant model

    Centralized infrastructure; each partner is a **tenant** with an isolated `tnt_...` key.
    Sessions, API keys, and MCP tokens are tenant-scoped — partners cannot read each other's data.

    ### Authentication

    | Role | Header | Format | Used for |
    |------|--------|--------|----------|
    | Platform admin | `X-Admin-Token` or `Authorization: Bearer ...` | `ADMIN_MASTER_TOKEN` | Tenants, admin keys, docs (when locked) |
    | Tenant provisioner | `X-Tenant-Key` or `Bearer tnt_...` | `tnt_...` | Start connect flows |
    | Session API | `X-API-Key` or `Bearer wbot_...` | `wbot_...` | Send, history, session, MCP config |
    | MCP agent | `Authorization: Bearer mcp_...` | `mcp_...` | `POST /mcp` tool calls |

    Connect polling (`/connect/status`, `/connect/qr-image`) requires the **same tenant key** (or admin token)
    that started the flow — prevents UUID enumeration across tenants.

    When `REQUIRE_TENANT_KEY=true`, also send `X-Tenant-Key` on every session API call.

    ### AI agent workflow — pairing code (recommended)

    ```http
    POST /api/session/connect/code
    X-Tenant-Key: tnt_abc123...
    Content-Type: application/json

    {"phone_number": "5511999999999"}
    ```

    Response includes `uuid`, `pairing_code` (e.g. `ABCD-1234`), `expires_at`.
    **Agent action:** instruct the user to open WhatsApp → Settings → Linked Devices →
    Link a device → Link with phone number → enter the code.

    ```http
    GET /api/session/connect/status?uuid=<uuid>
    X-Tenant-Key: tnt_abc123...
    ```

    Poll every **3s** for up to **120s**. On `status=success`, response includes `api_key` (`wbot_...`).
    Store the API key securely — it is the session credential for REST and MCP enable.

    ### AI agent workflow — enable MCP and connect

    ```http
    POST /api/mcp/enable
    X-API-Key: wbot_...
    Content-Type: application/json

    {"description": "User assistant", "allow_provision": true}
    ```

    Requires WhatsApp `connected=true` and `logged_in=true`. Returns full connection details:

    - `endpoint_url` — e.g. `https://host/mcp`
    - `authentication.token` — exclusive `mcp_...` bearer token
    - `cursor_mcp_example` — ready-to-paste MCP server config
    - `tools` — all available MCP tool names
    - `permissions` — `allow_send`, `allow_read`, `allow_provision`

    Re-fetch anytime: `GET /api/mcp/connection` with the same `wbot_` key.

    ### AI agent workflow — manage via MCP

    Connect your MCP host to `endpoint_url` with `Authorization: Bearer mcp_...`.

    **Start here after connect:** `whatsapp_health_check`, `whatsapp_wait_for_sync`, `whatsapp_get_mcp_connection`

    **Messaging:** `whatsapp_send_message`, `whatsapp_reply_to_message`, `whatsapp_resolve_recipient`,
    `whatsapp_search_messages`, `whatsapp_get_chat`, `whatsapp_list_messages`, `whatsapp_list_chats`,
    `whatsapp_list_contacts`, `whatsapp_list_groups`, `whatsapp_send_reaction`, `whatsapp_send_typing`,
    `whatsapp_download_media`, `whatsapp_mark_chat_read`

    **Session & sync:** `whatsapp_get_session_status`, `whatsapp_get_sync_status`, `whatsapp_health_check`

    **Notifications:** `whatsapp_watch_notifications`, `whatsapp_list_notifications`,
    `whatsapp_get_unread_notification_count`, `whatsapp_mark_notification_read`

    **Lifecycle** (provision tools need `allow_provision=true`):
    `whatsapp_reconnect`, `whatsapp_disconnect`, `whatsapp_connect_kit`, `whatsapp_connect_code`,
    `whatsapp_connect_qr`, `whatsapp_connect_status`, `whatsapp_enable_mcp`, `whatsapp_rotate_mcp_token`

    **Resources:** `whatsapp://session`, `whatsapp://sync`, `whatsapp://notifications/unread`,
    `whatsapp://messages/{chat_jid}`

    **Full catalog:** every tool name, parameter, and `when_to_use` hint is in `GET /docs/agent-playbook` → `mcp.tools[]`

    ### Alternative — QR pairing

    1. `POST /api/session/connect/qr` (phone optional)
    2. Show user `GET /api/session/connect/qr-image?uuid=<uuid>` or render `qr_data`
    3. Poll `GET /api/session/connect/status?uuid=<uuid>` until `success`

    ### Error handling for agents

    - **401** — wrong or missing auth header
    - **403** — connect UUID belongs to another tenant
    - **408** — pairing code not ready; poll status instead
    - **409** — WhatsApp offline; call `POST /api/session/reconnect` or `whatsapp_reconnect`
    - **Rate limit** — MCP default 120 req/min per token; audit via `GET /api/admin/mcp/audit`

    ### Connect endpoints and Supabase

    `/api/session/connect/*` writes to the same Postgres tables used by Supabase Realtime
    (`whatsmeow_login_requests`, `whatsmeow_qr_codes`, `whatsmeow_pairing_codes`,
    `whatsmeow_active_sessions`) — no manual Supabase inserts required.
  version: 1.4.0
  contact:
    name: Whatsmeow Bot

servers:
  - url: https://wamcp.up.railway.app
    description: Production (Railway)
  - url: http://localhost:8080
    description: Local development

tags:
  - name: AI Agent Guide
    description: |
      Self-service onboarding for AI agents. Read `info.description` and `GET /docs/agent-playbook`.
      Typical flow: connect/code → poll status → mcp/enable → configure MCP host → manage via /mcp.
  - name: Connect
    description: |
      Start QR or pairing-code sessions. Tenant key is optional on first connect (auto-provisioned).
      MCP is auto-enabled when pairing succeeds. Poll status for api_key, tenant_key, and mcp_connection.
  - name: Tenant MCP
    description: |
      Tenant-scoped MCP inventory, active sessions, audit log, and per-integration WhatsApp access control.
      Stored in Supabase (`whatsmeow_mcp_config`, `whatsmeow_mcp_active_sessions`, `whatsmeow_mcp_audit`).
  - name: Session
    description: Session status and lifecycle (wbot_ API key)
  - name: Messages
    description: Send and read messages (wbot_ API key)
  - name: Admin
    description: Platform admin — tenants, keys, MCP audit
  - name: Tenants
    description: White-label tenant provisioning (platform admin only)
  - name: MCP
    description: |
      Model Context Protocol for AI agents. Enable with wbot_ key; connect agent to /mcp with mcp_ token.
      Returns cursor_mcp_example for one-step host configuration.

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
    BearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: Use `Bearer wbot_...`
    AdminToken:
      type: apiKey
      in: header
      name: X-Admin-Token
    BearerAdmin:
      type: http
      scheme: bearer
      bearerFormat: Admin Token
      description: Use `Bearer <ADMIN_MASTER_TOKEN>`
    MCPBearer:
      type: http
      scheme: bearer
      bearerFormat: MCP Token
      description: Exclusive per-session MCP token (`Bearer mcp_...`)
    TenantKey:
      type: apiKey
      in: header
      name: X-Tenant-Key
      description: White-label tenant credential (`tnt_...`)
    BearerTenant:
      type: http
      scheme: bearer
      bearerFormat: Tenant Key
      description: Use `Bearer tnt_...`

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string

    ConnectQRRequest:
      type: object
      properties:
        phone_number:
          type: string
          description: Optional. Omit for anonymous QR (phone discovered after scan).
          example: "5511999999999"
        wait_seconds:
          type: integer
          description: Seconds to wait for QR generation (max 60). Default 20.
          example: 20

    ConnectCodeRequest:
      type: object
      required:
        - phone_number
      properties:
        phone_number:
          type: string
          example: "5511999999999"
        wait_seconds:
          type: integer
          example: 20

    ConnectStatus:
      type: object
      properties:
        uuid:
          type: string
        status:
          type: string
          enum: [pending, processed, success, expired, not_found, qr_ready, qr_pending, pairing_timeout, pending_user_verification]
        connect_type:
          type: string
          enum: [QR, code]
        phone_number:
          type: string
        qr_data:
          type: string
          description: Raw WhatsApp pairing string — render as QR on the frontend
        qr_generated_at:
          type: string
          format: date-time
        pairing_code:
          type: string
          example: "ABCD-1234"
        expires_at:
          type: string
          format: date-time
        api_key:
          type: string
        connected:
          type: boolean
        logged_in:
          type: boolean
        is_active_db:
          type: boolean
        message:
          type: string

    SessionStatus:
      type: object
      properties:
        phone_number:
          type: string
        in_memory:
          type: boolean
        connected:
          type: boolean
        logged_in:
          type: boolean
        is_active_db:
          type: boolean
        last_updated:
          type: string
        last_active:
          type: string
        session_uuid:
          type: string

    MCPEnableRequest:
      type: object
      properties:
        allow_send:
          type: boolean
          default: true
        allow_read:
          type: boolean
          default: true
        allow_provision:
          type: boolean
          default: false
          description: Legacy flag — prefer access_mode tenant_all or custom allow list
        access_mode:
          type: string
          enum: [self_only, tenant_all, custom]
          default: self_only
        allowed_phones:
          type: array
          items:
            type: string
          description: Phones this MCP may target when access_mode=custom
        blocked_phones:
          type: array
          items:
            type: string
          description: Phones explicitly denied even if otherwise allowed
        rotate_token:
          type: boolean
          default: false
        description:
          type: string
          example: "Production AI agent"
        public_base_url:
          type: string
          example: "https://bot.example.com"

    MCPConnectionDetails:
      type: object
      description: |
        Complete MCP connection package returned by POST /api/mcp/enable and GET /api/mcp/connection.
        AI agents should copy `cursor_mcp_example` into the host config, then use tools at `endpoint_url`.
      properties:
        phone_number:
          type: string
          example: "5511999999999"
        session_active:
          type: boolean
          example: true
        mcp_enabled:
          type: boolean
          example: true
        transport:
          type: string
          example: streamable-http
        protocol_version:
          type: string
          example: "2025-11-25"
        endpoint_url:
          type: string
          example: "https://wamcp.up.railway.app/mcp"
        authentication:
          type: object
          properties:
            type:
              type: string
              example: bearer
            token:
              type: string
              example: "mcp_a1b2c3..."
            header:
              type: string
              example: Authorization
            example:
              type: string
              example: "Bearer mcp_a1b2c3..."
        required_headers:
          type: object
          example:
            Authorization: "Bearer mcp_a1b2c3..."
            MCP-Protocol-Version: "2025-11-25"
            Accept: "application/json, text/event-stream"
            Content-Type: "application/json"
        tools:
          type: array
          description: All MCP tools available for this session
          items:
            type: string
          example:
            - whatsapp_send_message
            - whatsapp_list_messages
            - whatsapp_list_chats
            - whatsapp_list_contacts
            - whatsapp_get_session_status
            - whatsapp_reconnect
            - whatsapp_connect_code
            - whatsapp_connect_qr
            - whatsapp_connect_status
            - whatsapp_enable_mcp
        lifecycle_tools:
          type: array
          description: Tools that work when WhatsApp is offline (provision tools need allow_provision)
          items:
            type: string
          example:
            - whatsapp_reconnect
            - whatsapp_connect_code
            - whatsapp_connect_qr
            - whatsapp_connect_status
            - whatsapp_enable_mcp
        resources:
          type: array
          items:
            type: string
          example:
            - "whatsapp://session"
            - "whatsapp://messages/{chat_jid}"
        permissions:
          type: object
          properties:
            allow_send:
              type: boolean
            allow_read:
              type: boolean
            allow_provision:
              type: boolean
              description: When true, MCP can start connect flows for other phones in the tenant
        description:
          type: string
          example: "User AI assistant"
        cursor_mcp_example:
          type: object
          description: Ready-to-paste MCP server block for Cursor, Claude Desktop, etc.
          example:
            mcpServers:
              whatsapp-5511999999999:
                url: "https://wamcp.up.railway.app/mcp"
                headers:
                  Authorization: "Bearer mcp_a1b2c3..."
                  MCP-Protocol-Version: "2025-11-25"

    SendMessageRequest:
      type: object
      required:
        - recipient
      properties:
        recipient:
          type: string
          description: WhatsApp JID or phone number
          example: "5511888888888@s.whatsapp.net"
        content:
          type: string
        media_url:
          type: string
        media_type:
          type: string
          enum: [image, video, audio, document]
        context:
          type: string
          description: JSON string for quoted reply context

paths:
  /docs:
    get:
      summary: Swagger UI with AI Agent Playbook banner
      description: |
        Interactive API documentation. Includes a collapsible **AI Agent Playbook** panel with the
        end-to-end flow: pairing code → poll status → enable MCP → configure agent → manage.

        Public when `DOCS_PUBLIC=true` (default). Requires admin token when `REQUIRE_ADMIN_FOR_DOCS=true`.
      tags: [AI Agent Guide]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - {}
      responses:
        '200':
          description: Swagger UI HTML page with agent playbook banner
        '401':
          description: Admin token required

  /docs/agent-playbook:
    get:
      summary: Machine-readable AI agent onboarding playbook (JSON)
      description: |
        Structured JSON workflow for AI agents. Includes authentication roles, step-by-step pairing
        and MCP enablement, tool catalog, polling intervals, error handling, and doc URLs.

        **Recommended first fetch** for autonomous agents before calling any API endpoint.
      tags: [AI Agent Guide]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - {}
      responses:
        '200':
          description: Agent playbook JSON
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Admin token required

  /docs/openapi.yaml:
    get:
      summary: OpenAPI specification (YAML)
      description: Full API spec including AI agent workflow in `info.description`.
      tags: [AI Agent Guide]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - {}
      responses:
        '200':
          description: OpenAPI YAML spec
        '401':
          description: Admin token required

  /docs/openapi.json:
    get:
      summary: OpenAPI specification (JSON)
      description: Same spec as openapi.yaml, JSON-encoded for agents that prefer JSON parsers.
      tags: [AI Agent Guide]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - {}
      responses:
        '200':
          description: OpenAPI JSON spec
        '401':
          description: Admin token required

  /api/session/connect/qr:
    post:
      summary: Start QR code pairing
      description: |
        Creates a `whatsmeow_login_requests` row (type=QR), starts the WhatsApp client,
        and waits for a QR code. Returns `qr_data` and a link to render the QR as PNG.
        Supabase Realtime subscribers on `whatsmeow_qr_codes` will also receive updates.
      tags: [Connect]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - TenantKey: []
        - BearerTenant: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectQRRequest'
      responses:
        '200':
          description: QR code ready
        '202':
          description: Client started; poll status or qr-image endpoints
        '401':
          description: Unauthorized

  /api/session/connect/code:
    post:
      summary: Start 8-digit pairing code flow
      description: |
        **AI agent step 1 — generate pairing code for the user.**

        Creates a `whatsmeow_login_requests` row (type=code), resets any existing session,
        and returns `uuid`, `pairing_code`, and `expires_at`.

        **Agent action:** display `pairing_code` to the user and instruct them to enter it in
        WhatsApp → Settings → Linked Devices → Link with phone number.

        Then poll `GET /api/session/connect/status?uuid=<uuid>` every 3 seconds until `status=success`.
      tags: [AI Agent Guide, Connect]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - TenantKey: []
        - BearerTenant: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectCodeRequest'
      responses:
        '200':
          description: Pairing code generated — show pairing_code to user, save uuid for polling
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectStatus'
              example:
                uuid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                status: "pending_user_verification"
                connect_type: "code"
                phone_number: "5511999999999"
                pairing_code: "ABCD-1234"
                expires_at: "2026-06-27T12:05:00Z"
                connected: false
                logged_in: false
        '408':
          description: Timeout waiting for code — poll status endpoint
        '401':
          description: Unauthorized

  /api/session/connect/status:
    get:
      summary: Poll connect flow status
      description: |
        **AI agent step 2 — poll until WhatsApp is linked.**

        Returns QR data, pairing code, connection state, and — on success — the `api_key` (`wbot_...`).

        Poll every **3 seconds** for up to **120 seconds**. Terminal success: `status=success`.
        On success, store `api_key` and proceed to `POST /api/mcp/enable`.

        **Requires authorization** — same `X-Tenant-Key` that started the flow (prevents UUID enumeration).
      tags: [AI Agent Guide, Connect]
      security:
        - TenantKey: []
        - BearerTenant: []
        - ApiKeyAuth: []
        - BearerApiKey: []
        - BearerAdmin: []
        - AdminToken: []
      parameters:
        - name: uuid
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Status retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectStatus'
        '403':
          description: UUID does not belong to this tenant/session
        '404':
          description: UUID not found

  /api/session/connect/qr-image:
    get:
      summary: Render QR code as PNG
      description: Returns a scannable QR image. Requires the same auth as `/connect/status`.
      tags: [Connect]
      security:
        - TenantKey: []
        - BearerTenant: []
        - ApiKeyAuth: []
        - BearerApiKey: []
        - BearerAdmin: []
        - AdminToken: []
      parameters:
        - name: uuid
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: PNG image
          content:
            image/png:
              schema:
                type: string
                format: binary
        '403':
          description: UUID does not belong to this tenant/session
        '404':
          description: QR not ready yet

  /api/session/pair:
    post:
      summary: "[Legacy] Pairing code — alias of /connect/code"
      tags: [Connect]
      security:
        - BearerAdmin: []
        - AdminToken: []
        - TenantKey: []
        - BearerTenant: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectCodeRequest'
      responses:
        '200':
          description: Pairing code generated

  /api/session:
    get:
      summary: Get session status
      tags: [Session]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      responses:
        '200':
          description: Session info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionStatus'

  /api/session/reconnect:
    post:
      summary: Trigger reconnection
      tags: [Session]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      responses:
        '200':
          description: Reconnection started

  /api/session/disconnect:
    post:
      summary: Deactivate and disconnect session
      tags: [Session]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      responses:
        '200':
          description: Session deactivated

  /api/send:
    post:
      summary: Send a message
      tags: [Messages]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageRequest'
      responses:
        '200':
          description: Message sent

  /api/history:
    get:
      summary: List message history
      tags: [Messages]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      parameters:
        - name: chat_jid
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: start_date
          in: query
          schema:
            type: string
        - name: end_date
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Message list

  /mcp:
    post:
      summary: MCP Streamable HTTP transport (agent protocol)
      description: |
        **AI agent step 4 — listen and manage WhatsApp via MCP tools.**

        Connect your MCP host to this endpoint using the `mcp_` token from `/api/mcp/enable`.
        Required headers: `Authorization: Bearer mcp_...`, `MCP-Protocol-Version`, `Accept: application/json, text/event-stream`.

        **Messaging tools** (require active WhatsApp session):
        `whatsapp_send_message`, `whatsapp_list_messages`, `whatsapp_list_chats`,
        `whatsapp_list_contacts`, `whatsapp_get_session_status`

        **Lifecycle tools** (work when session is offline):
        `whatsapp_reconnect`, `whatsapp_connect_code`, `whatsapp_connect_qr`,
        `whatsapp_connect_status`, `whatsapp_enable_mcp`

        Provision tools (`connect_code`, `connect_qr`) require `allow_provision=true` on the MCP config.
        All tool calls are audited in `whatsmeow_mcp_audit`.
      tags: [AI Agent Guide, MCP]
      security:
        - MCPBearer: []
      responses:
        '200':
          description: MCP JSON-RPC response
        '401':
          description: Invalid MCP token
    get:
      summary: MCP SSE listener stream
      tags: [MCP]
      security:
        - MCPBearer: []
      responses:
        '200':
          description: SSE event stream

  /api/mcp/enable:
    post:
      summary: Enable MCP for the connected WhatsApp account
      description: |
        **AI agent step 3 — obtain MCP credentials and connection details.**

        Call after connect status is `success` and you have the `wbot_` API key.
        Generates (or rotates) an exclusive `mcp_` bearer token for this phone number.

        Response includes everything needed to configure an MCP host:
        - `endpoint_url` — POST tool calls here
        - `authentication.token` — `mcp_...` bearer token
        - `cursor_mcp_example` — paste into Cursor / Claude Desktop config
        - `tools` — full tool catalog for this session

        MCP is **auto-enabled** when WhatsApp pairing succeeds. Use this endpoint to rotate tokens
        or change permissions. Requires WhatsApp session `connected` and `logged_in`.
      tags: [AI Agent Guide, MCP]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MCPEnableRequest'
      responses:
        '200':
          description: MCP enabled with connection details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MCPConnectionDetails'
        '409':
          description: WhatsApp session not connected

  /api/mcp/disable:
    post:
      summary: Disable MCP for the connected account
      tags: [MCP]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      responses:
        '200':
          description: MCP disabled

  /api/mcp/config:
    get:
      summary: Get MCP configuration and connection details
      tags: [MCP]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      responses:
        '200':
          description: MCP config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MCPConnectionDetails'
    put:
      summary: Update MCP permissions and metadata
      tags: [MCP]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                allow_send:
                  type: boolean
                allow_read:
                  type: boolean
                allow_provision:
                  type: boolean
                access_mode:
                  type: string
                  enum: [self_only, tenant_all, custom]
                allowed_phones:
                  type: array
                  items:
                    type: string
                blocked_phones:
                  type: array
                  items:
                    type: string
                description:
                  type: string
                public_base_url:
                  type: string
      responses:
        '200':
          description: Updated MCP config

  /api/tenant/mcp:
    get:
      summary: Tenant MCP inventory and active sessions
      description: |
        Lists all connected WhatsApp numbers, MCP integrations, access rules, and currently active
        MCP HTTP sessions (`whatsmeow_mcp_active_sessions`) for the tenant. Requires `X-Tenant-Key`.
      tags: [Tenant MCP]
      security:
        - TenantKeyAuth: []
      responses:
        '200':
          description: Tenant MCP overview

  /api/tenant/mcp/audit:
    get:
      summary: Tenant MCP access audit log
      description: Recent MCP tool calls and auth events from `whatsmeow_mcp_audit`.
      tags: [Tenant MCP]
      security:
        - TenantKeyAuth: []
      parameters:
        - name: phone_number
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Audit events

  /api/tenant/mcp/access:
    get:
      summary: Get MCP access rules for one phone
      tags: [Tenant MCP]
      security:
        - TenantKeyAuth: []
      parameters:
        - name: phone_number
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Access rules
    put:
      summary: Update MCP access rules or enable/disable integration
      description: |
        Control which WhatsApp numbers an MCP integration may access under the tenant.
        `access_mode`: `self_only` (default), `tenant_all`, or `custom` (use `allowed_phones`).
        `blocked_phones` always denies listed numbers.
      tags: [Tenant MCP]
      security:
        - TenantKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number:
                  type: string
                access_mode:
                  type: string
                  enum: [self_only, tenant_all, custom]
                allowed_phones:
                  type: array
                  items:
                    type: string
                blocked_phones:
                  type: array
                  items:
                    type: string
                allow_provision:
                  type: boolean
                is_enabled:
                  type: boolean
      responses:
        '200':
          description: Updated access rules

  /api/mcp/connection:
    get:
      summary: Get live MCP connection details for AI agents
      description: |
        **AI agent step 3 (re-fetch) — recover MCP connection details.**

        Returns `endpoint_url`, `mcp_` bearer token, `tools`, `resources`, and `cursor_mcp_example`.
        Use when the agent host lost config but still has the `wbot_` API key.

        If MCP is not yet enabled, returns `next_step: POST /api/mcp/enable` with `mcp_enabled: false`.
        Returns 409 when WhatsApp session is offline — reconnect first.
      tags: [AI Agent Guide, MCP]
      security:
        - ApiKeyAuth: []
        - BearerApiKey: []
      responses:
        '200':
          description: Connection details or next-step instructions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MCPConnectionDetails'
        '409':
          description: WhatsApp session not active

  /api/admin/tenants:
    get:
      summary: List white-label tenants
      tags: [Tenants]
      security:
        - BearerAdmin: []
        - AdminToken: []
      responses:
        '200':
          description: Tenant list (tenant keys masked)
    post:
      summary: Create a white-label tenant
      description: Returns the full `tenant_key` once — store it securely on the partner system.
      tags: [Tenants]
      security:
        - BearerAdmin: []
        - AdminToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [slug, name]
              properties:
                slug:
                  type: string
                  example: acme-corp
                name:
                  type: string
                  example: Acme Corp WhatsApp
                allowed_origins:
                  type: string
                  description: Comma-separated CORS origins for this tenant's frontends
      responses:
        '201':
          description: Tenant created with tenant_key

  /api/admin/mcp:
    get:
      summary: List MCP integrations
      description: Returns all MCP configs with masked tokens. Filter by tenant_id or phone_number.
      tags: [Admin]
      security:
        - BearerAdmin: []
        - AdminToken: []
      parameters:
        - name: tenant_id
          in: query
          schema:
            type: integer
        - name: phone_number
          in: query
          schema:
            type: string
      responses:
        '200':
          description: MCP integration list

  /api/admin/mcp/audit:
    get:
      summary: MCP access audit log
      description: |
        Per-request MCP audit trail stored in `whatsmeow_mcp_audit` (Supabase Postgres).
        Includes tool calls, auth failures, rate limits, IP, latency, and tenant scope.
      tags: [Admin]
      security:
        - BearerAdmin: []
        - AdminToken: []
      parameters:
        - name: tenant_id
          in: query
          schema:
            type: integer
        - name: phone_number
          in: query
          schema:
            type: string
        - name: action
          in: query
          schema:
            type: string
          description: Prefix filter (e.g. mcp_tool_, mcp_auth_)
        - name: success
          in: query
          schema:
            type: boolean
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Audit events

  /api/admin/keys:
    get:
      summary: List API keys
      description: API keys are masked in list responses. Filter by `tenant_id` query param.
      tags: [Admin]
      security:
        - BearerAdmin: []
        - AdminToken: []
      parameters:
        - name: tenant_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Key list (masked)
    post:
      summary: Generate API key for a phone
      tags: [Admin]
      security:
        - BearerAdmin: []
        - AdminToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number:
                  type: string
                tenant_id:
                  type: integer
                  description: Bind key to a white-label tenant
      responses:
        '201':
          description: Key created