## Site IP lists (`site-ip-lists`)

Permanent IP blacklist and whitelist of one site; blacklisted IPs are dropped and whitelisted IPs skip CC and other protections. The site's temporary whitelist on nodes can also be cleared.

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

### GET `/api/v2/site-ip-lists/{site_id}`

Get site IP blacklist and whitelist · Authentication: User (some parameters admin only)

```sh
curl "https://panel.example.com/api/v2/site-ip-lists/{site_id}" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### PUT `/api/v2/site-ip-lists/{site_id}`

Set site IP blacklist and whitelist · Authentication: User (some parameters admin only)

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `black` | string[] |  | Permanent blacklist (a match = connection dropped); the count is limited by protect.black_ip_max. |
| `black_items` | IpListItem[] |  | Blacklist entries with expiry, reason and source; expired entries are not returned. |
| `expected_updated_at` | string |  | Optimistic lock - the updated_at as the caller read it (when the read updated_at is null, i.e. never set, use 1970-01-01T00:00:00Z). If it differs from the stored one, 409 stale_version is returned and nothing is saved; omit to skip the check. |
| `white` | string[] |  | Permanent whitelist (a match = CC and other protections are skipped); the count is limited by protect.white_ip_max. |
| `white_items` | IpListItem[] |  | Whitelist entries with expiry, reason and source; expired entries are not returned. |

```sh
curl -X PUT "https://panel.example.com/api/v2/site-ip-lists/{site_id}" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"black":["198.51.100.0/24"],"white":["203.0.113.9"]}'
```

### GET `/api/v2/site-ip-lists/{site_id}/export`

Export site IP list entries · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `list` | string | Yes | Which list to export. Values: ["black","white"] |
| `source` | IpListSource |  | Only export entries from this source; omit for all. Values: ["manual","import","ip_detail","api"] |
| `format` | string |  | txt (default, one entry per line) or csv (with header, a source column and a UTF-8 BOM). Values: ["txt","csv"] Default: "txt" |

```sh
curl "https://panel.example.com/api/v2/site-ip-lists/{site_id}/export" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/site-ip-lists/{site_id}/import`

Bulk import site IP list entries · Authentication: User (some parameters admin only)

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `dry_run` | boolean |  | true only checks the file and saves nothing. Default: false |
| `expected_updated_at` | string |  | Optimistic lock - the updated_at as the caller read it (when the read updated_at is null, i.e. never set, use 1970-01-01T00:00:00Z). If it differs from the stored one, 409 stale_version is returned and nothing is saved; omit to skip the check. |
| `list` | string | Yes | Which list to import into. Values: ["black","white"] |
| `mode` | string |  | merge adds to the current list (the imported row wins for the same IP); replace swaps the whole list for the imported rows. Values: ["merge","replace"] Default: "merge" |
| `text` | string | Yes | File content (txt/csv), one entry per line: IP/CIDR[,expiry][,reason]. |

```sh
curl -X POST "https://panel.example.com/api/v2/site-ip-lists/{site_id}/import" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dry_run":true,"list":"black","mode":"merge","text":"198.51.100.0/24,2030-01-01T00:00:00Z,scanner\n"}'
```

### GET `/api/v2/site-ip-lists/{site_id}/ip-detail`

IP detail · Authentication: User (some parameters admin only)

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `ip` | string | Yes | IPv4 or IPv6 address. |

```sh
curl "https://panel.example.com/api/v2/site-ip-lists/{site_id}/ip-detail" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/site-ip-lists/{site_id}/ip-detail`

Blacklist or whitelist from IP detail · Authentication: User (some parameters admin only)

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `expires_at` | string |  | Expiry time; empty = permanent. Must be in the future. |
| `ip` | string | Yes | Normalized address (IPv4-mapped addresses shown as IPv4). |
| `list` | string | Yes | Which list to import into. Values: ["black","white"] |
| `reason` | string |  | Reason (note), at most 200 characters. |
| `scope` | string | Yes | site = this site's list; global = the global list (admins only). Values: ["site","global"] |

```sh
curl -X POST "https://panel.example.com/api/v2/site-ip-lists/{site_id}/ip-detail" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/site-ip-lists/{site_id}/tmp-white-clear`

Clear site temporary whitelist · Authentication: User (some parameters admin only)

```sh
curl -X POST "https://panel.example.com/api/v2/site-ip-lists/{site_id}/tmp-white-clear" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

Site IP lists object:

| Field | Type | Required | Description |
|---|---|---|---|
| `added` | integer |  | Entries added. |
| `applied` | boolean |  | Whether the import was saved (false for dry_run or when blocking issues exist). |
| `black` | string[] |  | Permanent blacklist (a match = connection dropped); the count is limited by protect.black_ip_max. |
| `black_items` | IpListItem[] |  | Blacklist entries with expiry, reason and source; expired entries are not returned. |
| `block_denied` | string |  | Why it cannot be blacklisted: private (private/loopback/link-local), node (a node IP; admins only), self (the caller's own IP), other (any other reason; tenants get this for node IPs so node addresses are not revealed); omitted when allowed. Values: ["private","node","self","other"] |
| `dry_run` | boolean |  | true only checks the file and saves nothing. Default: false |
| `expected_updated_at` | string |  | Optimistic lock - the updated_at as the caller read it (when the read updated_at is null, i.e. never set, use 1970-01-01T00:00:00Z). If it differs from the stored one, 409 stale_version is returned and nothing is saved; omit to skip the check. |
| `expires_at` | string |  | Expiry time; empty = permanent. Must be in the future. |
| `geo` | GeoipLookup |  | IP geolocation lookup result. |
| `global_black` | IpListItem |  | IP list entry (in IP details, the entry that matches the IP; omitted when none matches). |
| `global_white` | IpListItem |  | IP list entry (in IP details, the entry that matches the IP; omitted when none matches). |
| `ip` | string |  | Normalized address (IPv4-mapped addresses shown as IPv4). |
| `issues` | IpListImportIssue[] |  | Per-line issues (at most 1000, ascending by line). |
| `lines` | integer |  | Number of valid entries (after de-duplication). |
| `list` | string | Yes | Which list to import into. Values: ["black","white"] |
| `mode` | string |  | merge adds to the current list (the imported row wins for the same IP); replace swaps the whole list for the imported rows. Values: ["merge","replace"] Default: "merge" |
| `reason` | string |  | Reason (note), at most 200 characters. |
| `removed` | integer |  | In replace mode, existing entries that are not in the new list (entries with the same IP count as updated, not removed). |
| `scope` | string | Yes | site = this site's list; global = the global list (admins only). Values: ["site","global"] |
| `site_black` | IpListItem |  | IP list entry (in IP details, the entry that matches the IP; omitted when none matches). |
| `site_id` | integer |  | Site ID (read-only) |
| `site_white` | IpListItem |  | IP list entry (in IP details, the entry that matches the IP; omitted when none matches). |
| `task_id` | integer |  | Id of the background task that pushes the change to nodes (poll progress with GET /tasks/{id}). |
| `text` | string | Yes | File content (txt/csv), one entry per line: IP/CIDR[,expiry][,reason]. |
| `updated` | integer |  | Entries already in the list that were updated by the import. |
| `updated_at` | string |  | Updated at (read-only) |
| `white` | string[] |  | Permanent whitelist (a match = CC and other protections are skipped); the count is limited by protect.white_ip_max. |
| `white_items` | IpListItem[] |  | Whitelist entries with expiry, reason and source; expired entries are not returned. |
