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 connects every Page at once, and you can assign Pages to brands afterwards (see Facebook). 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:auth_url. After they approve, their browser lands on your redirect_uri:
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, needed only forPOST /v1/oauth/exchange), andmessage. For WhatsApp,metadatacarries the values your page needs to open Meta’s signup popup, andauth_urlis empty. See WhatsApp. - HTTP 201 for Telegram and Bluesky, which connect without a consent screen:
account_id,platform,username, anddisplay_name.
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 yourredirect_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, if asked
When
status is selection_required, no account exists yet. Read the candidates and choose within 30 minutes, as described in Choose a Google Business Profile.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 examplebilling.brands_limit(your plan’s brand allowance is used up),account.already_linked(the brand already holds a different account on this platform),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), orbyok.app_access_denied(X refused your app access to its API). Each code is listed in Errors. missing_codewhen the platform returned neither a code nor an error.exchange_failedwhen the failure has no code.
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
Theplatform 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
Yourredirect_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 withstatus=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}:
platform_account_id:
Pending connection fields
Selection rules
- A pending connection expires 30 minutes after it is created and works once. The first successful
selectconsumes it. A secondselect, or a call after expiry, returns404 resource.not_found. - A
400keeps the pending connection. Fix the request and send it again within the 30 minutes. - A
platform_account_idthat is not inprofiles[]returns400 validation.profile_not_in_set. - A body without
platform_account_idreturns400 validation.field_required.
Facebook Pages
A Facebook connection has no selection step. SocialAPI adds every Page the login granted that none of your other brands holds to the brand’s Facebook account, and the redirect carriesstatus=success.
- A login that grants no Page ends with
status=erroranderror=platform.facebook.no_pageson yourredirect_uri. - A login whose every Page belongs to another of your brands ends with
status=erroranderror=platform.facebook.no_assignable_pageson yourredirect_uri.
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 getsstatus: 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.
Finish a connection from your own page
POST /v1/oauth/exchange finishes a connection when your page, not SocialAPI, receives the authorization code. WhatsApp works this way: your page opens Meta’s signup popup and receives the code. The WhatsApp page has the full sequence.
metadata.stateis thestatethatPOST /v1/accounts/connectreturned. It works once, for 1 hour, and only with the API key that started the connection. An unknown, expired, or usedstatereturns400 validation.state_invalid. Astatestarted with a different API key returns403 auth.state_mismatch.- A missing
platformorcodereturns400 validation.field_required, and so does a missingmetadata.state. - When one authorization connects more than one account, the response (HTTP 201) is
{"data": [...], "count": 2}with one entry per account. - When the connection needs a choice, the response is HTTP 200 with
"status": "selection_required", theconnection_id, and the sameprofilesandlogin_idfields asGET /v1/accounts/pending/{connection_id}.
Account status
GET /v1/accounts returns every connected account with its status:
Treat a
reconnect_reason you do not recognize as a request to reconnect.
Reconnect an account
To reconnect an account, callPOST /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:
- The account keeps its
id, its brand, its posts, conversations, and Automations. statusreturns toactiveandreconnect_reasonis cleared.- The account keeps its
permissionsunless you sendpermissionson the reconnect. A narrower list deletes the data the removed permissions covered: see Removing a permission deletes data. - SocialAPI sends
account.connectedagain.
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. A login whose every Page or Instagram account belongs to another brand ends with
error=platform.facebook.no_assignable_pagesorerror=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}/assignreturns400 validation.location_already_assignedfor a location that another brand holds. See Google locations. lock_accounts_to_brandset totrueon your SocialAPI account (PATCH /v1/users/me). A connect that would move an account to a different brand ends witherror=account.already_linked, or returns409 account.already_linked. The default isfalse. The lock does not apply to Facebook.
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.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_idthat is not one of your brands returns404 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.
- 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 withid,platform,token,url,is_active,expires_at,created_at, andpermissions. A missingbrand_idreturns400 validation.field_required.DELETE /v1/invites/{id}revokes an invite and returns HTTP 204 with no body. An unknown or already used invite returns404 resource.not_found.
Disconnect an account
DELETE /v1/accounts/{id} disconnects an account:
404 account.not_found.
Disconnecting an account:
- Removes the account. It no longer appears in
GET /v1/accounts, and every request with its ID returns404 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
platformreturns400 validation.field_required. - A
statelonger than 512 characters returns400 validation.customer_state_too_long. - A slug not in the platform slugs table, including
trustpilot, returns403 billing.platform_not_available. - A request without
brand_idwhen your plan’s brand allowance is used up returns403 billing.brands_limit. - A
brand_idthat is not one of your brands returns404 resource.not_found, withmeta.resourceset tobrand. Abrand_idthat is one of your brands but outside your brand-restricted key’s brands returns404 brand.not_found. - A malformed or unregistered
redirect_urireturns400 validation.redirect_uri_invalid_formator400 validation.redirect_uri_not_whitelisted. - An empty
permissionslist returns400 validation.permissions_empty, and an unknown permission name returns400 validation.permission_unknown, withmeta.permissionnaming it.POST /v1/invitesreturns both codes too. - X with no app credentials saved returns
412 byok.credentials_missing. - Telegram or Bluesky with a missing
metadatafield returns400 validation.field_required, withmeta.fieldnaming it.
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.connectedis sent each time an account connects or reconnects.account.updatedwithchange: "permissions_reduced"is sent when an account loses a permission you did not remove yourself: see Account permissions.account.disconnectedis sent in two cases. When the person removes SocialAPI’s app from Instagram, Facebook, or Threads, SocialAPI disconnects and deletes the account asDELETE /v1/accounts/{id}does, and the event carriesaccount_id,platform, andplatform_user_id. When an account needs reconnecting, the account stays connected and the event carriesreconnect_required: trueand areason.
reason of a reconnect event names the cause. It equals reconnect_reason on GET /v1/accounts, with these exceptions:
access_revokedis sent foraccess_revokeditself, forpassword_changed,session_expired,invalid_access_token,app_not_installed,user_checkpointed,checkpoint_required, anduser_not_confirmed, and forwhatsapp_number_unavailable.number_bannedis sent forwhatsapp_number_banned.- No event is sent for
platform_unauthorized.
Use from an AI agent
See MCP tools for every tool.
Related
- Account permissions for connecting an account with only the permissions you need
- Platform apps for which app each platform connects through
- Supported platforms for what each platform can do once connected
- Scoped API keys for keys limited to some brands
- Plans for the brand allowance
- Webhook events for the
account.connectedandaccount.disconnectedpayloads - Errors for every error code above