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

# Connect Customer Accounts

> Create isolated end users and connect their social accounts through hosted OAuth

Each customer in your application maps to an isolated Social media studio end user. Social
connections and posts are scoped to that end user, while your partner key is
scoped to the entire developer project.

## Upsert an end user

Use an immutable identifier from your own database as `externalId`. Calling
`upsert` again with the same value updates the customer and re-enables it if it
was previously disabled.

```ts theme={null}
const endUser = await soMe.endUsers.upsert({
  externalId: "customer_123",
  displayName: "Acme Ltd",
  metadata: {
    plan: "pro",
    region: "eu",
  },
});
```

Store `endUser.id` with your customer record. SDK methods use this Social media studio ID,
not `externalId`.

```ts theme={null}
const customers = await soMe.endUsers.list();
const customer = await soMe.endUsers.get(endUser.id);
```

To stop a customer from using the embedded platform:

```ts theme={null}
await soMe.endUsers.disable(endUser.id);
```

Disabling deactivates the customer's isolated workspace. A later `upsert` with
the same `externalId` reactivates it.

## Create a connect session

```ts theme={null}
const session = await soMe.connectSessions.create({
  endUserId: endUser.id,
  platform: "THREADS",
  returnUrl: "https://app.example.com/settings/social",
});
```

Connect sessions expire after 10 minutes and can be used only for the specified
end user. Redirect the customer to the returned `connectUrl`:

```ts theme={null}
return Response.redirect(session.connectUrl!);
```

<Warning>
  The `returnUrl` origin must exactly match an origin registered on your partner
  project, including scheme, hostname, and port. Paths and query parameters may
  vary. Credentials embedded in a return URL are rejected.
</Warning>

## Handle completion

After the hosted flow reaches a terminal state, the customer sees a return
button. The destination receives these query parameters:

| Parameter        | Value                                 |
| ---------------- | ------------------------------------- |
| `social_connect` | `success` or `failed`                 |
| `end_user_id`    | The Social media studio end-user ID   |
| `connection_id`  | The new connection ID when successful |

Treat browser parameters as a notification, not proof of success. Confirm the
result from your backend using the connect-session ID you created:

```ts theme={null}
const result = await soMe.connectSessions.get(session.id);

if (result.status === "completed" && result.connectionId) {
  // Persist result.connectionId for this customer.
}
```

The hosted page also sends a `postMessage` to its opener when used in a popup:

```ts theme={null}
window.addEventListener("message", (event) => {
  if (event.origin !== "https://api.so-me.studio") return;
  if (event.data?.type !== "some.social_connect.completed") return;

  // Ask your backend to verify the connect session.
});
```

## List and disconnect accounts

```ts theme={null}
const connections = await soMe.connections.list(endUser.id);

await soMe.connections.disconnect(endUser.id, connections[0].id);
```

Always pass the end-user ID that owns the connection. Social media studio rejects attempts to
read or disconnect a connection belonging to a different customer.

## Session states

| Status      | Meaning                                            |
| ----------- | -------------------------------------------------- |
| `created`   | Ready for the customer to open                     |
| `started`   | The hosted OAuth flow has begun                    |
| `completed` | Account connected; `connectionId` is available     |
| `failed`    | Authorization was denied or could not be completed |
| `expired`   | The 10-minute session window elapsed               |

Create a new session after `failed` or `expired`; terminal sessions cannot be
restarted.
