> Source: https://wexa.ai/docs/connectors/complete-whatsapp-connection

# complete-whatsapp-connection

Finish connecting a WhatsApp connector that [`connect-whatsapp`](/docs/connectors/connect-whatsapp) started, the same as **Finish authentication** in the console. Call it once after the QR code was scanned. Needs the `fabric:connector.connect` grant.

**Finish connecting WhatsApp**

**Wexa MCP server**

```bash
curl -sS -X POST https://fabric.wexa.ai/mcp/$FABRIC_PROJECT_ID \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "Mcp-Session-Id: $MCP_SESSION_ID" \
  -H "content-type: application/json" -H "accept: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"complete-whatsapp-connection","arguments":{"connector_id":"6b0f2c9e4a1d8e7f3c2b1a90"}}}'
```

```json
{
  "content": [
    {
      "type": "text",
      "text": "{\"lifecycle_id\":\"qlc_000004\",\"result\":{\"connector_id\":\"6b0f2c9e4a1d8e7f3c2b1a90\",\"name\":\"Support WhatsApp\",\"status\":\"ready\",\"connected\":true}}"
    }
  ],
  "isError": false
}
```

**REST API**

```bash
curl -sS https://fabric.wexa.ai/v1/complete-whatsapp-connection \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "content-type: application/json" \
  -d '{"connector_id":"6b0f2c9e4a1d8e7f3c2b1a90"}'
```

```json
{
  "lifecycle_id": "qlc_000004",
  "result": {
    "connector_id": "6b0f2c9e4a1d8e7f3c2b1a90",
    "name": "Support WhatsApp",
    "status": "ready",
    "connected": true
  }
}
```

**TypeScript SDK**

```ts
import { Fabric } from '@wexa-fabric/sdk'

const fabric = new Fabric()
const result = await fabric.completeWhatsAppConnection({
  connector_id: '6b0f2c9e4a1d8e7f3c2b1a90'
})
```

```ts
// result is the payload itself, with no `.result` to read
{
  connector_id: '6b0f2c9e4a1d8e7f3c2b1a90',
  name: 'Support WhatsApp',
  status: 'ready',
  connected: true
}
```

**Python SDK**

```python
from wexa import Fabric

fabric = Fabric()
out = fabric.complete_whatsapp_connection(
    connector_id="6b0f2c9e4a1d8e7f3c2b1a90",
)
```

```python
# out is the result, already unwrapped
{
    "connector_id": "6b0f2c9e4a1d8e7f3c2b1a90",
    "name": "Support WhatsApp",
    "status": "ready",
    "connected": True
}
```

## Parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `connector_id` | string | Required | The `connector_id` that `connect-whatsapp` returned. A string. One that is not a WhatsApp connector this project may use is refused (403). |

**Errors**

- `ForbiddenError` (403): your access does not include the WhatsApp connector (`S8:whatsapp-connect-refused`). The message asks you to have an org admin grant it.
- `ValidationError` (400): the platform refused the connect request (`S8:whatsapp-connect-refused`), with its own message, the same one the console shows.
- `ValidationError` (422): the stored configuration is not valid (`S8:whatsapp-connect-refused`).
- `UpstreamError` (502): WhatsApp's provider rejected the link request. The message reads `WhatsApp provider (Unipile) rejected the connect request (status N). Please verify the phone number and try again.`

**Show full response**

An example response, after the QR code was scanned. The connector is ready.

**Result of complete-whatsapp-connection**

**REST API**

```json
{
  "lifecycle_id": "qlc_000004",
  "result": {
    "connector_id": "6b0f2c9e4a1d8e7f3c2b1a90",
    "name": "Support WhatsApp",
    "status": "ready",
    "connected": true,
    "next_step": "The WhatsApp account is connected and the connector is ready to use."
  }
}
```

**Wexa MCP server**

```json
{
  "content": [
    {
      "type": "text",
      "text": "{\"lifecycle_id\":\"qlc_000004\",\"result\":{\"connector_id\":\"6b0f2c9e4a1d8e7f3c2b1a90\",\"name\":\"Support WhatsApp\",\"status\":\"ready\",\"connected\":true,\"next_step\":\"The WhatsApp account is connected and the connector is ready to use.\"}}"
    }
  ],
  "isError": false
}
```

**TypeScript SDK**

```ts
// the object you get back IS the result — there is no `.result` to read
{
  "connector_id": "6b0f2c9e4a1d8e7f3c2b1a90",
  "name": "Support WhatsApp",
  "status": "ready",
  "connected": true,
  "next_step": "The WhatsApp account is connected and the connector is ready to use."
}

// The TS SDK names these camelCase — lifecycleId, traceId — where Python uses
// lifecycle_id and trace_id. Both are non-enumerable: readable, but omitted by
// Object.keys() and JSON.stringify(). `result.lifecycleId` here is undefined.
result.lifecycleId // 'qlc_000004'
```

**Python SDK**

```python
# a dict subclass; `result` is already unwrapped
{
 "connector_id": "6b0f2c9e4a1d8e7f3c2b1a90",
 "name": "Support WhatsApp",
 "status": "ready",
 "connected": True,
 "next_step": "The WhatsApp account is connected and the connector is ready to use."
}

out.lifecycle_id  # 'qlc_000004' — an attribute, not a key
```

When the code was not scanned, the answer has the shape that
[`connect-whatsapp`](/docs/connectors/connect-whatsapp) returns for a pending connector: `status` is
`pending`, `connected` is `false`, and `connect_url`, `link_type` and `link_expires_at` describe the new link.

Keeping the `lifecycle_id` is what lets you ask later why a call was allowed, refused or held.

**More details**

**Response fields**

- `connector_id`: The connector you named.
- `name`: The connector's name.
- `status`: `pending` while the account is not linked, `ready` once it is.
- `connected`: `true` when the WhatsApp account is linked, whatever `status` says.
- `connect_url`: Present only while a scan is awaited: the new page with the QR code.
- `link_type`: Present with `connect_url`: `create` for a new link, `reconnect` for one that re-links the account.
- `link_expires_at`: Present with `connect_url`: when the link stops working, as an RFC 3339 time, 10 minutes after it was issued.
- `next_step`: What to do next, in words.

The connector's configuration, and the PIN in it, is never returned.

The call checks the account with WhatsApp's provider again:

- The QR code was scanned: `status` is `ready` and `connected` is `true`.
- The code was not scanned, the scan failed, or the link expired: `status` stays `pending`, and a new `connect_url` is issued.
- The connector was already connected: it is reported as connected, and nothing is changed.

A failed scan is not an error. It is not stored as an error on the connector: the connector stays `pending`, and this call issues a new link to scan. That is the same handling as Slack and the other connectors whose account is linked on a hosted page. When the code was not scanned, the answer has the shape that [`connect-whatsapp`](/docs/connectors/connect-whatsapp) returns for a pending connector: `status` is `pending`, `connected` is `false`, and `connect_url`, `link_type` and `link_expires_at` describe the new link.

Each call on a connector that was not scanned issues a new link, which replaces the one before it. Call it once after the scan, not in a loop. To watch a connector without changing it, use [`get-whatsapp-connection`](/docs/connectors/get-whatsapp-connection).

The connector's configuration, and the PIN in it, is never returned. The connector id in the example is illustrative. Swap it for the one `connect-whatsapp` returned to you.

**Waiting for the scan.** With `wait`, both SDKs make this call once, then poll `get-whatsapp-connection` every `poll` seconds until `connected` is `true`, and return that status. A connector that is already connected is returned at once. When the time limit is reached, the SDK calls `complete-whatsapp-connection` once more and returns its answer: connected when the scan landed, and otherwise still pending with a new `connect_url`. Nothing is raised at the limit.

The SDKs also take three options that are not sent to the platform:

- `wait` (TypeScript `wait`): off by default. Poll `get-whatsapp-connection` until the account is connected.
- `wait_timeout` (TypeScript `waitTimeout`): `600` by default. The most seconds to wait, which is the lifetime of the QR link.
- `poll` (TypeScript `poll`): `5` by default. Seconds between two polls.

Every call also writes a `complete-whatsapp-connection.run` audit event, with the outcome: `connected`, `awaiting_scan` or `failed`.

Not marked consequential, so it does not wait for approval. It needs the same grant as `connect-whatsapp`, `fabric:connector.connect`, because a call on an unscanned connector issues a new link.

Every tool call works the same way: [How every tool call works](/docs/sdks-and-apis/how-every-tool-call-works).
