Source: https://developers.tokportal.com/bans-and-appeals/
Markdown: https://developers.tokportal.com/bans-and-appeals.md

# Bans & Appeals

When a platform bans one of your delivered accounts, TokPortal runs a validated lifecycle: the account manager reports the ban, files a platform appeal when one is available, and — once the ban is confirmed — TokPortal staff decides the commercial outcome. This page's endpoint lets you **poll that whole lifecycle** and show it to your own users, instead of leaving them without feedback while an appeal is pending.

Only **validated** reports appear here (reported by the manager or confirmed by staff). Detections from TokPortal's internal health scans are never listed.

## The lifecycle

```
Manager reports a ban
├─ platform appeal available  → status: appeal_pending      (account in limbo, NOT banned yet)
│    ├─ platform accepts      → status: appeal_accepted     (account survived, case closed)
│    └─ platform refuses      → status: appeal_refused      (ban validated)
└─ no appeal available        → status: no_appeal_banned    (ban validated)

Ban validated (appeal_refused | no_appeal_banned)
└─ staff decides              → resolution:
     ├─ refund     — eligible credits restored (refund_credits)
     ├─ remake     — mission reset & relisted under the SAME bundle_id
     └─ no_remake  — no compensation (reason_code, e.g. tos_ban)
```

Key behaviors:

- **`appeal_pending` is limbo, not a ban.** The account is unavailable while the platform reviews the appeal, but it is not marked banned, its bundles stay open, and TokPortal deliberately does **not** recreate it (a duplicate created during a pending appeal can trigger a ban-evasion strike).
- **A validated ban cancels every active bundle and order** on the account and marks it `banned` on `GET /accounts` (the account stays visible).
- **`remake` keeps the `bundle_id` stable** — an `account.remade` event fires and the rebuilt account surfaces through subsequent `account.*` events on the same bundle.
- **`reason_code: "tos_ban"`** means the account was banned for a direct Terms of Service violation; the case closes with no refund.

Each step has a push counterpart: [`account.ban_appeal.submitted`](https://developers.tokportal.com/webhooks#the-accountban_appealsubmitted-event), [`account.ban_appeal.resolved`](https://developers.tokportal.com/webhooks#the-accountban_appealresolved-event), [`account.banned`](https://developers.tokportal.com/webhooks#the-accountbanned-event), and [`account.ban_resolution.decided`](https://developers.tokportal.com/webhooks#the-accountban_resolutiondecided-event).

## List Ban Reports

```
GET /account-bans
```

Returns a paginated list of validated ban reports for your delivered accounts, most recently updated first.

| Parameter             | Type              | Description                                                                                                                       |
| --------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `status`              | string            | *(Optional)* `appeal_pending`, `appeal_accepted`, `appeal_refused`, or `no_appeal_banned`.                                        |
| `resolution`          | string            | *(Optional)* `refund`, `remake`, `no_remake`, or `pending` (confirmed bans still awaiting the staff decision).                    |
| `account_id`          | string (UUID)     | *(Optional)* Only reports for one saved account.                                                                                  |
| `since`               | string (ISO 8601) | *(Optional)* Only reports updated at or after this timestamp — use the highest `updated_at` you have seen as a polling watermark. |
| `include_screenshots` | boolean           | *(Optional)* When `true`, adds a signed 7-day `screenshot_url` of the ban evidence when one exists.                               |
| `page`                | integer           | Page number (default: `1`).                                                                                                       |
| `per_page`            | integer           | Results per page (default: `25`, max: `100`).                                                                                     |

```bash
curl -X GET "https://app.tokportal.com/api/ext/account-bans?resolution=pending&per_page=25" \
  -H "X-API-Key: sk_xxx"
```

**Response:**

```json
{
  "data": [
    {
      "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
      "account_id": "7c9e0f1a-2b3c-4d5e-8f6a-1b2c3d4e5f60",
      "username": "coolcreator99",
      "platform": "tiktok",
      "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
      "order_id": "0d1e2f3a-4b5c-6789-8abc-def012345678",
      "status": "appeal_pending",
      "reported_at": "2026-08-13T09:12:00Z",
      "decided_at": null,
      "updated_at": "2026-08-13T09:12:00Z",
      "resolution": null
    },
    {
      "id": "1f2e3d4c-5b6a-4798-8899-aabbccddeeff",
      "account_id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e",
      "username": "trendwatcher22",
      "platform": "tiktok",
      "bundle_id": "22222222-3333-4444-8555-666666666666",
      "order_id": null,
      "status": "appeal_refused",
      "reported_at": "2026-08-10T14:03:00Z",
      "decided_at": "2026-08-12T18:40:00Z",
      "updated_at": "2026-08-12T19:05:00Z",
      "resolution": {
        "resolution": "refund",
        "reason_code": "eligible_refund",
        "refund_credits": 42,
        "resolved_at": "2026-08-12T19:05:00Z"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 2,
    "total_pages": 1
  }
}
```

### Response Fields

| Field            | Type                      | Description                                                                                                           |
| ---------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `id`             | string (UUID)             | The ban report identifier (matches `appeal_id` in the webhook payloads).                                              |
| `account_id`     | string (UUID) \| null     | The saved account. `null` after a staff reset deleted the account row — key on `bundle_id`.                           |
| `username`       | string \| null            | Handle of the account at report time.                                                                                 |
| `platform`       | string                    | `tiktok`, `instagram`, or `youtube`.                                                                                  |
| `bundle_id`      | string (UUID) \| null     | The bundle the report is attached to. Stable across remakes.                                                          |
| `order_id`       | string (UUID) \| null     | The order the report is attached to, when one exists.                                                                 |
| `status`         | string                    | `appeal_pending`, `appeal_accepted`, `appeal_refused`, or `no_appeal_banned`.                                         |
| `reported_at`    | string (ISO 8601) \| null | First durable manager report of the ban incident.                                                                     |
| `decided_at`     | string (ISO 8601) \| null | Platform appeal decision timestamp. `null` while the appeal is pending.                                               |
| `updated_at`     | string (ISO 8601) \| null | Last change to the report — your polling watermark.                                                                   |
| `resolution`     | object \| null            | Staff commercial decision, `null` while the case is open: `{ resolution, reason_code, refund_credits, resolved_at }`. |
| `screenshot_url` | string \| null            | Signed 7-day URL of the ban evidence. Only present with `include_screenshots=true`.                                   |

> **TIP: Polling recipe**
> Poll `GET /account-bans?since=<highest updated_at you have seen>` every few minutes. A new row with `status: "appeal_pending"` means "appeal filed, account unavailable — waiting on the platform". A row whose `resolution` filled in tells you exactly how the case ended: `refund` (credits restored), `remake` (watch for `account.remade` and the rebuilt account on the same `bundle_id`), or `no_remake` (closed, see `reason_code`).

> **NOTE: note**
> The `resolution.reason_code` values are machine-readable: `eligible_refund`, `eligible_credit_restoration`, `replacement_approved`, `tos_ban`, `tos_violation`, `content_related`, `custom`. Free-text staff/manager notes are never exposed through the API; clients receive the decision email separately.

## Related

- [Delivered Accounts](https://developers.tokportal.com/saved-accounts) — the `banned` filter, `ban` and `ban_appeal` fields on account responses.
- [Webhooks](https://developers.tokportal.com/webhooks) — push counterparts of this endpoint.
- [Credits](https://developers.tokportal.com/credits) — how restored credits land back on your balance.
