# Create a connect link

Create a one-time hosted link your user opens to connect a social account through OAuth, without a Relaycast login.


```http
POST https://social-publish-api.onrender.com/v1/accounts/connect-links
```

Lets your own users connect their social accounts to your project from inside your app. You create a link, send your user to its `url`, they approve access on the platform's consent screen, and Relaycast sends them back to your `redirectUrl` with the result. Your users never need a Relaycast account.

1. Your server calls this endpoint and gets a `url`.
2. Your app redirects the user (or opens a popup) to that `url`.
3. The user signs in to the platform and approves access.
4. Relaycast redirects to your `redirectUrl` with `status=connected` and the new `accountId`, or `status=error` and an `error` code.
5. Publish to the account with its `id`, as with any other account.

A link works **once** and expires after `expiresInMinutes`. Create a new link each time a user wants to connect. To show your own app name and logo on the consent screen instead of Relaycast's, connect through your own [OAuth app](/docs/oauth-apps) with `oauthAppId`.

> [!NOTE]
> Treat the `url` like a password until it's used: anyone who opens it can connect an account to your project. Create links on your server, never in client-side code, so your API key stays private.

## Headers

| Header | Required | Description |
| --- | --- | --- |
| `x-access-key` | Yes* | Your API key. *Or send `Authorization: Bearer <key>`. |
| `content-type` | Yes | `application/json` |

## Body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `platform` | string | Yes | Any OAuth platform, for example `youtube`, `facebook`, `instagram`, `linkedin`, `tiktok`. Bluesky uses [Connect Bluesky](/docs/api/connect-bluesky) instead. |
| `redirectUrl` | string | Yes | Where to send the user when they're done. Must be `https` (`http` is allowed for `localhost` only). Up to 2,048 characters. Your own query parameters are kept. |
| `state` | string | No | Any value up to 500 characters, returned unchanged to `redirectUrl`. Use it to match the result to your user or session. |
| `groupId` | string | No | Put the new account(s) in this [social group](/docs/groups). |
| `oauthAppId` | string | No | Connect through one of your project's own [OAuth apps](/docs/oauth-apps) for this platform. |
| `via` | string | No | Alternative connect flow. `facebook` connects Instagram through Facebook Login (Instagram professional accounts linked to a Facebook Page). |
| `expiresInMinutes` | integer | No | 5 to 10,080 (7 days). Default `60`. |

## Request

```bash tab="cURL"
curl -X POST https://social-publish-api.onrender.com/v1/accounts/connect-links \
  -H "x-access-key: $RELAYCAST_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "platform": "youtube", "redirectUrl": "https://app.example.com/integrations/done", "state": "user_42" }'
```

```ts tab="Node.js"
const res = await fetch("https://social-publish-api.onrender.com/v1/accounts/connect-links", {
  method: "POST",
  headers: {
    "x-access-key": process.env.RELAYCAST_API_KEY!,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    platform: "youtube",
    redirectUrl: "https://app.example.com/integrations/done",
    state: "user_42",
  }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data: link } = await res.json();
// Send your user to link.url
```

```python tab="Python"
import os, requests

res = requests.post(
    "https://social-publish-api.onrender.com/v1/accounts/connect-links",
    headers={"x-access-key": os.environ["RELAYCAST_API_KEY"]},
    json={"platform": "youtube", "redirectUrl": "https://app.example.com/integrations/done", "state": "user_42"},
)
res.raise_for_status()
link = res.json()["data"]  # send your user to link["url"]
```

## Response

```json title="Response 201"
{
  "data": {
    "id": "5b0c7e1a-2f3d-4c5b-8a9e-1d2c3b4a5f6e",
    "url": "https://social-publish-kappa.vercel.app/connect/3q2-Xb8…",
    "platform": "youtube",
    "status": "pending",
    "accountIds": [],
    "error": null,
    "errorDetail": null,
    "redirectUrl": "https://app.example.com/integrations/done",
    "state": "user_42",
    "groupId": null,
    "oauthAppId": null,
    "expiresAt": "2026-11-02T10:00:00.000Z",
    "completedAt": null,
    "createdAt": "2026-11-02T09:00:00.000Z"
  }
}
```

The `url` is only returned here. Relaycast stores a hash of it, so it can't be shown again.

## Back at your redirectUrl

Relaycast adds these query parameters:

| Parameter | When | Description |
| --- | --- | --- |
| `status` | Always | `connected` or `error`. |
| `connectLinkId` | Always | The link's `id`. |
| `platform` | Always | The platform from the link. |
| `accountId` | Connected | The connected account's `id`. |
| `accountIds` | Connected | Comma-separated ids. One login can grant several accounts, for example several Facebook Pages. |
| `error` | Error | One of the codes below. |
| `detail` | Sometimes | The platform's own explanation, when it gave one. |
| `state` | If you sent one | Your `state`, unchanged. |

```http
https://app.example.com/integrations/done?connectLinkId=5b0c7e1a-…&platform=youtube&status=connected&accountId=7c6b5a49-…&accountIds=7c6b5a49-…&state=user_42
```

Don't rely on the query string alone for anything sensitive: confirm with [Get a connect link](/docs/api/get-connect-link) from your server.

| `error` | Meaning |
| --- | --- |
| `access_denied` | The user cancelled on the consent screen. |
| `link_expired` | The link expired before it was used. Create a new one. |
| `link_used` | The link was already used. Create a new one. |
| `no_accounts` | The login worked but had nothing to publish to (for example, a Facebook user with no Pages). |
| `connect_failed` | The platform rejected the connection, or the project's account limit was reached. See `detail`. |
| `provider_error` | The platform returned an error. See `detail`. |
| `not_configured` / `app_not_configured` | The platform (or your OAuth app) is missing credentials on the server. |
| `invalid_group` / `invalid_app` | The group or OAuth app was deleted after the link was created. |

## Errors

| Status | Code | When |
| --- | --- | --- |
| `400` | `validation_error` | A field is missing or invalid, for example an `http` redirect that isn't `localhost`. |
| `400` | `unsupported_platform` | The platform can't be connected with a link (Bluesky). |
| `403` | `project_suspended` | The project is suspended. |
