Zernio
Zernio
API Reference

Connect

General

Get OAuth connect URLGETComplete OAuth callbackPOSTGet pending OAuth dataGETConnect ads for a platformGET

Facebook

List Facebook pagesGETSelect Facebook pagePOSTList Facebook pagesGETUpdate Facebook pagePUTComplete Meta business loginGETRead a Facebook Page's webhook subscriptionGETRe-subscribe a Facebook Page to Zernio's webhooksPOST

Google Business

List Google Business Profile locationsGETSelect Google Business Profile locationPOSTAssign Google Business Profile location to another profilePOSTList Google Business Profile locationsGETUpdate Google Business Profile locationPUT

Instagram

List Pages with a linked Instagram accountGETSelect the Page whose Instagram account to connectPOST

LinkedIn

List LinkedIn orgsGETSelect LinkedIn orgPOSTList LinkedIn orgsGETSwitch LinkedIn account typePUT

Pinterest

List Pinterest boardsGETSelect Pinterest boardPOSTList Pinterest boardsGETCreate Pinterest boardPOSTSet default Pinterest boardPUT

Reddit

List Reddit subredditsGETList subreddit flairsGETGet subreddit rulesGETSet Reddit post flairPOSTVote on a Reddit post or commentPOSTSet default subredditPUT

Bluesky

Connect Bluesky accountPOST

Discord

Connect a Discord channelPOST

Slack

List Slack channels for the channel pickerGETConnect a Slack channelPOST

Telegram

Generate Telegram codeGETConnect Telegram directlyPOSTCheck Telegram statusPATCH

Snapchat

List Snapchat profilesGETSelect Snapchat profilePOST

TikTok

Set TikTok brand identityPATCH

OpenAI Ads

Connect an OpenAI Ads accountPOST

WhatsApp

Connect WhatsApp via credentialsPOSTConnect WhatsApp from Embedded SignupPOSTGet Embedded Signup SDK configGETList numbers for selectionGETComplete number selectionPOST

YouTube

List YouTube playlistsGETCreate YouTube playlistPOSTSet default YouTube playlistPUTGet a YouTube video transcriptGET

Shopify

Get Shopify OAuth connect URLGETConnect a Shopify store with a custom-app Admin tokenPOST

WordPress

Get WordPress.com OAuth connect URLGETConnect self-hosted WordPress with an application passwordPOST

Other

Connect a Whop accountPOST
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources
Connect

Connect ads for a platform

Unified ads connection endpoint. Creates a dedicated ads SocialAccount for the specified platform.

Meta business login (opt-in). Set loginMode=business for facebook or instagram to use Facebook Login for Business and a Business Integration System User token. No posting account is created or required. This mode always returns an authUrl; it returns 503 when the server has no META_ADS_CONFIG_ID. Complete the dialog in a browser. The callback creates or reconnects only the metaads account, preserving its ID and history. Non-empty successful subscription results replace subscribedAdAccountIds to remove stale grants; an empty result leaves routing unchanged. A reconnect is accepted when the new grant shares at least one ad account with the existing connection (its scopedAdAccountIds plus the previous grant, or the ad accounts its old token can read when neither is stored), so re-running the dialog can add, drop or swap ad accounts. A grant with zero overlap is refused before changing the account and redirects with error=invalid_field_value and error_reason=reconnect_mismatch (disconnect and connect again to switch); when the previous ad accounts cannot be read at all, it redirects with error=reconnect_required. On an accepted reconnect the scope is rebuilt from the new grant: adAccountIds passed on the re-auth become the scope; otherwise a stored scope keeps its ad accounts that are still granted plus any granted for the first time, and an unscoped connection follows the new grant.

Pass pageId to select a granted Page for creatives and lead forms. API integrations otherwise reuse the previous Page or sole granted Page. Multiple Pages without a selection return 400 with available Page IDs for API integrations; restart with pageId. Dashboard session logins use the sole current grant automatically or open the existing Facebook Page picker for several grants, including reconnects. Selection completes the Meta Ads connection. Success redirects with connected=metaads, profileId and accountId. Every failure after Meta's dialog redirects to redirect_url (or the dashboard) with error, platform=metaads, error_message, request_id and stage, plus is_user_fixable and error_reason when known. error is the API error code: invalid_field_value with error_reason no_pages_granted (no Page ticked), page_not_granted (pageId not in the grant), ad_accounts_not_granted or reconnect_mismatch (the grant shares no ad account with the existing connection); reconnect_required, ads_addon_required, payment_required and the profile gates of GET /v1/connect/{platform}; invalid_state with error_reason=state_expired after the 30-minute window; connection_failed when Meta refuses the code (e.g. a replayed callback). A denial in the dialog is meta_ads_authorization_denied with the platform_error* params. Only a state that cannot be decrypted at all still answers 400 JSON, since it names no redirect_url. Business login reports metadata.tokenType=system-user in GET /v1/accounts. An absent Meta expires_in leaves tokenExpiresAt absent; no personal-token re-exchange occurs. Subsequent classic requests can change the ad-account scope using the business token; force=true (or disconnecting the business connection first) switches the profile to the standard Facebook login; the ads account is then held by the user token.

Same-token platforms (facebook, instagram, linkedin, pinterest). The ads SocialAccount (metaads, linkedinads, pinterestads) reuses the OAuth token of the parent posting account, but only when an active parent exists and, for facebook and instagram, its stored token carries ads_management and ads_read (linkedin and pinterest need no extra scope). In that case no extra OAuth happens and the response is alreadyConnected: true.

When no such parent exists, or the scopes are missing, the endpoint returns an authUrl and a full OAuth round trip is required. When a parent exists but carries no token usable for ad accounts, the call fails with 400 RECONNECT_REQUIRED. Independently of the branch, the call can return 403 ADS_ADDON_REQUIRED without the ads add-on and 402 PAYMENT_REQUIRED when the billing gate is closed.

Meta Ads prerequisite: connecting Meta Ads (via facebook or instagram) requires a Facebook Page. Not because the ad account is read through a Page, but because both parent posting accounts are: the facebook flow only offers Pages you manage, and the instagram flow with loginMethod=facebook_login only offers Instagram accounts linked to one of those Pages. Without a Page there is no parent account to inherit a token from. A user who manages no Facebook Page cannot complete this connection, and the facebook flow ends with error=no_facebook_pages.

Separate-token platforms (tiktok, twitter). Starts the platform-specific marketing API OAuth flow and creates an ads SocialAccount (tiktokads, xads) with its own token. If the ads account already exists, returns alreadyConnected: true.

  • tiktok: accountId is OPTIONAL. With accountId, the new tiktokads account links to that posting account (parentAccountId set), so Spark Ads + standalone ads using the posting TT_USER identity become available. Without accountId, ads-only mode kicks in: the new tiktokads account has parentAccountId=null and standalone ads use a synthetic CUSTOMIZED_USER ("Brand Identity"); Spark Ads are unavailable because TikTok requires a posting account for them. The Brand Identity is configured separately via PATCH /v1/connect/tiktok-ads (or inline on POST /v1/ads/create via the brandIdentity field).
  • twitter (X Ads): accountId is REQUIRED. There's no ads-only mode, because tweets need to be authored by a real X user.

Standalone platforms (googleads). Starts the Google Ads OAuth flow and creates a standalone ads SocialAccount (googleads) with no parent. If the account already exists and has at least one discovered Google Ads customer account, returns alreadyConnected: true. When the existing connection has zero customer accounts (metadata.googleAdsCustomerIds is empty) or is flagged needsReconnection, it returns an authUrl instead; completing it reconnects the same account (same accountId) and re-runs ad account discovery. Discovery includes the client accounts under every manager (MCC) the Google user can reach, and includes Google Ads test accounts, which Google always reports with status CLOSED. When discovery finds no usable customer account, the callback saves nothing and redirects with error=google_ads_no_ad_accounts, platform=googleads and an error_message naming each customer the Google user can reach and why it was left out (for example CUSTOMER_NOT_ENABLED). Sign in with a Google user that has access to the ad account or to its manager account. When the user unticks the Google Ads permission on Google's consent screen, the callback saves nothing and redirects with error=missing_google_permissions, platform=googleads, is_user_fixable=true and missing_scopes. An existing connection whose token lacks that permission is flagged needsReconnection, and the health endpoints report "Google Ads permission not granted"; this endpoint then returns an authUrl for it.

Ads accounts appear as regular SocialAccount documents with ads platform values (e.g., metaads, tiktokads) in GET /v1/accounts.


PlatformsMetaGoogleTikTokLinkedInPinterest
GET
/v1/connect/{platform}/ads

Authorization

bearerAuth
AuthorizationBearer <token>

API key authentication: send your Zernio API key in the Authorization header, prefixed with Bearer.

In: header

Path Parameters

platform*string

Platform to connect ads for. Only platforms with ads support are accepted.

In classic mode, instagram requires an Instagram account connected with loginMethod=facebook_login whose token carries ads_management and ads_read. With an account connected through the default instagram_login flow no ads account can be created; do not use this value for those accounts.

Value in

  • "facebook"
  • "instagram"
  • "linkedin"
  • "tiktok"
  • "twitter"
  • "pinterest"
  • "googleads"

Query Parameters

loginMode?string

Meta ads authorization mode. Business login is opt-in for Facebook and Instagram; classic preserves the posting-account flow.

Default"classic"

Value in

  • "classic"
  • "business"
permissionLevel?string

Business login only. Ad-account permission the connection's system user will hold. full asks the owner for Full control (MANAGE; required to create pixels and other account-level assets through Zernio). advertise asks for Manage campaigns (ADVERTISE), enough for campaigns, ad sets, creatives, ads, media and reporting, for owners who will not grant billing-level control to an integration. Either way Meta only lets a business admin complete the grant. 503 if the advertise configuration is not set up.

Default"full"

Value in

  • "full"
  • "advertise"
pageId?string

Business login only. Facebook Page ID to select from the token grants for ad creatives and lead forms.

Match^\d+$
profileId*string

Your Zernio profile ID

accountId?string

Existing SocialAccount ID. Required for twitter (X Ads). Optional for tiktok: omit to enter ads-only mode (no TikTok posting account linked; ad creation uses a Brand Identity instead of a TT_USER). For same-token platforms (facebook, instagram, linkedin, pinterest) it picks which posting account the ads connection uses when the profile holds several of that platform; with one it can be omitted, and an id that names no active account of the platform on the profile is ignored. Ignored for standalone platforms (googleads).

redirect_url?string

Custom URL the browser is sent to once the OAuth flow finishes. Honored on every ads platform, including the separate-token (tiktok, twitter) and standalone (googleads) flows. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. On success tiktok, twitter and googleads land on the URL unchanged, while the same-token platforms (facebook, instagram, linkedin, pinterest) append connected, profileId, accountId, username and, on API-key calls, connect_token. On failure the same error contract applies as on GET /v1/connect/{platform}: error and platform are always appended, other params are optional, and the value list there is not exhaustive. On the tiktok, twitter and googleads flows platform carries the ads platform id (tiktokads, xads, googleads), not the value used in the request path. When omitted, the browser lands on the Zernio dashboard.

Formaturi
headless?boolean

Enable headless mode (same-token platforms only)

Defaultfalse
force?boolean

Force a fresh OAuth even when an account already exists. Normally the endpoint returns alreadyConnected: true whenever a connected account is found, keying off its active state rather than token liveness. Set force=true to bypass that and always receivean authUrl. Completing the returned OAuth refreshes the stored token on the existing posting and ads accounts in place. An alreadyConnected response re-runs ad discovery and webhook subscriptions in the background at most once every 15 minutes per ads account (always when the call changes the ad-account scope), so it is not a sync trigger. To check the connection's health, read GET /v1/ads/accounts?accountId=.

Defaultfalse
adAccountId?string

Scope ad sync to a single platform ad account. Without this param, sync covers every ad account the connected token can see. Business-login reconnects preserve the existing scope; supplied IDs are checked against the new grant. To change that scope after migration, call this endpoint with the IDs and omit loginMode. Supported on facebook/instagram (Meta, act_<digits>), linkedin (bare numeric sponsored-account id), googleads (bare customer id digits) and twitter (X Ads, base36 account id). tiktok scopes advertisers at OAuth and pinterest has no ads discovery, so both ignore it. Meta ids are additionally validated against the connected token: an id the token cannot see returns 400 invalid_field_value, and an id it can see but that Meta marks unusable for ads (disabled, closed, under risk review, settlement pending) returns 400 ad_account_unusable with details.accountStatus (Meta's account_status) and, when Meta gives one, details.disableReason. Unsettled and in-grace accounts are accepted, matching the selectable flag of GET /v1/ads/accounts. Setting a scope also removes already synced ads, campaigns, ad sets and keywords from de-scoped ad accounts. On Meta the scope also decides which ad accounts Zernio subscribes to ad-account webhooks: only the scoped ones, instead of every ad account the login can reach. The scope is kept when this call returns an authUrl, so the Meta Ads account created after OAuth is scoped (and only its scoped ad accounts synced and subscribed) from the start. On googleads the scope is kept through OAuth when redirect_url is set, so the Google Ads connection created after OAuth syncs only its scoped customers from the first sync. For multiple accounts use adAccountIds instead.

adAccountIds?array<string>

Scope ad sync to multiple platform ad accounts (same platform support and id shapes as adAccountId). Repeat the param (?adAccountIds=act_1&adAccountIds=act_2) or comma-separate (?adAccountIds=act_1,act_2). Persisted server-side; latest call wins, and de-scoped ad accounts have their synced ads, campaigns, ad sets and keywords removed. On Meta only the scoped ad accounts get webhook subscriptions, including when the call starts a fresh OAuth. Omitting both adAccountId and adAccountIds keeps any previously persisted scope unchanged (no scope means every reachable ad account).

Response Body

application/json

application/json

import Zernio from '@zernio/node';const zernio = new Zernio({ apiKey: process.env.ZERNIO_API_KEY });const { data } = await zernio.connect.connectAds({  path: {    platform: 'facebook',  },  query: {    profileId: 'profile_abc123',  },});console.log(data);

{  "alreadyConnected": true,  "accountId": "664a1b2c3d4e5f6789012345",  "platform": "instagram",  "username": "@mybrand",  "displayName": "My Brand"}

Was this page helpful?

Get pending OAuth data

Fetch pending OAuth data for headless mode using the pendingDataToken from the redirect URL. **Scope**: This endpoint is used for LinkedIn organizations, Google Business Profile locations, Slack channels, Snapchat profiles, and Pinterest boards, where the selection list is too large to fit in URL params. The redirect carries a `pendingDataToken` instead of the full payload; the response includes the corresponding selection array (e.g. `boards` for Pinterest). WhatsApp, Facebook and other platforms pass selection state directly via URL query params on the redirect (`profileId`, `tempToken`, `step`), no pending record is created, so this endpoint will return 404 for those flows. Use the platform-specific selection endpoint instead (e.g. `/v1/connect/whatsapp/select-phone-number`). Reading the token does not consume it, so this fetch is repeatable until the token expires 1 hour after issuance. Completing the platform selection deletes the pending record, so the token stops working from then on. No authentication required.

List Facebook pages

Returns Facebook Pages after OAuth. Classic connections require profileId and tempToken from the OAuth redirect. Use X-Connect-Token for headless connections. The dashboard business-login picker instead sends only selectionToken, an encrypted grant valid for ten minutes. This requires the initiating user and current profile access and returns only Page IDs and names. X-Connect-Token cannot authorize business selection.