Skip to content

OAuth Flow Guide

A step-by-step guide to connecting social media accounts to your Repliz workspace using the OAuth API.

Overview

Every platform starts the same way: you request an authorization URL, the user approves access in their browser, and the platform redirects back to your app with an authorization code. What you do with that code depends on the platform.

  • Direct flow (Instagram, Threads, TikTok, Shopee, X): send the code straight to Connect.
  • Flow with asset selection (Facebook, YouTube, LinkedIn): one login can manage several Pages, channels, or organizations, so you first exchange the code for a token, list the available assets, and then connect the one the user picks.

Access Tier

The OAuth API is available on the Gold+ tier. All requests use Basic Auth (Authorization: Basic Base64(AccessKey:SecretKey)).

Which Flow Does My Platform Use?

PlatformFlowAsset to pickAsset list endpointID field on Connect
InstagramDirect---
ThreadsDirect---
TikTokDirect---
ShopeeDirect---
X (Twitter)Direct---
FacebookWith asset selectionPageGet Page FacebookpageId
YouTubeWith asset selectionChannelGet Channel YouTubechannelId
LinkedInWith asset selectionPersonal account or organizationGet Organization LinkedInorganizationId

All endpoints follow the same path pattern, where {platform} is one of facebook, instagram, threads, youtube, linkedin, tiktok, shopee, or twitter:

StepEndpointPlatforms
AuthorizeGET /public/account/{platform}/authorizeAll
ExchangePOST /public/account/{platform}/exchangeFacebook, YouTube, LinkedIn
List assetGET /public/account/facebook/pageFacebook
List assetGET /public/account/youtube/channelYouTube
List assetGET /public/account/linkedin/organizationLinkedIn
ConnectPOST /public/account/{platform}/connectAll
ReconnectPOST /public/account/{platform}/connect/{accountId}All

Direct Flow

Used by Instagram, Threads, TikTok, Shopee, and X (Twitter).

Authorize → User approves → Redirect with code → Connect / Reconnect

Step 1: Get the Authorization URL

Call Authorize with the redirect URL where the user should land after approving access. URL-encode the value if it contains its own query string.

bash
curl -X GET "https://api.repliz.com/public/account/tiktok/authorize?redirect=https://your-app.com/oauth/callback" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)"
json
{
  "url": "https://www.tiktok.com/v2/auth/authorize?client_key=...&redirect_uri=https://your-app.com/oauth/callback&state=..."
}

Open the returned url in the user's browser, either as a full-page redirect or a popup. The user logs in and approves access on the platform's own page, so this step cannot run on your server alone.

Step 2: Handle the Redirect

After the user approves, the platform sends them back to your redirect URL. On most platforms the authorization code is in the query string:

https://your-app.com/oauth/callback?code=AUTH_CODE_FROM_REDIRECT&state=...

Read the code parameter on your callback page and pass it to your backend. You don't need to send state to Repliz.

Two platforms return the value in a different shape:

PlatformWhat the redirect containsSend as code
Facebook#access_token=... in the URL fragmentThe access_token value
Shopee?code=...&shop_id=... in the query stringBoth joined with an underscore: {code}_{shop_id}
Others?code=... in the query stringThe code value

The URL fragment (everything after #) never reaches your server, so for Facebook read it in the browser with window.location.hash and pass the value to your backend.

Use the code right away

Authorization codes are short-lived and can only be used once. If Connect fails because the code has expired or was already used, start again from Step 1.

Step 3: Connect the Account

Send the code to Connect. Repliz exchanges it with the platform and adds the account to your workspace.

bash
curl -X POST "https://api.repliz.com/public/account/tiktok/connect" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"code":"AUTH_CODE_FROM_REDIRECT"}'
json
{
  "accountId": "69e97477a795504e0786cec6"
}

Store the accountId. You will use it for schedules, automations, and any later reconnect.

To reconnect an existing account instead, send the same body to the Reconnect endpoint with the accountId in the path. It returns 204 No Content on success.

bash
curl -X POST "https://api.repliz.com/public/account/tiktok/connect/69e97477a795504e0786cec6" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"code":"AUTH_CODE_FROM_REDIRECT"}'

Flow with Asset Selection

Used by Facebook, YouTube, and LinkedIn. One login on these platforms can manage several assets: a Facebook user can manage many Pages, a Google account can own several YouTube channels, and a LinkedIn member can connect their personal account or any organization they administer. Each Repliz account maps to one asset, so the user has to pick which one to connect.

Authorize → User approves → Redirect with code → Exchange → Get Page / Channel / Organization → Connect / Reconnect

Step 1 & 2: Authorize and Handle the Redirect

These are the same as in the Direct Flow. Call the platform's Authorize endpoint, open the returned url in the browser, and read the value from your callback URL. For Facebook, that value is the access_token in the URL fragment; see Step 2.

bash
curl -X GET "https://api.repliz.com/public/account/facebook/authorize?redirect=https://your-app.com/oauth/callback" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)"

Step 3: Exchange the Code for a Token

Send the code to Exchange. Unlike the direct flow, this does not connect anything yet. It returns a token that lets you list the user's assets.

bash
curl -X POST "https://api.repliz.com/public/account/facebook/exchange" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"code":"AUTH_CODE_FROM_REDIRECT"}'
json
{
  "token": "9402c20336a7c3ce2d8e4f76...2255a73f-8431-4458-abcf-ee7f867952db"
}

Step 4: List the Available Assets

Pass the token from Step 3 as a query parameter to the platform's list endpoint:

PlatformEndpoint
FacebookGET /public/account/facebook/page
YouTubeGET /public/account/youtube/channel
LinkedInGET /public/account/linkedin/organization
bash
curl -X GET "https://api.repliz.com/public/account/facebook/page?token=TOKEN_FROM_EXCHANGE" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)"

All three endpoints return the same shape:

json
{
  "docs": [
    {
      "id": "113958387982006",
      "name": "Halaman ke 3",
      "username": "Kafe",
      "picture": "https://scontent.fbdo9-1.fna.fbcdn.net/v/t39.30808-1/...",
      "token": "PAGE_ACCESS_TOKEN"
    }
  ]
}
FieldDescription
idThe ID of the Page, channel, or LinkedIn account. Send it to Connect.
nameDisplay name, useful for showing a picker to the user.
usernameHandle or username of the asset.
pictureAvatar URL of the asset.
tokenAccess token tied to this asset. Send it to Connect.

LinkedIn personal accounts

For LinkedIn, the list includes the user's own personal account (urn:li:person:...) along with the organizations they administer (urn:li:organization:...). Both are connected the same way, by sending the id as organizationId.

Show the list to the user and let them choose which asset to connect. If docs is empty, the account the user logged in with doesn't manage any Page, channel, or organization.

Step 5: Connect the Selected Asset

Send the chosen item's id and token to Connect. The name of the ID field depends on the platform:

PlatformRequest body
Facebook{ "pageId": "...", "token": "..." }
YouTube{ "channelId": "...", "token": "..." }
LinkedIn{ "organizationId": "...", "token": "..." }
bash
curl -X POST "https://api.repliz.com/public/account/facebook/connect" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"pageId":"113958387982006","token":"PAGE_ACCESS_TOKEN"}'
json
{
  "accountId": "680affa5ce12f2f72916f67e"
}

To connect several assets, call Connect once for each selected item. Each call creates a separate account with its own accountId.

To reconnect an existing account, send the same body to the Reconnect endpoint with the accountId in the path. Pick the same Page, channel, or LinkedIn account the account was originally connected with. It returns 204 No Content on success.

bash
curl -X POST "https://api.repliz.com/public/account/facebook/connect/680affa5ce12f2f72916f67e" \
  -H "Authorization: Basic $(echo -n 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"pageId":"113958387982006","token":"PAGE_ACCESS_TOKEN"}'

Full Example

Here is the complete flow for both types in JavaScript. getAuthorizeUrl runs before the redirect; handleCallback runs on your callback URL with the code from the query string.

javascript
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.repliz.com/public/account',
  auth: {
    username: 'YOUR_ACCESS_KEY',
    password: 'YOUR_SECRET_KEY',
  },
});

// Platforms that need an asset picked after login, with their list endpoint and ID field
const ASSET_PLATFORMS = {
  facebook: { list: 'page', idField: 'pageId' },
  youtube: { list: 'channel', idField: 'channelId' },
  linkedin: { list: 'organization', idField: 'organizationId' },
};

// Read the value to send as `code` from your callback URL
function getCode(platform, callbackUrl) {
  const url = new URL(callbackUrl);
  // Facebook returns an access token in the URL fragment
  if (platform === 'facebook') {
    return new URLSearchParams(url.hash.slice(1)).get('access_token');
  }
  const code = url.searchParams.get('code');
  // Shopee needs the shop ID joined to the code
  if (platform === 'shopee') {
    return `${code}_${url.searchParams.get('shop_id')}`;
  }
  return code;
}

// Step 1: Get the authorization URL and send the user to it
async function getAuthorizeUrl(platform, redirect) {
  const { data } = await api.get(`/${platform}/authorize`, {
    params: { redirect },
  });
  return data.url;
}

// Step 2+: Called on your callback page with the code from the redirect.
// Pass accountId to reconnect an existing account instead of adding a new one.
async function handleCallback(platform, code, accountId, pickAsset) {
  const connectPath = accountId
    ? `/${platform}/connect/${accountId}`
    : `/${platform}/connect`;

  const asset = ASSET_PLATFORMS[platform];

  // Direct flow: Instagram, Threads, TikTok, Shopee, X
  if (!asset) {
    const { data } = await api.post(connectPath, { code });
    return data;
  }

  // Asset selection flow: Facebook, YouTube, LinkedIn
  const { data: exchange } = await api.post(`/${platform}/exchange`, { code });

  const { data: list } = await api.get(`/${platform}/${asset.list}`, {
    params: { token: exchange.token },
  });

  const selected = await pickAsset(list.docs); // let the user choose one

  const { data } = await api.post(connectPath, {
    [asset.idField]: selected.id,
    token: selected.token,
  });
  return data;
}

// Usage
const url = await getAuthorizeUrl('facebook', 'https://your-app.com/oauth/callback');
// ...user approves and lands on your callback URL, where you read the code
const code = getCode('facebook', window.location.href);

const result = await handleCallback('facebook', code, null, (docs) => docs[0]);
console.log('Connected account:', result.accountId);
javascript
const API_BASE = 'https://api.repliz.com/public/account';
const credentials = btoa('YOUR_ACCESS_KEY:YOUR_SECRET_KEY');

// Platforms that need an asset picked after login, with their list endpoint and ID field
const ASSET_PLATFORMS = {
  facebook: { list: 'page', idField: 'pageId' },
  youtube: { list: 'channel', idField: 'channelId' },
  linkedin: { list: 'organization', idField: 'organizationId' },
};

// Read the value to send as `code` from your callback URL
function getCode(platform, callbackUrl) {
  const url = new URL(callbackUrl);
  // Facebook returns an access token in the URL fragment
  if (platform === 'facebook') {
    return new URLSearchParams(url.hash.slice(1)).get('access_token');
  }
  const code = url.searchParams.get('code');
  // Shopee needs the shop ID joined to the code
  if (platform === 'shopee') {
    return `${code}_${url.searchParams.get('shop_id')}`;
  }
  return code;
}

async function request(path, { method = 'GET', body } = {}) {
  const response = await fetch(`${API_BASE}${path}`, {
    method,
    headers: {
      Authorization: `Basic ${credentials}`,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
  return response.status === 204 ? null : response.json();
}

// Step 1: Get the authorization URL and send the user to it
async function getAuthorizeUrl(platform, redirect) {
  const data = await request(
    `/${platform}/authorize?redirect=${encodeURIComponent(redirect)}`,
  );
  return data.url;
}

// Step 2+: Called on your callback page with the code from the redirect.
// Pass accountId to reconnect an existing account instead of adding a new one.
async function handleCallback(platform, code, accountId, pickAsset) {
  const connectPath = accountId
    ? `/${platform}/connect/${accountId}`
    : `/${platform}/connect`;

  const asset = ASSET_PLATFORMS[platform];

  // Direct flow: Instagram, Threads, TikTok, Shopee, X
  if (!asset) {
    return request(connectPath, { method: 'POST', body: { code } });
  }

  // Asset selection flow: Facebook, YouTube, LinkedIn
  const exchange = await request(`/${platform}/exchange`, {
    method: 'POST',
    body: { code },
  });

  const list = await request(
    `/${platform}/${asset.list}?token=${encodeURIComponent(exchange.token)}`,
  );

  const selected = await pickAsset(list.docs); // let the user choose one

  return request(connectPath, {
    method: 'POST',
    body: { [asset.idField]: selected.id, token: selected.token },
  });
}

// Usage
const url = await getAuthorizeUrl('facebook', 'https://your-app.com/oauth/callback');
// ...user approves and lands on your callback URL, where you read the code
const code = getCode('facebook', window.location.href);

const result = await handleCallback('facebook', code, null, (docs) => docs[0]);
console.log('Connected account:', result.accountId);

Connect vs Reconnect

UseWhenResult
ConnectAdding an account that is not in your workspace yet.200 with a new accountId
ReconnectAn existing account lost access, for example the token expired or the user revoked the app.204 No Content, same accountId

Reconnect keeps the same accountId, so schedules, automations, and anything else that references the account keep working. Use Get Account or Get One Account to check the isConnected field and find accounts that need a reconnect.

Both run the same flow for the platform. The only difference is the final request: Reconnect adds /{accountId} to the Connect path.

Common Errors

StatusMessageCause and fix
400incorrect generatedId to reconnectThe account the user logged in with, or the asset they picked, is not the one linked to accountId. Log in with the same platform account and pick the same Page, channel, or LinkedIn account.
404account not foundThe accountId on Reconnect doesn't exist in your workspace.
401unauthorizedYour Access Key or Secret Key is wrong. See Install.