<!-- https://cutedyno.com/docs/guides/connecting-accounts -->

# Connecting accounts

Run the OAuth flow that links a social account to a CuteDyno profile.

Before anything can be published, someone has to grant CuteDyno permission to post on their behalf. You start a connect session, send the user to the platform, and CuteDyno handles the callback, the token exchange, and the refresh cycle from there.

Five platforms connect this way: `facebook`, `instagram`, `tiktok`, `linkedin`, and `youtube`.

## The flow

```text
1. POST /v1/connect/sessions   ->  authUrl + session.id
2. Send the user to authUrl    ->  they approve on the platform
3. Platform redirects back     ->  CuteDyno exchanges the code
4. connection.completed webhook->  the account is usable
```

Steps 3 and 4 happen without you. You need to build steps 1 and 2, and listen in step 4.

## Start a session

```typescript
const { authUrl, session } = await cutedyno.accounts.connect({
  platform: 'instagram',
  profileId: 'prof_customer_42',
  redirectUrl: 'https://yourapp.com/settings/social?connected=instagram',
});

// Redirect the user, or open authUrl in a popup.
```

`redirectUrl` must be HTTPS, and it is where the user lands when the flow finishes — success or failure. Send them back to the screen they started from, not to a bare confirmation page; people connect accounts in the middle of doing something else.

Sessions expire. Treat `authUrl` as single-use and short-lived: generate one when the user clicks Connect, not when the settings page renders.

`profileId` decides which customer gets the account. Omit it and the account lands in the API key's home profile, which is what you want for a single-tenant setup and almost never what you want on a platform. See [Build a platform](/docs/build-a-platform).

## Know when it finished

Subscribe to `connection.completed`. It is the only reliable signal, because the user might approve on their phone while your tab sits idle, or abandon the flow entirely.

```typescript
if (event.type === 'connection.completed') {
  const { profileId, account } = event.data;
  await db.socialAccounts.insert({
    customerId: profileId,
    accountId: account.id,
    platform: account.platform,
    handle: account.username,
  });
}
```

Also handle `connection.failed`, which carries an `errorMessage` explaining what the platform rejected. Surface that text; it is usually actionable ("this Instagram account is not a professional account").

If you need to check state directly — for a status page, or because a user is watching a spinner — poll the session:

```typescript
const { session, accounts } = await cutedyno.accounts.getConnectSession(session.id);
// session.status: 'pending' | 'completed' | 'failed' | 'expired'
```

Poll at most every few seconds, and stop once the status leaves `pending`. Do not use polling as your source of truth; use it to make the UI feel alive while the webhook does the real work.

## Two IDs per account

Every connected account has both:

```json
{
  "id": "3f2a…",
  "platformAccountId": "17841400000000000",
  "platform": "instagram",
  "name": "Acme Coffee",
  "username": "acmecoffee"
}
```

`id` is CuteDyno's. `platformAccountId` is the platform's, useful for reconciling with data you already hold from a previous integration. Endpoints that take account IDs — `accountIds` on a post, `accountId` on comments, `allowedAccountIds` on a key — accept either, so you do not have to migrate stored IDs to start using CuteDyno.

`accountId` is a deprecated alias of `id`, kept so older clients keep working. Use `id`.

## Platform requirements

Most failed connections are not bugs; they are accounts that were never eligible.

| Platform | Requirement |
| --- | --- |
| Instagram | A Professional (Business or Creator) account, linked to a Facebook Page. Personal accounts cannot be posted to via any API. |
| Facebook | A Page, and the connecting user must have a Page admin or content role. Personal profiles are not supported. |
| TikTok | Any account, but unaudited apps can only post privately. See [TikTok](/docs/platforms/tiktok). |
| LinkedIn | A personal profile or a Company Page you administer. |
| YouTube | A channel on the Google account being connected. |

Publish the relevant requirement next to your Connect button. It saves a support ticket per user.

## Reconnecting

Tokens get revoked: a user changes their password, removes your app, loses admin rights on a Page, or the platform expires a long-lived token. When that happens, publishing starts failing with `account_disconnected`.

The fix is the same flow again. Start a new session for the same platform and profile, and CuteDyno replaces the credentials on the existing account rather than creating a duplicate. Watch for `account.disconnected` webhooks and prompt the user before they discover it from a failed post.

## Listing what is connected

```typescript
const { accounts } = await cutedyno.accounts.list({ profileId: 'prof_customer_42' });
```

Show this list in your own UI, keyed by `platform`, and offer Connect for platforms that are missing. Read it fresh rather than trusting your mirror of it — an account can disappear from CuteDyno's side when a user revokes access on the platform.

## Next

- [Media uploads](/docs/guides/media-uploads) — get images and video into a post
- [Build a platform](/docs/build-a-platform) — connect accounts on behalf of your customers
- [Webhooks](/docs/webhooks) — the full event list
