# Use your own platform apps

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.

> **Not available yet:**
> 
> 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 |
| --- | --- | --- |
| Instagram | Meta app → Instagram → API setup with Instagram login | Advanced access, through Meta's App Review |
| Facebook | 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 |
| LinkedIn | 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 |
| Pinterest | 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 |
| WhatsApp | 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.

```bash cURL
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-..."
  }'
```
```ts Node
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;
```
```python Python
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](https://pozy.ai/docs/connect/hosted-connect):

```bash
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`. |
| Instagram | 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 `PATCH` applies to every account on the app.
- `test` refreshes `status`, `lastCheckedAt` and `lastError`. `invalid` means 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 `oauthAppId` uses the default app.
- Removing an app that still has accounts returns `409 OAUTH_APP_IN_USE` with `details.accountCount`. Pass `confirm=true` to remove it anyway: those accounts are marked as needing reconnection and each one fires `account.disconnected`. The response says how many in `disconnectedAccounts`.

## 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_changed` with `missingScopeAreas`. See [Scopes](https://pozy.ai/docs/connect/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`](https://pozy.ai/docs/api/listOAuthApps) | List your OAuth apps |
| [`POST /v1/connect/oauth-apps`](https://pozy.ai/docs/api/createOAuthApp) | Add an OAuth app (not available yet) |
| [`GET /v1/connect/oauth-apps/{appId}`](https://pozy.ai/docs/api/getOAuthApp) | Get an OAuth app |
| [`PATCH /v1/connect/oauth-apps/{appId}`](https://pozy.ai/docs/api/updateOAuthApp) | Update an OAuth app |
| [`DELETE /v1/connect/oauth-apps/{appId}`](https://pozy.ai/docs/api/deleteOAuthApp) | Remove an OAuth app |
| [`POST /v1/connect/oauth-apps/{appId}/test`](https://pozy.ai/docs/api/testOAuthApp) | Check an OAuth app's keys |
| [`GET /v1/connect/{platform}`](https://pozy.ai/docs/api/getConnectUrl) | Get OAuth connect URL |
