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

# Channel Connect

> Give a customer a link that connects their own WhatsApp, Facebook, Instagram or TikTok account, or connect a Telegram bot, with no dashboard login.

> [!NOTE]
>
> Private beta, and switched on per partner. `GET /api/partner/channel-connect/settings`
> tells you whether it is on for you.

Your software gives one of your customers a link. They open it, connect their
own WhatsApp number on Meta's dialog, and the channel appears in their
workspace. Nobody logs into the dashboard, and no provider token ever reaches
your software or their browser.

All paths are relative to `/api/partner`, authenticated by your partner key as
`X-API-KEY`, from your backend only.

## What you can connect

| Platform | How |
| - | - |
| `whatsapp` | A hosted link, then Meta's own signup dialog. Add `"coexistence": true` to keep the number on the WhatsApp Business app. |
| `facebook` | A hosted link, Facebook Login, then your customer picks the Page. |
| `instagram` | The same, for an Instagram professional account on one of their Pages. |
| `tiktok` | A hosted link and the TikTok Business login. One login is one account. |
| Telegram | No link needed — post the BotFather token. |

> [!NOTE]
>
> Which platforms are available depends on how OnlySocial is configured, so ask
> rather than assume: `GET /api/partner/channel-connect/settings` returns the
> list. A platform that is not on it is refused with a `422` and
> `"error_code": "provider_not_supported"`.

> [!WARNING]
>
> `instagram_login` — Instagram without a Facebook Page — is **not** supported and
> is always refused. An Instagram account connects through `instagram` if it has a
> Page, and not at all if it does not.
>
> Channel Connect is about **messaging** channels. Connecting a Page or an
> Instagram account does also connect it for publishing (that is how channels work
> here), but if you want a customer's accounts connected for publishing across
> every platform, use the dashboard's client-invite link.

## 1. Register your return URLs

```bash theme={null}
curl -X PUT https://app.onlysocial.io/api/partner/channel-connect/settings \
  -H "X-API-KEY: $PARTNER_KEY" -H "Content-Type: application/json" \
  -d '{"return_urls":["https://app.acmedealers.test/integrations/callback"]}'
```

Up to 20 HTTPS URLs, no credentials, query strings or fragments. **Matching is
exact**, including the path and any trailing slash. Removing a URL also stops
unfinished sessions from completing against it.

## 2. Create a connection

```bash theme={null}
curl -X POST https://app.onlysocial.io/api/partner/workspaces/$WORKSPACE/channel-connections \
  -H "X-API-KEY: $PARTNER_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: dealer-4821-whatsapp-1" \
  -d '{
    "platform": "whatsapp",
    "return_url": "https://app.acmedealers.test/integrations/callback",
    "state": "dealer-4821"
  }'
```

```json Response theme={null}
{
  "success": true,
  "data": {
    "id": "7f2c1a84-....",
    "workspace_id": "6b1f0e2c-....",
    "platform": "whatsapp",
    "coexistence": false,
    "status": "pending",
    "channel_id": null,
    "error_code": null,
    "state": "dealer-4821",
    "expires_at": "2026-10-20T09:42:00+00:00",
    "connect_url": "https://app.onlysocial.io/partner-connect/7f2c1a84-...?token=..."
  }
}
```

Open `connect_url` as a **top-level navigation** in your customer's browser —
not an iframe, not a fetch. Everything after they click Continue is bound to
that browser. The session lasts **30 minutes**.

For `facebook` and `instagram` your customer then picks which Page or account to
connect, on the same hosted page — the status is `selecting` while it waits for
them. `tiktok` grants one account, so that step is a single confirmation, and
WhatsApp's number selection happens inside Meta's own dialog.

`state` comes back to you on the return URL and in the completion event, so use
it to match the session to your own customer. Do not put secrets or personal
data in it; at most 500 characters.

`Idempotency-Key` (1–200 characters) is optional. The same key and the same
request returns the same connection with `200`; a different request on that key
is a `409`. Only a pending, unexpired session carries a `connect_url` — use a
new key to start over.

To **reconnect** an existing channel, pass its uuid as `channel_id`.

## 3. Read the result

Your customer comes back to your registered URL with `connection_id` and
`state`. That redirect is a hint, not the answer — read the authoritative
result:

```bash theme={null}
curl https://app.onlysocial.io/api/partner/workspaces/$WORKSPACE/channel-connections/$CONNECTION \
  -H "X-API-KEY: $PARTNER_KEY"
```

| `status` | Meaning |
| - | - |
| `pending` | The link has not been used yet. |
| `authorizing` / `selecting` | In progress. |
| `connected` | Done. `channel_id` is the new channel. |
| `failed` | See `error_code`. |
| `cancelled` | The customer denied or closed the dialog, or you cancelled. |
| `expired` | The 30 minutes ran out. |

`connected` means the number was stored and subscribed. Whether messages
actually flow still depends on Meta account eligibility and messaging rules.

`POST …/channel-connections/{connection}/cancel` cancels an unfinished session.
Repeating it is safe, and it never disconnects a channel that already connected.

## Or subscribe

Rather than polling, subscribe the workspace's webhooks (with the `osk_` key you
issued for it) to `salesbot.channel.connection.completed`, `.failed`,
`.cancelled` and `.expired`. See [Events](https://api.onlysocial.io/webhooks/events.md#channel-connect).

Delivery is at least once — **deduplicate on the envelope's `id`**.

## Telegram

```bash theme={null}
curl -X POST https://app.onlysocial.io/api/partner/workspaces/$WORKSPACE/channels/telegram \
  -H "X-API-KEY: $PARTNER_KEY" -H "Content-Type: application/json" \
  -d '{"bot_token":"123456:ABC...","name":"Acme Motors"}'
```

`201` for a new channel, `200` for the same bot again (its channel is refreshed,
not duplicated).

A `422` on `bot_token` means the token was rejected, or the bot is
OnlySocial's own bot, or it is already connected to another workspace, or the
plan's limit is reached.

A `422` on `takeover` means the bot's webhook points somewhere else. Connecting
it here stops that from receiving the bot's messages, so resend with
`"takeover": true` only if your customer confirms.

## Read channels

* `GET /api/partner/workspaces/{workspace}/channels` — `per_page` up to 100.
* `GET /api/partner/workspaces/{workspace}/channels/{channel}`

Both return the public channel fields only: never channel data or access tokens.

## Error codes

| Code | Meaning |
| - | - |
| `provider_not_supported` | That platform is not available on OnlySocial. Read `GET /channel-connect/settings` for the list. |
| `provider_not_configured` | The platform is not set up for that channel yet. Contact OnlySocial support. |
| `provider_unavailable` | A transient failure reaching the provider. Retry with a new session. |
| `provider_request_failed` | The provider answered with something unusable. |
| `token_exchange_failed` | The provider would not exchange the authorization. |
| `required_permissions_missing` | Your customer withheld a permission the channel cannot work without. Ask them to retry and accept the messaging permissions. |
| `credentials_expired` | The grant expired before they finished choosing. Start a new session. |
| `no_eligible_accounts` | Nothing connectable was granted — no Page, or no Instagram professional account on any Page they granted. |
| `asset_discovery_incomplete` | The provider's account list did not finish. Retry with a new session. |
| `authorization_not_completed` | The dialog was closed or denied, or nothing could be stored — already connected elsewhere, the plan's limits are full, or a permission was withheld. |
| `account_already_connected_elsewhere` | That Page, profile or number belongs to another workspace. |
| `channel_limit_reached` | The plan's connected-account limit is full. |
| `business_account_required` | TikTok: a personal account cannot message. |
| `messaging_unavailable_in_region` | TikTok withholds Business Messaging in that account's region. |
| `reconnection_target_mismatch` | A reconnect came back with a different account than the channel it was meant to reconnect. The new one **is** connected. |
| `session_expired` | The 30 minutes ran out. |

Provider error bodies are deliberately not exposed. Fix the configuration, or
let your customer retry with a **new** session.

## Keep it safe

* The partner key stays on your server. It can act on every workspace you have
  provisioned.
* `connect_url` carries a one-session capability. Hand it straight to the
  customer's browser; do not log it, e-mail it or put it in an analytics event.
* A connection id is not a secret, but your `state` should be unguessable if you
  use it to resume anything on your side.


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