Skip to main content
A connected account is one social account (an Instagram account, a Facebook login with its Pages, a YouTube channel, a Telegram bot) that SocialAPI can act on for you. You start a connection with POST /v1/accounts/connect, send the person to the platform’s consent screen, and SocialAPI redirects them back to your app with the new account’s ID. SocialAPI keeps the platform’s access tokens, so you never receive or refresh them. The same flow serves every platform. Telegram and Bluesky skip the consent screen and connect in one request. Google Business Profile can pause so you choose which location to connect. Facebook can pause so you choose which Pages join the brand (see Facebook Pages), and you can move Pages between brands afterwards. Connecting, reconnecting, and disconnecting cost no credits.

Endpoints

Your first connection

This request starts connecting an Instagram account into an existing brand and asks SocialAPI to send the person back to your app afterwards:
Response (HTTP 202):
Send the person to auth_url. After they approve, their browser lands on your redirect_uri:
Store account_id against your user. The account is usable at once.

Request fields

Response fields

POST /v1/accounts/connect answers in one of two ways:
  • HTTP 202 for platforms with a consent screen: auth_url (send the person there), state (SocialAPI’s own value for this connection), and message. WhatsApp works the same way: auth_url opens Meta’s signup. See WhatsApp.
  • HTTP 201 for Telegram and Bluesky, which connect without a consent screen: account_id, platform, username, and display_name.
The person has 1 hour from your request to finish on the platform. After 1 hour, the platform’s redirect reaches a SocialAPI error page instead of your redirect_uri, and you start a new connection.

How a connection works

A connection either completes at once, pauses for a choice of location, or fails. Every outcome reaches your redirect_uri as query parameters.
1

Start the connection

Call POST /v1/accounts/connect from your server with your API key. Pass the brand_id the account belongs in.
2

Send the person to the consent screen

Redirect the person’s browser to auth_url. They sign in to the platform and approve the scopes SocialAPI asks for. Each platform’s page lists those scopes.
3

Read the result on your redirect URI

SocialAPI completes the authorization with the platform and redirects the person to your redirect_uri with status and the parameters below. Check status first, then compare state with the value you sent.
4

Choose a Google Business Profile, Facebook Pages, or a WhatsApp number, if asked

When status is selection_required, read the candidates and choose within 30 minutes, as described in Choose a Google Business Profile, Choose Facebook Pages, and Choose a WhatsApp number.

Redirect parameters

error holds one of these values:
  • The platform’s own code when the person declines or the platform refuses the request, for example access_denied.
  • A platform.* or other SocialAPI error code when the platform or SocialAPI refuses the connection, for example billing.brands_limit (your plan’s brand allowance is used up), account.already_linked (the brand already holds a different account on this platform, or lock_accounts_to_brand blocked a move between brands), account.different_login (the brand’s Facebook is connected through a different login), byok.credentials_missing (no X app credentials saved), byok.credentials_invalid (X rejected the saved credentials), or byok.app_access_denied (X refused your app access to its API). Each code is listed in Errors.
  • missing_code when the platform returned neither a code nor an error.
  • exchange_failed when the failure has no code.
After any error, start a new connection: the old auth_url is spent. Without a redirect_uri, a connection that needs a choice sends the person to the SocialAPI dashboard, which asks them to choose.

Platform slugs

The platform field takes one of these values: A slug not in this table, including trustpilot, returns 403 billing.platform_not_available. Which app each platform connects through (SocialAPI’s or your own) is listed in Platform apps.

Redirect URI rules

Your redirect_uri must use http or https and must not contain a user name, a password, or a #fragment. A malformed value returns 400 validation.redirect_uri_invalid_format. Until you register a redirect URI, SocialAPI accepts any well-formed redirect_uri. Once you register one or more, every redirect_uri must match one of them, and any other value returns 400 validation.redirect_uri_not_whitelisted before a connection starts. Register up to 20 redirect URIs in the dashboard at app.social-api.ai/api-configuration. Deleting every registered URI accepts any well-formed value again. SocialAPI compares redirect URIs after normalizing both sides: the scheme and host are lowercased, a default port (:443 for https, :80 for http) is dropped, a trailing / is removed, and query parameters are sorted. https://App.example.com:443/oauth/callback/ matches a registered https://app.example.com/oauth/callback. Your redirect_uri can carry its own query parameters. SocialAPI adds its parameters to them. When one of yours has the same name as a parameter SocialAPI sends (status, platform, state, account_id, account_ids, connection_id, error, error_description), SocialAPI’s value replaces yours.

Choose a Google Business Profile

A Google connection pauses with status=selection_required when the Google login manages more than one Business Profile. You choose exactly one. A login with a single Profile connects at once. The redirect carries only the connection_id. Read the candidates with GET /v1/accounts/pending/{connection_id}:
Response (HTTP 200):
Finish the connection by sending the chosen platform_account_id:
Response (HTTP 201):

Pending connection fields

The response has no data wrapper: every field is at the top level.

Selection rules

  • A pending connection expires 30 minutes after it is created and works once. The first select that succeeds consumes it. On a Facebook or Instagram connection, some failed select calls consume it too: see Choose Facebook Pages. A second select, or a call after expiry, returns 404 resource.not_found.
  • A 400 keeps the pending connection. On a Facebook or Instagram connection, only the three 400 codes listed in Choose Facebook Pages keep it. Fix the request and send it again within the 30 minutes.
  • A platform_account_id that is not in profiles[] returns 400 validation.profile_not_in_set.
  • A body without platform_account_id returns 400 validation.field_required.

Facebook Pages

A Facebook connection pauses with status=selection_required so you choose which granted Pages join the brand. The same applies to Instagram connected with metadata.connection_type: "facebook_login", where each candidate is an Instagram account. Instagram connected through Instagram Login has no selection step. GET /v1/users/me returns facebook_page_selection, which is true on new SocialAPI accounts and which the API cannot change. When it is false on your SocialAPI account, a Facebook connection has no selection step: SocialAPI adds every granted Page that none of your other brands holds to the brand’s account, and the redirect carries status=success. A connection started from an invite link never has a selection step, whatever the setting.
  • A login that grants no Page ends with status=error and error=platform.facebook.no_pages on your redirect_uri.
  • Without a selection step, a login whose every Page belongs to another of your brands ends with status=error and error=platform.facebook.no_assignable_pages (platform.instagram.no_assignable_pages for Instagram). With a selection step, those Pages come back with assignable: false instead.
To move a Page to another brand after connecting, see One login, many brands.

Choose Facebook Pages

Read the candidates with GET /v1/accounts/pending/{connection_id}:
Response (HTTP 200):
Finish the connection by sending the chosen Page IDs:
Response (HTTP 201):
username and display_name are the name on the Facebook login, not a Page name. The selected Pages are on the account: list them with GET /v1/accounts/{id}/pages. The selection rules on expiry, single use, and 400 apply. Also:
  • page_ids empty or missing returns 400 validation.field_required.
  • A Page ID that is not in pages[] returns 400 validation.page_not_in_set, with the ID in meta.platform_page_id.
  • A Page that another of your brands holds returns 400 validation.page_already_assigned, with meta.brand_id naming that brand. A Page the connecting brand already holds can be selected. To take a Page from another brand, move it with the assign call in One login, many brands.
  • More Pages than your plan allows per brand returns 403 platform.facebook.page_cap, with the allowance in meta.limit. On a Facebook connection, the count includes the Pages the brand already holds. The allowance per plan is in Plans and usage.
  • An Instagram account you already connected through Instagram Login returns 409 platform.instagram.flow_conflict. meta.accounts is a comma-separated string of the Instagram account IDs concerned. Disconnect that account first.
  • On a Facebook or Instagram connection, only 400 validation.field_required, 400 validation.page_not_in_set, and 400 validation.page_already_assigned keep the pending connection. Any other error, including 400 validation.page_not_granted, 403 platform.facebook.page_cap, and 409 platform.instagram.flow_conflict, consumes it. Start a new connection to choose again.
  • No call cancels a pending connection. An unused one expires after 30 minutes.

When reconnecting Facebook removes Pages

Facebook grants Page access per app, not per brand. When a person reconnects a Facebook login and unchecks a Page on Facebook’s consent screen, every brand that used that Page through this login loses it. The affected account gets status: reconnect_recommended with reconnect_reason: page_access_lost. The Page stays assigned to its brand. When the login grants the Page again (the person reconnects and keeps the Page checked), the Page works again on the same brand with no further step.

Account status

GET /v1/accounts returns every connected account with its status:
Response (HTTP 200):
Treat a reconnect_reason you do not recognize as a request to reconnect.

Reconnect an account

To reconnect an account, call POST /v1/accounts/connect again with the same platform and the account’s current brand_id (the brand_id that GET /v1/accounts returns), and send the person through the consent screen with the same platform login:
SocialAPI updates the existing account in place:
  • The account keeps its id, its brand, its posts, conversations, and Automations.
  • status returns to active and reconnect_reason is cleared.
  • The account keeps its permissions unless you send permissions on the reconnect. A narrower list deletes the data the removed permissions covered: see Removing a permission deletes data.
  • SocialAPI sends account.connected again.
A Facebook reconnect, or an Instagram reconnect through Facebook Login, can end with status=selection_required like a first connection: see Facebook Pages. Always pass brand_id when you reconnect. Without it, SocialAPI creates a new brand for the connection and moves the account into it, which leaves the old brand without the account. Connecting a platform account that another of your brands already holds moves it to the brand you name. These cases do not move it:
  • Facebook, and Instagram connected through Facebook Login. A Page or Instagram account that another of your brands holds is not taken. With a selection step, it comes back with assignable: false. Without a selection step, a login whose every Page or Instagram account belongs to another brand ends with error=platform.facebook.no_assignable_pages or error=platform.instagram.no_assignable_pages. You assign Pages to brands as described in Facebook.
  • Google Business Profile locations assigned with the assign call. POST /v1/platforms/google/logins/{loginId}/locations/{locationId}/assign returns 400 validation.location_already_assigned for a location that another brand holds. See Google locations.
  • lock_accounts_to_brand set to true on your SocialAPI account (PATCH /v1/users/me). A connect that would move an account to a different brand ends with error=account.already_linked, or returns 409 account.already_linked. The default is false. The lock does not apply to Facebook.
Each of those errors leaves the account in its current brand. A brand holds one connected account per platform. Connecting a different account of the same platform into a brand that already has one ends with status=error and error=account.already_linked on your redirect_uri. Telegram and Bluesky, which connect in one request, return 409 account.already_linked from POST /v1/accounts/connect, and so does POST /v1/accounts/pending/{connection_id}/select for a Google location. On Facebook, connecting a brand whose account came from a different Facebook login ends with error=account.different_login on your redirect_uri. A Google login with more than one YouTube channel connects every channel in one authorization, and the redirect carries every account in account_ids. Without brand_id, each channel connects into its own new brand named after the channel. With a brand_id, the brand takes one channel, and each further channel ends the connection with status=error and error=account.already_linked on your redirect_uri. Channels connected before the error stay connected.

Let someone else connect an account

An invite link lets a person who has no access to your SocialAPI account connect their own account to one of your brands. Agencies send it to clients. The link works once.
Response (HTTP 201):
Send url to the person. They approve on the platform’s consent screen, SocialAPI connects the account to the brand, and account.connected is sent to your webhook endpoints.
  • A brand_id that is not one of your brands returns 404 resource.not_found.
  • A brand that already holds an account on that platform, or that already has an unused, unexpired invite for that platform, returns 409 resource.conflict.
  • A Facebook invite connects every Page of the person’s login that none of your other brands holds. The person cannot choose Pages. When facebook_page_selection is true, more Pages than your plan allows per brand end the invite with platform.facebook.page_cap.
  • An expired, used, or revoked link shows the person a SocialAPI error page.
  • GET /v1/invites?brand_id={brand_id} lists the brand’s invites, newest first, each with id, platform, token, url, is_active, expires_at, created_at, and permissions. A missing brand_id returns 400 validation.field_required.
  • DELETE /v1/invites/{id} revokes an invite and returns HTTP 204 with no body. An unknown or already used invite returns 404 resource.not_found.
Creating, listing, and revoking invites needs a full-access key or the dashboard, and costs no credits.

Disconnect an account

DELETE /v1/accounts/{id} disconnects an account:
Response: HTTP 204 with no body. An ID that is not one of your accounts returns 404 account.not_found.
Disconnecting cannot be undone. Connecting the same platform account again creates a new account with a new id, and nothing below comes back.
Disconnecting an account:
  • Removes the account. It no longer appears in GET /v1/accounts, and every request with its ID returns 404 account.not_found. Its brand stays.
  • Deletes what SocialAPI stored for it: its Pages and board settings, its Automations and their run history, its DM conversations and messages, its comments and review replies, and the attachments received in its messages.
  • Removes the account from your posts. A post that targeted only this account is deleted. A post that also targeted other accounts keeps those targets.
  • Asks the platform to end SocialAPI’s access on Instagram, Facebook, Threads, Google Business Profile, YouTube, TikTok, X, and WhatsApp. The account is disconnected even when the platform does not answer. SocialAPI keeps the authorization while another connected account still uses the same platform login, so disconnecting one brand’s Facebook or Google account never cuts off another brand.
  • Leaves the authorization in place on LinkedIn, Pinterest, Zalo, and Bluesky, which offer SocialAPI no way to end it. The person removes SocialAPI from their platform settings (on Bluesky, deletes the app password). For Telegram, SocialAPI stops receiving the bot’s messages, and the bot token stays valid until you revoke it in @BotFather.
DELETE /v1/accounts/{id} does not send a webhook event.

Errors

POST /v1/accounts/connect refuses a request before the consent screen with these codes. Every code is described in Errors.
  • A missing platform returns 400 validation.field_required.
  • A state longer than 512 characters returns 400 validation.customer_state_too_long.
  • A slug not in the platform slugs table, including trustpilot, returns 403 billing.platform_not_available.
  • A request without brand_id when your plan’s brand allowance is used up returns 403 billing.brands_limit.
  • A brand_id that is not one of your brands returns 404 resource.not_found, with meta.resource set to brand. A brand_id that is one of your brands but outside your brand-restricted key’s brands returns 404 brand.not_found.
  • A malformed or unregistered redirect_uri returns 400 validation.redirect_uri_invalid_format or 400 validation.redirect_uri_not_whitelisted.
  • An empty permissions list returns 400 validation.permissions_empty, and an unknown permission name returns 400 validation.permission_unknown, with meta.permission naming it. POST /v1/invites returns both codes too.
  • X with no app credentials saved returns 412 byok.credentials_missing.
  • Telegram or Bluesky with a missing metadata field returns 400 validation.field_required, with meta.field naming it.
Errors after the consent screen reach your redirect_uri as status=error (see Redirect parameters).

Scopes

A brand-restricted key must pass a brand_id from its own list. A connection started without brand_id creates a new brand that the key cannot see. Invite endpoints need a full-access key. See Scoped API keys.

Events

  • account.connected is sent each time an account connects or reconnects.
  • account.updated with change: "permissions_reduced" is sent when an account loses a permission you did not remove yourself: see Account permissions.
  • account.disconnected is sent in two cases. When the person removes SocialAPI’s app from Instagram, Facebook, or Threads, SocialAPI disconnects and deletes the account as DELETE /v1/accounts/{id} does, and the event carries account_id, platform, and platform_user_id. When an account needs reconnecting, the account stays connected and the event carries reconnect_required: true and a reason.
The reason of a reconnect event names the cause. It equals reconnect_reason on GET /v1/accounts, with these exceptions:
  • access_revoked is sent for access_revoked itself, for password_changed, session_expired, invalid_access_token, app_not_installed, user_checkpointed, checkpoint_required, and user_not_confirmed, and for whatsapp_number_unavailable.
  • number_banned is sent for whatsapp_number_banned.
  • No event is sent for platform_unauthorized.
Payloads are in Webhook events.

Use from an AI agent

See MCP tools for every tool.
Last modified on October 26, 2026