Connecting accounts
Coming soon. Connect your customers through your own app on each platform, so the consent screen shows your name and the platform's quotas are yours.
Using your own platform apps is coming soon. Until it is, POST /v1/connect/oauth-apps answers 403 with code: "feature_not_available", and every account connects through the platform's own consent screen with the default app. This page describes how it will work, so you can prepare your apps and their reviews in advance.
By default, accounts connect through the platform's own consent screen. If you run a product on top of Pozy you may want that screen to show your own app name and logo, and to get your own rate limits on platforms that set them per app. You will be able to register your own app for a platform and connect accounts through it, while Pozy still handles tokens, publishing, analytics and the inbox.
Nothing changes for accounts you connect without an app id: they stay on the default app. Switch one platform at a time, at your own pace.
#Before you start
- A workspace owner or admin, or a full-scope API key. Profile-scoped keys cannot manage apps and get
403 insufficient_permissions. - An app on the platform, created in its developer console, with its client ID and secret.
- Time for the platform's review. Most platforms only let outside accounts connect after they approve the app.
#Supported platforms
| Platform | Paste the redirect URI here | What the platform must approve first |
|---|---|---|
| Meta app → Instagram → API setup with Instagram login | Advanced access, through Meta's App Review | |
| Meta app → Facebook Login for Business → Valid OAuth redirect URIs | Advanced access, through Meta's App Review | |
| Threads | Meta app → Threads use case → Redirect callback URLs | Meta's App Review |
| YouTube | Google Cloud → OAuth client of type Web application → Authorized redirect URIs | A verified consent screen |
| Google Business Profile | Google Cloud → OAuth client of type Web application → Authorized redirect URIs | Access to the Business Profile APIs, granted by Google |
| Google (Gmail, Calendar, Drive) | Google Cloud → OAuth client of type Web application → Authorized redirect URIs | Verification of Gmail's restricted scopes |
| Microsoft (Outlook, Calendar, OneDrive) | Azure → App registrations → Authentication → Web platform redirect URI | Nothing |
| X | Developer portal → User authentication settings → Callback URI | Nothing |
| Developer app → Auth → Authorized redirect URLs | Community Management API access, needed for comments and analytics | |
| TikTok | Developer app → Login Kit → Redirect URI | Content Posting API, with direct posting enabled |
| Developer app → Settings → Redirect URIs | Standard access | |
| Snapchat | Business Manager → OAuth apps → Redirect URI | Public Profile API access |
| Slack | Slack app → OAuth & Permissions → Redirect URLs | Nothing, for installs in your own Slack workspace |
| No redirect: numbers connect with credentials | A system user token that can send messages and manage WhatsApp |
GET /v1/connect/oauth-apps returns the same list in platforms, each with its redirectUri, the scopes every connection needs (coreScopes) and the scopes behind each area (scopeAreas).
#Set up an app
#Add the app
Send the client ID and secret. Pozy checks them with the platform before saving.
curl -X POST "https://api.pozy.ai/v1/connect/oauth-apps" \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "youtube",
"label": "Acme Studio",
"clientId": "4471209931-q8r2s6t0.apps.googleusercontent.com",
"clientSecret": "GOCSPX-..."
}'const res = await fetch("https://api.pozy.ai/v1/connect/oauth-apps", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
platform: "youtube",
label: "Acme Studio",
clientId: "4471209931-q8r2s6t0.apps.googleusercontent.com",
clientSecret: process.env.YOUTUBE_CLIENT_SECRET!,
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
const appId = data.app.id;
const redirectUri = data.app.redirectUri;resp = requests.post(
"https://api.pozy.ai/v1/connect/oauth-apps",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
json={
"platform": "youtube",
"label": "Acme Studio",
"clientId": "4471209931-q8r2s6t0.apps.googleusercontent.com",
"clientSecret": os.environ["YOUTUBE_CLIENT_SECRET"],
},
timeout=30,
)
app = resp.json()["app"]You get 201 back with the new app: its id, its redirectUri and a status. ok means the platform took the keys. unchecked means the platform has no way to check them yet; Slack, for one, only validates an app when it is first installed.
If the platform approved your app for only part of what Pozy can do, list the parts in scopeAreas: any of posting, analytics, comments and messaging. A LinkedIn app starts with posting alone, since the rest depends on LinkedIn's Community Management API.
#Register the redirect URI
Take redirectUri from the response and add it to your app on the platform, at the spot the table above shows. Character for character, no slash at the end. The platform blocks every connect until the two match.
#Connect accounts through the app
Add oauthAppId to the usual connect call:
curl "https://api.pozy.ai/v1/connect/youtube?profileId=6650c1f2a9e4b7d3c8a1f0e2&oauthAppId=$APP_ID&redirect_url=https://app.example.com/connected" \
-H "Authorization: Bearer $POZY_API_KEY"Your app's name is what the user approves. After that, everything for the account (refreshing its token, posting, analytics, inbox) uses your keys. GET /v1/accounts shows the app behind each account in its oauthAppId field.
#Set up events where needed
Most platforms need nothing else. These do:
| Platform | What to do |
|---|---|
| Facebook, WhatsApp | Nothing manual: adding the app also subscribes Pozy to its events. The result is in app.webhook. |
Include metaAppId and metaAppSecret of the parent Meta app. Pozy needs them to subscribe to the Instagram app's events. |
|
| TikTok | In your TikTok app, set the webhook callback to the sharedWebhookUrl Pozy returns. Posting does not depend on it. |
| Slack | Add slackSigningSecret with the app. Then, under Event Subscriptions in Slack, use slackEvents.requestUrl as the request URL and add each bot event from slackEvents.botEvents. |
#Manage apps
| Job | Call |
|---|---|
| List your apps | GET /v1/connect/oauth-apps (paginated with cursor) |
| Read one | GET /v1/connect/oauth-apps/{appId} |
| Rotate the secret, rename, change areas | PATCH /v1/connect/oauth-apps/{appId} |
| Check the keys again | POST /v1/connect/oauth-apps/{appId}/test |
| Remove an app | DELETE /v1/connect/oauth-apps/{appId} |
- The client secret is never returned after you add it. Rotating it with
PATCHapplies to every account on the app. testrefreshesstatus,lastCheckedAtandlastError.invalidmeans connects and token refreshes through the app fail until you fix the keys.- You can add several apps for the same platform. Nothing picks one automatically: a connect without
oauthAppIduses the default app. - Removing an app that still has accounts returns
409 OAUTH_APP_IN_USEwithdetails.accountCount. Passconfirm=trueto remove it anyway: those accounts are marked as needing reconnection and each one firesaccount.disconnected. The response says how many indisconnectedAccounts.
#Limits
- Instagram apps cover Instagram login only. Ads connects and WhatsApp Embedded Signup always use the default app.
- A WhatsApp app you add has its webhook callback set to Pozy, replacing any other. Keep a separate Meta app for Pozy.
- Fresh Google Cloud projects get a tiny YouTube upload quota, roughly six videos a day. Request a higher quota from Google before customers depend on it.
- If your app loses an area, for example after a platform review, the accounts on it fire
account.permissions_changedwithmissingScopeAreas. See Scopes.
#Errors
| Error | Why | What to do |
|---|---|---|
OAUTH_APP_KEYS_REJECTED |
The platform said no to the client ID or secret, so nothing was stored. | Copy both values from the console again. |
409 OAUTH_APP_ALREADY_EXISTS |
That client ID is already registered for the platform. | Reuse the app named in details.appId, or PATCH it with a new secret. |
403 insufficient_permissions or role_forbidden |
The key is profile-scoped, or the user is not an owner or admin. | Use a full-scope key or an admin account. |
| The platform shows a redirect URI error | The URI in your app does not match redirectUri. |
Paste redirectUri exactly. |
| The consent screen warns that the app is unapproved | The platform's review of your app is not done. | Complete the review, or list the user as a tester of the app. |
webhook.status is failed |
Subscribing to your Meta app's events did not work. | webhook.error says why. For Instagram, PATCH the app with metaAppId and metaAppSecret. |
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/connect/oauth-apps |
List your OAuth apps |
POST /v1/connect/oauth-apps |
Add an OAuth app (not available yet) |
GET /v1/connect/oauth-apps/{appId} |
Get an OAuth app |
PATCH /v1/connect/oauth-apps/{appId} |
Update an OAuth app |
DELETE /v1/connect/oauth-apps/{appId} |
Remove an OAuth app |
POST /v1/connect/oauth-apps/{appId}/test |
Check an OAuth app's keys |
GET /v1/connect/{platform} |
Get OAuth connect URL |