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

# Connect channels from your product

> Let your users connect WhatsApp, Messenger, Instagram or TikTok inside your own app, through a popup we host.

If you build on Linkiasoft, your users shouldn't have to leave your product to connect a WhatsApp number or a Facebook Page. A **connect session** gives you a one-time link to a page we host. You open it in a popup, the person signs in with Meta or TikTok there, and the result comes back to your window.

You never handle Meta or TikTok credentials, and you don't need your own Meta app.

| Platform | `platform` | Signs in with |
| - | - | - |
| WhatsApp Business | `whatsapp` | Facebook — a new number, or the WhatsApp Business app already on the phone |
| WhatsApp (QR code) | `whatsapp_qr` | No sign-in — the person scans a QR code or types a pairing code on the phone, like WhatsApp Web |
| Messenger | `facebook` | Facebook — the person picks one Page |
| Instagram | `instagram` | Instagram professional account |
| TikTok | `tiktok` | TikTok account |

**The channel is always created in the workspace that owns the API key.** A connect session cannot add a channel anywhere else.

## How it works

**1. Your backend creates a session**

Call [`POST /v1/connect-sessions`](https://docs.linkiasoft.com/api-reference/connect-sessions/create-a-connect-link) with a key that holds `channels:manage`. You get back a `connectUrl`.

**2. Your frontend opens it in a popup**

The popup goes straight on to Meta or TikTok to sign in. For `whatsapp_qr` it shows a QR code to scan instead.

**3. The popup reports back and closes**

It sends the result to your window with `postMessage`, or redirects to your `redirectUri` when there is no window to notify.

**4. Your backend confirms**

Read [`GET /v1/connect-sessions/{id}`](https://docs.linkiasoft.com/api-reference/connect-sessions/get-a-connect-session), or subscribe to the `connect_session.completed` [webhook](https://docs.linkiasoft.com/webhooks.md). The server record is the truth; the browser message is a hint.

## 1. Create a session

Do this on your server — it needs your API key, which must never reach a browser.

```bash theme={null}
curl -X POST https://social.linkiasoft.com/api/v1/connect-sessions \
  -H "x-api-key: lk_live_..." \
  -H "X-Tenant-Id: 8f6a2b3c-4d5e-4a7b-9c1e-3f2b1a9c0d4e" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "whatsapp",
    "origin": "https://app.example.com",
    "state": "user-4821"
  }'
```

```json theme={null}
{
  "id": "6f1c8e2a-9b4d-4c7e-8a13-2d5f0b7c9e41",
  "platform": "whatsapp",
  "status": "pending",
  "connectUrl": "https://crm.linkiasoft.com/connect#token=cst_...",
  "channelId": null,
  "channelName": null,
  "state": "user-4821",
  "createdAt": "2026-10-22T09:00:00.000Z",
  "expiresAt": "2026-10-22T09:30:00.000Z",
  "completedAt": null
}
```

| Field | What it does |
| - | - |
| `platform` | Required. `whatsapp`, `whatsapp_qr`, `facebook`, `instagram` or `tiktok`. |
| `channelName` | `whatsapp_qr` only. The channel's name in Linkiasoft. Defaults to the linked number, such as `+201001234567`. |
| `origin` | The origin of the page that opens the popup, such as `https://app.example.com`. The result is posted to `window.opener` at exactly this origin, and to nothing else. |
| `redirectUri` | Where the popup goes when it has no opener — for example if your page was closed or opened the link in a full tab. |
| `state` | Your own reference — a user id, an onboarding step. Echoed back untouched in every result. |
| `locale` | `en` (default) or `ar` — the language of the hosted page. |

Give `origin`, `redirectUri`, or both. Both must use `https`; plain `http` is accepted only for `localhost`, so you can test locally.

> [!WARNING]
>
> The `connectUrl` is a credential. It works **once** and expires after **30 minutes**. Create a fresh session for every attempt, don't cache it, and don't write it to logs. The token sits in the URL fragment, so it never reaches a server log or a `Referer` header on its own — keep it that way.

## 2. Open the popup

Open it directly from the click handler. A popup opened after an `await` is usually blocked by the browser.

```javascript theme={null}
async function connectWhatsApp() {
  // Open first, then load the URL into it — keeps the popup inside the click.
  const popup = window.open('', 'linkia_connect', 'width=520,height=720');
  const { connectUrl } = await fetch('/api/linkia/connect-session', { method: 'POST' })
    .then((r) => r.json());
  popup.location.href = connectUrl;
}

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://crm.linkiasoft.com') return;
  if (event.data?.source !== 'linkia-connect') return;

  const { sessionId, status, platform, channelId, channelName, state } = event.data;
  // status: 'connected' | 'cancelled'
  confirmOnServer(sessionId);
});
```

Always check `event.origin`. Any page can post a message to your window; only one from `https://crm.linkiasoft.com` with `source: 'linkia-connect'` is ours.

### Without an opener: the redirect

When the popup can't reach your window it navigates to your `redirectUri` with the result in the query string:

```
https://app.example.com/done?sessionId=6f1c…&status=connected&channelId=b21e…&state=user-4821
```

Your existing query parameters are kept.

## 3. Confirm on your server

```bash theme={null}
curl https://social.linkiasoft.com/api/v1/connect-sessions/6f1c8e2a-9b4d-4c7e-8a13-2d5f0b7c9e41 \
  -H "x-api-key: lk_live_..." \
  -H "X-Tenant-Id: 8f6a2b3c-4d5e-4a7b-9c1e-3f2b1a9c0d4e"
```

| `status` | Meaning |
| - | - |
| `pending` | Not finished yet. |
| `connected` | Done. `channelId` is the new channel — use it anywhere this API asks for a `channelId`. |
| `cancelled` | The person pressed **Cancel**, or you [cancelled the session](https://docs.linkiasoft.com/api-reference/connect-sessions/cancel-a-connect-session.md). |
| `expired` | The 30-minute link ran out before the person finished. Create a new session. |

The session stays readable for **24 hours**.

If the person simply closes the popup, nothing is sent — the session stays `pending` until it expires. Treat a closed popup with no message as "not connected" and let them try again.

### Or listen for the webhook

Subscribe an endpoint to `connect_session.completed` under **Integrations → Webhooks**. It fires when a session ends as `connected` or `cancelled`:

```json theme={null}
{
  "event": "connect_session.completed",
  "source": "social",
  "data": {
    "session_id": "6f1c8e2a-9b4d-4c7e-8a13-2d5f0b7c9e41",
    "status": "connected",
    "platform": "whatsapp",
    "channel_id": "b21e7d40-3a55-4f89-9c02-77ab1e6d3c88",
    "channel_name": "Acme Support",
    "state": "user-4821"
  }
}
```

Sessions that expire unused don't fire it.

## Things to know

**One session, one channel.** If the person's Facebook login covers several Pages, they pick one. Run another session for the next Page.

**Workspace rules still apply.** A session is refused up front with `403` if your workspace isn't enabled for the direct Meta connection for that channel type, and the connection itself fails if the plan's channel limit is reached. The popup shows the reason to the person.

**Reconnecting is the same call.** Connecting a number, Page or account that is already a channel in your workspace refreshes it rather than adding a duplicate — `channelId` is the existing channel's id.

**WhatsApp may finish with a warning.** If Meta saves the number but won't register it for the Cloud API yet, the session still reports `connected` and the channel shows as needing action in Linkiasoft. Check its `connection_status` with [`GET /v1/connect/channels`](https://docs.linkiasoft.com/api-reference/channels/list-your-connected-channels) before sending.

**WhatsApp by QR code.** With `whatsapp_qr` the popup shows a QR code straight away; the person opens WhatsApp → Settings → Linked devices → Link a device and scans it. A *Use phone number* tab gives an 8-character pairing code instead, for when the phone is the only screen. The number keeps working in the WhatsApp app as before. This is an unofficial (WhatsApp Web) connection, so it can't send WhatsApp message templates — use `whatsapp` when you need them. The plan's channel limit is checked when you create the session, so a full workspace gets `403` before the popup opens.

**Pop-up blockers.** Everything happens inside the one popup you open: the provider sign-in loads in that same window and comes back to our page, so no second window is opened. Open the popup from the person's click (not after an `await`) so blockers allow it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.