# Account health

Check token status, granted permissions and sync status for every connected account, and catch disconnects before your users do.

Connected accounts break for reasons outside your control: a password change on the platform, a revoked app, an expired token, a Page role removed. Account health tells you, per account, whether Pozy can still post and read analytics, what is wrong if not, and what to do about it.

## Check every account

`GET /v1/accounts/health` returns a summary and one row per account. Filter with `profileId`, `platform` or `status`.

```bash cURL
curl "https://api.pozy.ai/v1/accounts/health?status=error" \
  -H "Authorization: Bearer $POZY_API_KEY"
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/accounts/health?status=error", {
  headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);

for (const a of data.accounts) {
  if (a.needsReconnect) console.log(`${a.platform} @${a.username} needs reconnecting: ${a.issues.join("; ")}`);
}
```
```python Python
resp = requests.get(
    "https://api.pozy.ai/v1/accounts/health",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    params={"status": "error"},
    timeout=30,
)
for a in resp.json()["accounts"]:
    if a["needsReconnect"]:
        print(a["platform"], a["username"], a["issues"])
```

```json
{
  "summary": { "total": 14, "healthy": 11, "warning": 2, "error": 1, "needsReconnect": 1 },
  "accounts": [
    {
      "accountId": "6651d3a7b2c9e4f1a0b8c7d6",
      "platform": "linkedin",
      "username": "acme",
      "profileId": "6650c1f2a9e4b7d3c8a1f0e2",
      "status": "error",
      "canPost": false,
      "canFetchAnalytics": false,
      "tokenValid": false,
      "needsReconnect": true,
      "issues": ["The access token is no longer valid."],
      "disconnectionType": "unintentional",
      "unhealthySince": "2026-10-29T18:04:11.000Z",
      "analyticsSync": { "status": "failing", "lastSyncedAt": "2026-10-28T06:00:00.000Z" }
    }
  ]
}
```

A profile-scoped key only sees the accounts in its profiles, and the summary counts only those.

## Check one account in detail

`GET /v1/accounts/{accountId}/health` adds the token details, the scope-by-scope permission list and recommendations.

| Field | What it tells you |
| --- | --- |
| `status` | `healthy`, `warning` or `error`. |
| `tokenStatus` | `valid`, `expiresAt`, `expiresIn` and `needsRefresh`. Pozy refreshes tokens on its own; this is for diagnosis. |
| `permissions` | Each scope in the `posting`, `analytics` and `optional` groups with `granted` and `required`, plus `canPost`, `canFetchAnalytics` and `missingRequired`. See [Scopes](https://pozy.ai/docs/connect/scopes). |
| `issues` | Plain-language problems, ready to show an operator. |
| `recommendations` | What to do next, for example reconnecting through a specific endpoint. |
| `analyticsSync` | `ok`, `failing`, `stalled` or `never_synced`, with `lastSyncedAt`. A failing or stalled sync raises a healthy account to at least `warning`. |
| `messagingRestriction` | Set when the platform has paused messaging on the account, with the platform's `code`, `message` and `pausedUntil`. |
| `platformConnection` | WhatsApp only: a live check of the number behind the account. See below. |
| `disconnectReason`, `disconnectionType`, `unhealthySince` | Why and since when the account stopped working. |

### What `status` means

| `status` | Meaning | Typical action |
| --- | --- | --- |
| `healthy` | Posting and analytics work. | None. |
| `warning` | The account works, but something needs attention, for example a failing analytics sync. | Read `issues`. |
| `error` | Pozy cannot use the account for at least one core job. | Reconnect, or fix the cause in `issues`. |

### Meta accounts

For Facebook, Instagram and Threads, Pozy also checks the token live with Meta. Meta sometimes refuses a valid token for a moment, so a refusal does not mark the token invalid if an analytics sync succeeded with it in the last six hours; `issues` then says the check was inconclusive. Health checks never disconnect an account on their own.

### WhatsApp

A WhatsApp account's token can be valid while Meta no longer serves the number behind it, for example after the number was disconnected from the WhatsApp Business app on the phone. `platformConnection` is the live check:

- `platformConnection.status: "disconnected"` sets the overall `status` to `error`.
- `inboundWebhookSubscribed: false` means incoming messages are not reaching Pozy. It adds an issue and sets `status` to at least `warning`.

For WhatsApp, poll health rather than relying only on the `account.disconnected` event. See [WhatsApp phone numbers](https://pozy.ai/docs/whatsapp/phone-numbers).

## How disconnects reach you

`account.disconnected` fires when an account stops working or is removed. How fast depends on what happened:

| Cause | `disconnectionType` | When the event fires |
| --- | --- | --- |
| You called `DELETE /v1/accounts/{accountId}` | `intentional` | Immediately. |
| WhatsApp number disconnected from the phone | `unintentional` | Within seconds of Meta's notice. `reason` carries Meta's explanation. |
| A publish or analytics sync gets a definitive auth error | `unintentional` | On that call. For publishing, Pozy tries a token refresh first, so the event means the refresh failed too. |
| Access revoked quietly on the platform side | `unintentional` | When Pozy's periodic checks confirm it, from minutes to about a day later. |

Do not treat the absence of `account.disconnected` as proof an account works. Combine the webhook with a scheduled health check.

## Reconnect an account

Send the user through the connect flow again with `reconnectAccountId`. The account keeps its id, posts and analytics history.

```bash
curl "https://api.pozy.ai/v1/connect/linkedin?profileId=6650c1f2a9e4b7d3c8a1f0e2&reconnectAccountId=6651d3a7b2c9e4f1a0b8c7d6&redirect_url=https://app.example.com/connected" \
  -H "Authorization: Bearer $POZY_API_KEY"
```

For standalone ads connections (Google Ads, Reddit Ads), `recommendations` points to `GET /v1/connect/{platform}/ads` instead. Its `authUrl` reconnects the same ads account.

> **A reconnect banner that works:**
> 
> Show a banner in your app for every account with `needsReconnect: true`. Name the platform and the handle, and add a button that calls your backend to create the reconnect URL.

## Facebook Page events

Lead ads, comments and messages on a Facebook Page arrive through the Page's webhook subscription. If one of them stops while the rest still work, read the subscription live with [`GET /v1/accounts/{accountId}/webhook-subscription`](https://pozy.ai/docs/api/getPageWebhookSubscription) and repair it with [`POST /v1/accounts/{accountId}/webhook-subscription`](https://pozy.ai/docs/api/resyncPageWebhookSubscription).

## Disconnect an account

`DELETE /v1/accounts/{accountId}` disconnects the account and fires `account.disconnected` with `disconnectionType: "intentional"`. Calling it again for the same account returns `404`.

## A daily health job

```ts
export async function dailyHealthCheck() {
  const res = await fetch("https://api.pozy.ai/v1/accounts/health", {
    headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error);
  const broken = data.accounts.filter((a) => a.status === "error" || a.needsReconnect);
  for (const a of broken) {
    await notifyOwner(a.profileId, `Your ${a.platform} account @${a.username} needs to be reconnected.`);
  }
}
```

`notifyOwner` is your own code: email, in-app message, or a Slack alert to your support team.

## Webhook events

| Event | When it fires |
| --- | --- |
| [`account.connected`](https://pozy.ai/docs/webhooks/events#account-connected) | Account connected. |
| [`account.disconnected`](https://pozy.ai/docs/webhooks/events#account-disconnected) | Account disconnected. |
| [`account.permissions_changed`](https://pozy.ai/docs/webhooks/events#account-permissions-changed) | Account permissions changed. |

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/accounts/health`](https://pozy.ai/docs/api/getAllAccountsHealth) | Check accounts health |
| [`GET /v1/accounts/{accountId}/health`](https://pozy.ai/docs/api/getAccountHealth) | Check account health |
| [`GET /v1/accounts`](https://pozy.ai/docs/api/listAccounts) | List accounts |
| [`DELETE /v1/accounts/{accountId}`](https://pozy.ai/docs/api/deleteAccount) | Disconnect account |
| [`GET /v1/accounts/{accountId}/webhook-subscription`](https://pozy.ai/docs/api/getPageWebhookSubscription) | Read a Facebook Page's webhook subscription |
| [`POST /v1/accounts/{accountId}/webhook-subscription`](https://pozy.ai/docs/api/resyncPageWebhookSubscription) | Re-subscribe a Facebook Page to Pozy's webhooks |
