Connecting Accounts
How to connect social media accounts using OAuth flows, headless mode, and non-OAuth platforms
Before you can post to a platform, you need to connect a social media account to a profile. Zernio supports 14 platforms, each with its own connection method.
OAuth Flow (Most Platforms)
Most platforms use OAuth. The basic flow is:
- Call
GET /v1/connect/{platform}with yourprofileId - The API returns an
authUrl - Redirect the user to that URL to authorize
- After authorization, the user is redirected back to your
redirect_url - The account is connected
const { authUrl } = await zernio.connect.getConnectUrl({
platform: 'twitter',
profileId: 'prof_abc123',
redirectUrl: 'https://myapp.com/callback'
});
// Redirect user to authUrlresult = client.connect.get_connect_url(
platform="twitter",
profile_id="prof_abc123",
redirect_url="https://myapp.com/callback"
)
# Redirect user to result.auth_urlcurl "https://zernio.com/api/v1/connect/twitter?profileId=prof_abc123&redirect_url=https://myapp.com/callback" \
-H "Authorization: Bearer YOUR_API_KEY"See the Start OAuth endpoint for full parameter details.
Platforms Requiring Secondary Selection
Some platforms require an extra step after OAuth - the user needs to select which page, organization, or board to connect:
| Platform | What to Select | Endpoints |
|---|---|---|
| Page | List Pages → Select Page | |
| Organization or Personal | List Orgs → Select Org | |
| Board | List Boards → Select Board | |
| Google Business | Location | List Locations → Select Location |
| Snapchat | Public Profile | List Profiles → Select Profile |
Standard vs Headless Mode
Standard mode (default): Zernio hosts the selection UI. The user picks their page/org in Zernio's hosted interface, then gets redirected to your redirect_url.
Headless mode: You build your own branded selection UI. Pass headless=true when starting the OAuth flow. After OAuth completes, the user is redirected to your redirect_url with tempToken, userProfile (URL-encoded JSON), step=select_page, and connect_token query params. Your backend then forwards those into the list and select endpoints to finalize the connection.
// 1. Start the connect flow, returns the OAuth URL.
const { authUrl } = await zernio.connect.getConnectUrl({
path: { platform: 'facebook' },
query: {
profileId: 'prof_abc123',
headless: true,
redirect_url: 'https://your-app.com/cb',
},
});
// Redirect the end-user's browser to authUrl.
// 2. Meta redirects the end-user to your redirect_url with these query
// params: profileId, tempToken, userProfile (URL-encoded JSON),
// platform=facebook, step=select_page, connect_token.
// 3. Your backend lists the user's pages.
const { pages } = await zernio.connect.facebook.listFacebookPages({
query: { profileId: 'prof_abc123', tempToken: '<from step 2>' },
});
// 4. Your backend posts the chosen pageId. userProfile must be the
// DECODED object, JSON.parse(decodeURIComponent(...)) the value
// you got in step 2.
const { redirect_url, account } = await zernio.connect.facebook.selectFacebookPage({
body: {
profileId: 'prof_abc123',
pageId: pages[0].id,
tempToken: '<from step 2>',
userProfile: JSON.parse(decodeURIComponent('<from step 2>')),
redirect_url: 'https://your-app.com/final-success',
},
});
// Redirect the browser to redirect_url; account.accountId is the
// SocialAccount ID for the connected page.# 1. Start the connect flow, returns the OAuth URL.
result = client.connect.get_connect_url(
platform="facebook",
profile_id="prof_abc123",
headless=True,
redirect_url="https://your-app.com/cb",
)
# Redirect the end-user's browser to result["authUrl"].
# 2. Meta redirects the end-user to your redirect_url with these query
# params: profileId, tempToken, userProfile (URL-encoded JSON),
# platform=facebook, step=select_page, connect_token.
# 3. Your backend lists the user's pages.
pages = client.connect.list_facebook_pages(
profile_id="prof_abc123",
temp_token="<from step 2>",
)
# 4. Your backend posts the chosen pageId. user_profile must be the
# DECODED dict, json.loads(urllib.parse.unquote(...)) the value
# you got in step 2.
result = client.connect.select_facebook_page(
profile_id="prof_abc123",
page_id=pages["pages"][0]["id"],
temp_token="<from step 2>",
user_profile=json.loads(urllib.parse.unquote("<from step 2>")),
redirect_url="https://your-app.com/final-success",
)
# Redirect the browser to result["redirect_url"]; result["account"]["accountId"]
# is the SocialAccount ID for the connected page.# 1. Start the connect flow, returns the OAuth URL.
curl "https://zernio.com/api/v1/connect/facebook?profileId=prof_abc123&headless=true&redirect_url=https://your-app.com/cb" \
-H "Authorization: Bearer YOUR_API_KEY"
# → 200 { "authUrl": "https://www.facebook.com/...", "state": "..." }
# Redirect the end-user's browser to authUrl.
# 2. Meta redirects the end-user to your redirect_url with profileId,
# tempToken, userProfile (URL-encoded JSON), platform=facebook,
# step=select_page, and connect_token query params.
# 3. Your backend lists the user's pages.
curl "https://zernio.com/api/v1/connect/facebook/select-page?profileId=prof_abc123&tempToken=TEMP_TOKEN" \
-H "Authorization: Bearer YOUR_API_KEY"
# → 200 { "pages": [{ "id": "...", "name": "...", ... }] }
# 4. Your backend posts the chosen pageId. userProfile must be the
# decoded JSON object, not the URL-encoded string.
curl -X POST "https://zernio.com/api/v1/connect/facebook/select-page" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "prof_abc123",
"pageId": "123456789",
"tempToken": "TEMP_TOKEN",
"userProfile": { "id": "1234567890", "username": "...", "displayName": "..." },
"redirect_url": "https://your-app.com/final-success"
}'
# → 200 {
# "redirect_url": "https://your-app.com/final-success?connected=facebook&profileId=...&username=...",
# "account": { "accountId": "<fb_id>", "platform": "facebook", ... }
# }
# Redirect the browser to redirect_url.Connecting Meta Ads only (skip the Page picker)
If your end-user only needs ads (audience uploads, Conversions API, analytics, list ads/campaigns), you can auto-pick the first Page in your backend without ever rendering a picker. The end-user transitions from Meta's OAuth screen directly to your "Connected" screen.
The flow is identical to the headless flow above, but uses /v1/connect/facebook/ads to start and your backend skips any UI step. Roughly 70% of the metaads surface (everything that doesn't emit object_story_spec.page_id) works regardless of which Page is bound, so picking the first one is fine for ads-only callers.
The remaining 30%, boost Page posts, Click-to-WhatsApp ads, Lead Gen Forms, IS Page-specific. If your end-user later needs those for a specific Page, surface a picker at that point and POST a new pageId to /v1/connect/facebook/select-page. Our handler updates the existing Facebook account in place, no re-OAuth.
// 1. Start the ads connect flow, returns the OAuth URL.
const { authUrl } = await zernio.connect.connectAds({
path: { platform: 'facebook' },
query: {
profileId: 'prof_abc123',
headless: true,
redirect_url: 'https://your-app.com/cb',
},
});
// Redirect the end-user's browser to authUrl.
// 2. End-user completes Meta OAuth. Browser lands at your redirect_url
// with profileId, tempToken, userProfile, step=select_page, etc.
// 3. Your backend lists pages and auto-picks the first one, no UI shown.
const { pages } = await zernio.connect.facebook.listFacebookPages({
query: { profileId: 'prof_abc123', tempToken: '<from step 2>' },
});
const { redirect_url, account } = await zernio.connect.facebook.selectFacebookPage({
body: {
profileId: 'prof_abc123',
pageId: pages[0].id,
tempToken: '<from step 2>',
userProfile: JSON.parse(decodeURIComponent('<from step 2>')),
redirect_url: 'https://your-app.com/final-success',
},
});
// Redirect the browser to redirect_url. account.accountId is the
// connected Facebook account ID (the metaads SocialAccount is created
// alongside it and accessible via GET /v1/accounts).# 1. Start the ads connect flow, returns the OAuth URL.
result = client.connect.connect_ads(
platform="facebook",
profile_id="prof_abc123",
headless=True,
redirect_url="https://your-app.com/cb",
)
# Redirect the end-user's browser to result["authUrl"].
# 2. End-user completes Meta OAuth. Browser lands at your redirect_url
# with profileId, tempToken, userProfile, step=select_page, etc.
# 3. Your backend lists pages and auto-picks the first one, no UI shown.
pages = client.connect.list_facebook_pages(
profile_id="prof_abc123",
temp_token="<from step 2>",
)
result = client.connect.select_facebook_page(
profile_id="prof_abc123",
page_id=pages["pages"][0]["id"],
temp_token="<from step 2>",
user_profile=json.loads(urllib.parse.unquote("<from step 2>")),
redirect_url="https://your-app.com/final-success",
)
# Redirect the browser to result["redirect_url"]. The metaads
# SocialAccount is created alongside the Facebook account.# 1. Start the ads connect flow, returns the OAuth URL.
curl "https://zernio.com/api/v1/connect/facebook/ads?profileId=prof_abc123&headless=true&redirect_url=https://your-app.com/cb" \
-H "Authorization: Bearer YOUR_API_KEY"
# → 200 { "authUrl": "...", "state": "..." }
# 2. End-user OAuths. Browser lands at your redirect_url with the
# standard headless params (profileId, tempToken, userProfile, etc.).
# 3. Your backend lists pages and auto-picks the first one.
curl "https://zernio.com/api/v1/connect/facebook/select-page?profileId=prof_abc123&tempToken=TEMP_TOKEN" \
-H "Authorization: Bearer YOUR_API_KEY"
curl -X POST "https://zernio.com/api/v1/connect/facebook/select-page" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "prof_abc123",
"pageId": "FIRST_PAGE_ID",
"tempToken": "TEMP_TOKEN",
"userProfile": { "id": "...", "username": "...", "displayName": "..." },
"redirect_url": "https://your-app.com/final-success"
}'
# Redirect the browser to the response's redirect_url. The metaads
# SocialAccount is created alongside the Facebook account.Non-OAuth Platforms
Bluesky
Bluesky uses app passwords instead of OAuth:
const account = await zernio.connect.connectBlueskyCredentials({
profileId: 'prof_abc123',
identifier: 'yourhandle.bsky.social',
password: 'your-app-password'
});
console.log('Connected:', account._id);account = client.connect.connect_bluesky_credentials(
profile_id="prof_abc123",
identifier="yourhandle.bsky.social",
password="your-app-password"
)
print(f"Connected: {account['_id']}")curl -X POST "https://zernio.com/api/v1/connect/bluesky/credentials" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "prof_abc123",
"identifier": "yourhandle.bsky.social",
"password": "your-app-password"
}'See Connect Bluesky for details.
Telegram
Telegram uses an access code flow:
- Call
POST /v1/connect/telegramto initiate the connection and get an access code - The user sends this code to the Zernio Telegram bot
- Poll
GET /v1/connect/telegramto check the status until connected
Managing Connected Accounts
After connecting, you can:
- List all accounts - see all connected accounts
- Update an account - change settings like default pages or boards
- Check account health - verify tokens and permissions are valid
- Disconnect an account - remove a connection
Updating Selections After Connection
You can change the selected page, organization, or board on an existing connection without re-authenticating: