## Pending nodes (`pending-nodes`)

Nodes that have enrolled with a token and await admin approval; once approved they become regular nodes.

Authentication: Admin only · Docs: https://www-demo.kuxitong.com/en/docs/api/pending-nodes

### GET `/api/v2/pending-nodes`

List pending nodes · Authentication: 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` |

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

### DELETE `/api/v2/pending-nodes/{id}`

Delete pending node · Authentication: Admin only

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

### POST `/api/v2/pending-nodes/{id}/approve`

Approve pending node · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `des` | string |  | Description |
| `ip` | string |  | Source address of the registration connection. |
| `name` | string |  | Name |
| `region_id` | integer |  | Region ID |
| `replace_node_id` | integer |  | Replace an existing primary node (new machine takes over the old one): the new node inherits the old node's lines, IP pool IP, region (when region_id is omitted), monitor settings, service period and area/bandwidth; the old node is disabled and removed from DNS, all in the approval transaction. The old node's sub IPs move to the new node by default (lines, pool IPs and monitor settings kept); see replace_sub_ips for new addresses or opting out. The old node must be an approved primary node with the same role; 409 ip_in_use when its pool IP is bound to IP sites and the IP changes. |
| `replace_sub_ips` | ReplaceSubIp[] |  | Per-sub-IP handling of the old node when replacing (only with replace_node_id); unlisted sub IPs move to the new node keeping their address. ip switches to a new address (422 conflict if another node uses it); skip=true leaves the sub IP and its lines on the old node, disabled. |

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

Pending nodes object:

| Field | Type | Required | Description |
|---|---|---|---|
| `area` | string |  | Area (display only, never sent to nodes); empty = not set. Sub-IPs take the primary node's value. |
| `bandwidth_mbps` | integer |  | Node bandwidth in Mbps (display only, unrelated to monitor.bw_limit_mbps); 0 = not set. Sub-IPs take the primary node's value. |
| `capacity` | NodeCapacity |  | Node memory capacity (read-only, only returned by GET /nodes/{id}?capacity=true): per-site resident cost = locations × 28.1KB + HTTPS ratio × 25.5KB, budget = memory MiB × 0.25 / 2 − 28MiB, limit = budget / per-site cost. null = no system info for the node or memory total missing. (read-only) |
| `cert_expiry` | string |  | Expiry time of the most recently issued certificate; null = no certificate issued yet (or a sub-IP). |
| `created_at` | string |  | Created at (read-only) |
| `des` | string |  | Description |
| `disable_by` | string |  | Disable source (manual = admin; monitor = auto-disabled by node monitoring and re-enabled once probes recover; bandwidth = auto-disabled when outbound bandwidth exceeds monitor.bw_limit_mbps and re-enabled when it drops back); empty when enabled. |
| `enabled` | boolean |  | Whether enabled |
| `enrolled_at` | string |  | Time the node registered. |
| `hostname` | string |  | Host name reported by the node. |
| `id` | integer |  | Record ID (read-only) |
| `ip` | string |  | Source address of the registration connection. |
| `monitor` | NodeMonitor |  | Node monitor probe settings (the control plane probes this IP at the interval configured in monitor.node). |
| `name` | string |  | Name |
| `ops` | NodeOps |  | Node operations status (read-only, from heartbeats): online, whether the config lags behind the latest published version, and clock sync. Sub-IPs follow their primary node. (read-only) |
| `parent_id` | integer |  | Primary node that owns this sub-IP; null = primary node. |
| `region_id` | integer |  | Region ID |
| `replace_node_id` | integer |  | Replace an existing primary node (new machine takes over the old one): the new node inherits the old node's lines, IP pool IP, region (when region_id is omitted), monitor settings, service period and area/bandwidth; the old node is disabled and removed from DNS, all in the approval transaction. The old node's sub IPs move to the new node by default (lines, pool IPs and monitor settings kept); see replace_sub_ips for new addresses or opting out. The old node must be an approved primary node with the same role; 409 ip_in_use when its pool IP is bound to IP sites and the IP changes. |
| `replace_sub_ips` | ReplaceSubIp[] |  | Per-sub-IP handling of the old node when replacing (only with replace_node_id); unlisted sub IPs move to the new node keeping their address. ip switches to a new address (422 conflict if another node uses it); skip=true leaves the sub IP and its lines on the old node, disabled. |
| `role` | string |  | Node role: edge or l2. Values: ["edge","l2"] |
| `service_expire` | string |  | Server service expiry date YYYY-MM-DD (used for the home-page expiry reminder; never auto-disables); null = not set, no reminder. Sub-IPs take the primary node's value. |
| `service_start` | string |  | Server service start date YYYY-MM-DD (display only); null = not set. Sub-IPs take the primary node's value. |
| `updated_at` | string |  | Updated at (read-only) |
