> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keebai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /v1/channels/whatsapp/connect

> Kick off the connection flow for a new WhatsApp Business channel from your CLI or backend, without going through the portal.

There are **two modes** to connect a WhatsApp channel:

* `mode: "embedded_signup"` (default) — Meta's official flow for WhatsApp Business Cloud API. Requires a human to complete the [Embedded Signup](https://developers.facebook.com/docs/whatsapp/embedded-signup) in a browser. This is what `keebai whatsapp connect` does.
* `mode: "qr"` — multi-device QR pairing (no browser, no official WABA). The endpoint starts a session and returns an SSE `stream_url`; the client receives the QR, scans it with WhatsApp mobile, and when done receives the `connected` event with the `channel_id`. This is what `keebai whatsapp connect --qr` does. Documented in detail at [POST /v1/channels/whatsapp/connect (QR mode)](/dev/endpoints/channels-whatsapp-connect-qr).

## General flow

<Steps>
  <Step title="Your backend starts the session">
    `POST /v1/channels/whatsapp/connect`. Returns `session_id` + `verification_uri`.
  </Step>

  <Step title="Show the URL to the user">
    The user opens it in a browser, logs into the Keebai portal (if not already), and starts Meta's embedded signup.
  </Step>

  <Step title="Poll status every 5 seconds">
    `GET /v1/channels/whatsapp/connect/:session_id` until `status: completed`. The session expires in 15 minutes.
  </Step>

  <Step title="Receive the created channel">
    The polling response with `status: completed` includes `channel: { id, phone_number, ... }`.
  </Step>
</Steps>

## Start a session

`POST /v1/channels/whatsapp/connect`

**Required scope:** `channels:connect`

### Request

```bash theme={null}
curl -X POST https://api.keebai.com/v1/channels/whatsapp/connect \
  -H "Authorization: Bearer kbai_pk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "pipeline_id": "pip_01HXYZ...",
    "coexistence_enabled": false
  }'
```

| Field                 | Type                        | Required | Description                                                                                                                                                            |
| --------------------- | --------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`                | `"embedded_signup" \| "qr"` | no       | Default `embedded_signup`. When set to `qr`, the rest of the fields change — see [QR mode](/dev/endpoints/channels-whatsapp-connect-qr).                               |
| `pipeline_id`         | string                      | no       | (`embedded_signup` only) Id of the pipeline to attach the new channel to. If omitted, the portal uses the default pipeline.                                            |
| `coexistence_enabled` | boolean                     | no       | (`embedded_signup` only) `true` if the number is already registered in the traditional WhatsApp Business App and you want coexistence with Cloud API. Default `false`. |
| `name`                | string                      | no       | (`qr` only) Label for the QR channel. Default `"WhatsApp QR"`.                                                                                                         |

### Response 201

```json theme={null}
{
  "session_id": "cs_01HXYZ...",
  "verification_uri": "https://app.keebai.com/connect/whatsapp?session=cs_01HXYZ...",
  "expires_in": 900,
  "poll_interval": 5
}
```

| Field              | Type   | Description                                                             |
| ------------------ | ------ | ----------------------------------------------------------------------- |
| `session_id`       | string | Session id. Use it to poll status.                                      |
| `verification_uri` | string | Portal URL the user must open. Auto-triggers the embedded signup modal. |
| `expires_in`       | number | Seconds until the session expires (15 minutes).                         |
| `poll_interval`    | number | Suggested seconds between polls.                                        |

## Poll status

`GET /v1/channels/whatsapp/connect/:session_id`

**Required scope:** `channels:connect`

### Request

```bash theme={null}
curl https://api.keebai.com/v1/channels/whatsapp/connect/cs_01HXYZ \
  -H "Authorization: Bearer kbai_pk_xxx"
```

### Response 200

While the user hasn't completed signup:

```json theme={null}
{
  "status": "pending"
}
```

When embedded signup finishes successfully:

```json theme={null}
{
  "status": "completed",
  "channel": {
    "id": "ch_01HXYZ...",
    "name": "Soporte Chile",
    "type": "whatsapp",
    "is_active": true,
    "phone_number": "+56987654321",
    "phone_number_id": "1234567890",
    "business_account_id": "9876543210"
  }
}
```

Terminal states:

| Status      | Meaning                                                                   |
| ----------- | ------------------------------------------------------------------------- |
| `completed` | Channel created. It started receiving Meta webhooks immediately.          |
| `expired`   | 15 minutes elapsed without completing. Start a new session.               |
| `cancelled` | The user cancelled from the portal. Start a new one if you want to retry. |

## CLI equivalent

```bash theme={null}
keebai whatsapp connect [--pipeline pip_01HXYZ] [--coexistence]
```

The CLI does exactly this flow: creates the session, opens the URL in your browser, polls status, and on completion prints `Connected channel ch_xxx (phone_number_id: ...)`.

## Operational notes

<CardGroup cols={2}>
  <Card title="Cannot run 100% backend">
    Meta requires human browser interaction for embedded signup. Your UI needs a visible step for the user.
  </Card>

  <Card title="Subscribe to `whatsapp.channel.connected`">
    If you'd rather not poll, subscribe to the [webhook](/dev/webhooks/events#whatsapp-channel-connected). Your endpoint receives the event when the channel completes, no polling needed.
  </Card>

  <Card title="Coexistence with WABA Mobile App">
    If the number is already active in the traditional Business app, set `coexistence_enabled: true`. Meta syncs the history; the portal reflects it when activating.
  </Card>

  <Card title="The PAT determines the destination company">
    The channel is attached to the company of the PAT that started the session. There's no way to create the channel in a different company from the API.
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /v1/channels/whatsapp/connect
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/channels/whatsapp/connect:
    post:
      tags:
        - channels
      summary: >-
        Inicia un flow de conexión de WhatsApp Business desde el CLI. Devuelve
        una URL del portal donde el usuario completa el embedded signup.
      operationId: PublicChannelsController_initiateWhatsappConnect
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WhatsappConnectRequestDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicConnectSessionResponseDto'
      security:
        - PAT: []
components:
  schemas:
    WhatsappConnectRequestDto:
      type: object
      properties:
        mode:
          type: string
          description: >-
            Modo de conexión: `embedded_signup` (default) abre el flujo de Meta
            en el portal; `qr` devuelve un stream SSE para escanear desde la
            terminal con WhatsApp multi-dispositivo.
          enum:
            - embedded_signup
            - qr
          default: embedded_signup
        pipeline_id:
          type: string
          description: >-
            Si se especifica, el canal recién creado se asocia automáticamente
            al pipeline. Sólo aplica al modo embedded_signup.
        coexistence_enabled:
          type: boolean
          description: >-
            Activa el modo coexistencia (si el cliente ya tiene la app de
            WhatsApp Business activa). Sólo aplica al modo embedded_signup.
        name:
          type: string
          description: >-
            Etiqueta visible del canal cuando se conecta por QR. Default:
            "WhatsApp QR".
    PublicConnectSessionResponseDto:
      type: object
      properties:
        session_id:
          type: string
        provider:
          type: string
          description: whatsapp | whatsapp_qr
        mode:
          type: string
          description: embedded_signup | qr
        verification_uri:
          type: string
          description: URL del portal donde el usuario completa el Embedded Signup
        stream_url:
          type: string
          description: Ruta del stream SSE de pairing, sólo en modo qr
        expires_in:
          type: number
          description: Segundos hasta que la sesión expira
        poll_interval:
          type: number
          description: Segundos sugeridos entre polls
      required:
        - session_id
        - provider
        - mode
        - expires_in
        - poll_interval
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````