## Bans (`bans`)

Records of blocked IPs: bans created by hand, unban by IP or for a whole site with an unban history, and automatic bans reported by nodes.

Authentication: User (some parameters admin only) · Docs: https://www-demo.kuxitong.com/en/docs/api/bans

### GET `/api/v2/bans`

List bans · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `page` | integer |  | Page number, starting from 1 Default: 1 |
| `page_size` | integer |  | Items per page Default: 20 |
| `sort` | string |  | Sort fields, comma-separated; prefix `-` for descending, e.g. `-created_at,id` |
| `tenant_id` | integer |  | Admin only: filter by tenant. Ignored for tenant credentials, which only see their own tenant's data |
| `ip` | string |  | A single address matches exactly; a CIDR returns every ban inside that network (single addresses and networks no shorter than it). |
| `site_id` | integer |  | Filter by site. |
| `active` | boolean |  | true = only bans that have not expired. |
| `level` | string |  | Filter by scope (global = global blacklist). Values: ["site","tenant","global"] |
| `source` | string |  | Filter by source: manual = created in the panel; node = banned automatically by a node (challenge limit exceeded, rule ban, handshake flood, etc.). Values: ["manual","node"] |
| `source_type` | string |  | Filter by source type (manual / cc / waf / scan / status / tls / intel). Accepts a comma-separated list (e.g. status,tls; duplicates are dropped); every item must be one of the seven values, otherwise 422. Omit for all. |
| `created_from` | string |  | Only bans created at or after this instant (e.g. "added today"). A repeat ban by a node extends the existing row and does not change its creation time, so only the first record counts. |

```sh
curl "https://panel.example.com/api/v2/bans" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/bans`

Create ban · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `tenant_id` | integer |  | Admin only: tenant the ban belongs to. Required for level=tenant; optional for level=site (defaults to the site owner, and must match it when given). Ignored for tenant credentials |

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `expires_at` | string |  | Expiry time; null = permanent. |
| `ip` | string | Yes | IP or CIDR. |
| `level` | string | Yes | Scope: site = one site; tenant = all sites of the tenant; global = the whole network (admin only). Values: ["site","tenant","global"] |
| `reason` | string |  | Reason: manual by default for bans created in the panel; for automatic bans by a node, the triggering rule (ruleset/rule id such as cc.level.r5/r1) or module name (ssl_handshake = handshake flood), or auto when there is no rule. |
| `site_id` | integer |  | Site id, required when level=site; 0 otherwise. |

```sh
curl -X POST "https://panel.example.com/api/v2/bans" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ip":"198.51.100.7","level":"tenant"}'
```

### GET `/api/v2/bans/site-counts`

Count bans per site · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `page` | integer |  | Page number, starting from 1 Default: 1 |
| `page_size` | integer |  | Items per page Default: 20 |
| `tenant_id` | integer |  | Admin only: filter by tenant. Ignored for tenant credentials, which only see their own tenant's data |
| `source_type` | string |  | Filter by source type (manual / cc / waf / scan / status / tls / intel). Accepts a comma-separated list (e.g. status,tls; duplicates are dropped); every item must be one of the seven values, otherwise 422. Omit for all. |
| `created_from` | string |  | Only bans created at or after this instant (e.g. "added today"). A repeat ban by a node extends the existing row and does not change its creation time, so only the first record counts. |

```sh
curl "https://panel.example.com/api/v2/bans/site-counts" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### GET `/api/v2/bans/source-counts`

Count bans per source type · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `tenant_id` | integer |  | Admin only: filter by tenant. Ignored for tenant credentials, which only see their own tenant's data |
| `created_from` | string |  | Only bans created at or after this instant (e.g. "added today"). A repeat ban by a node extends the existing row and does not change its creation time, so only the first record counts. |

```sh
curl "https://panel.example.com/api/v2/bans/source-counts" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/bans/unban`

Unban by IP · Authentication: User (some parameters admin only)

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `all` | boolean |  | true = unban the whole site, clearing every ban of site_id; level=site only and counts as one unban toward the daily limit. |
| `ips` | string[] |  | IPs/CIDRs to unban (omit when all=true, required otherwise); the number per request is limited by protect.unban_batch_max and the daily count per tenant by protect.unban_daily_max. |
| `level` | string | Yes | Scope: site = one site; tenant = all sites of the tenant; global = the whole network (admin only). Values: ["site","tenant","global"] |
| `site_id` | integer |  | Site id, required when level=site; 0 otherwise. |

```sh
curl -X POST "https://panel.example.com/api/v2/bans/unban" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ips":["198.51.100.7"],"level":"tenant"}'
```

### GET `/api/v2/bans/unban-quota`

Unban quota and today's usage · Authentication: User (some parameters admin only)

```sh
curl "https://panel.example.com/api/v2/bans/unban-quota" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### GET `/api/v2/bans/unbans`

List unban records · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `page` | integer |  | Page number, starting from 1 Default: 1 |
| `page_size` | integer |  | Items per page Default: 20 |
| `tenant_id` | integer |  | Admin only: filter by tenant. Ignored for tenant credentials, which only see their own tenant's data |
| `site_id` | integer |  | Filter by site (site-level unbans only). |
| `since` | string |  | Only records at or after this time (for example to count today's unbans). |
| `by_admin` | boolean |  | Filter by whether an administrator unbanned on the site owner's behalf; pass false to count the owner's own unbans today (those done by an administrator do not use the owner's quota). |

```sh
curl "https://panel.example.com/api/v2/bans/unbans" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### DELETE `/api/v2/bans/{id}`

Delete ban · Authentication: User (some parameters admin only)

```sh
curl -X DELETE "https://panel.example.com/api/v2/bans/{id}" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

Bans object:

| Field | Type | Required | Description |
|---|---|---|---|
| `all` | boolean |  | true = unban the whole site, clearing every ban of site_id; level=site only and counts as one unban toward the daily limit. |
| `batch_max` | integer |  | Maximum IPs per unban request (setting protect.unban_batch_max). |
| `by_admin` | boolean |  | true = unbanned by an administrator on the site owner's behalf (recorded under and visible to the owner, does not use the owner's daily quota). |
| `count` | integer |  | Number of unexpired bans. |
| `created_at` | string |  | Created at (read-only) |
| `daily_max` | integer |  | Maximum unbans per tenant per day (setting protect.unban_daily_max). |
| `expires_at` | string |  | Expiry time; null = permanent. |
| `filter_id` | string |  | The filter (rule set or module) that triggered the ban, split from reason, such as cc.level.r5, waf, scan.builtin or ssl_handshake; absent for manual bans and for auto. (read-only) |
| `id` | integer |  | Record ID (read-only) |
| `intel` | BanIntel |  | Where a banned IP stands in the platform high-risk IP library right now (looked up against the current library when listing; fields the library does not carry, or entries already expired, are absent). |
| `intel_hit` | boolean |  | Whether the IP is in the platform high-risk IP library; null = not judged yet (always null until the library ships). |
| `ip` | string |  | IP or CIDR. |
| `ip_count` | integer |  | Number of unbanned IPs/CIDRs (0 for a whole-site unban). |
| `ips` | string[] |  | IPs/CIDRs to unban (omit when all=true, required otherwise); the number per request is limited by protect.unban_batch_max and the daily count per tenant by protect.unban_daily_max. |
| `items` | BanSourceCount[] |  | Items on this page |
| `level` | string |  | Scope: site = one site; tenant = all sites of the tenant; global = the whole network (admin only). Values: ["site","tenant","global"] |
| `reason` | string |  | Reason: manual by default for bans created in the panel; for automatic bans by a node, the triggering rule (ruleset/rule id such as cc.level.r5/r1) or module name (ssl_handshake = handshake flood), or auto when there is no rule. |
| `removed` | integer |  | Number of entries removed from the ban table at the same time. |
| `rule_id` | string |  | The rule number that triggered the ban, split from reason (r1 of cc.level.r5/r1, 941100 of waf:941100); absent when there is none. (read-only) |
| `site_id` | integer |  | Site id, required when level=site; 0 otherwise. |
| `source` | string |  | manual = created in the panel; node = banned automatically by a node and reported (one row per target while unexpired; a repeat ban extends it). Both are lifted the same way, by deleting or via /bans/unban. Values: ["manual","node"] (read-only) |
| `source_type` | BanSourceType |  | Source type: manual = created in the panel; cc = CC rule group (cc.level.*, cc.user.*, auto and other rule sets); waf = repeated WAF rule hits; scan = built-in scanner set; status = built-in status-code set; tls = handshake flood; intel = platform high-risk IP library. A ban made by a node is never manual. Values: ["manual","cc","waf","scan","status","tls","intel"] |
| `status` | string |  | Status of the push task pending/running/done/failed; null once the task has been purged. |
| `task_id` | integer |  | Id of the background task that pushes the change to nodes (poll progress with GET /tasks/{id}). |
| `tenant_id` | integer |  | Owning tenant; null = platform object (built-in or created by an admin, read-only for tenants). (read-only) |
| `updated_at` | string |  | Updated at (read-only) |
| `used` | integer |  | Unbans the tenant made itself today (since 00:00 UTC); unbans by an administrator do not count, and it is 0 when an administrator calls. |
