## Nodes (`nodes`)

Approved edge nodes and sub-IPs; primary nodes self-enroll with an enrollment token, while sub-IPs are added manually by an admin.

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

### GET `/api/v2/nodes`

List 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/nodes" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/nodes`

Create node · Authentication: Admin only

Request body:

| 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. |
| `des` | string |  | Description |
| `enabled` | boolean |  | Whether enabled |
| `ip` | string |  | Node IP address (IPv4 or IPv6). |
| `monitor` | NodeMonitor |  | Node monitor probe settings (the control plane probes this IP at the interval configured in monitor.node). |
| `name` | string | Yes | Name |
| `parent_id` | integer |  | Primary node that owns this sub-IP; null = primary node. |
| `region_id` | integer |  | Region ID |
| `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. |

```sh
curl -X POST "https://panel.example.com/api/v2/nodes" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ip":"192.0.2.20","name":"edge-1-ip2","parent_id":1}'
```

### GET `/api/v2/nodes/{id}`

Get node · Authentication: Admin only

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `capacity` | boolean |  | When true, include capacity (primary nodes only; null when no system info is available). |

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

### PUT `/api/v2/nodes/{id}`

Update node · Authentication: Admin only

Request body:

| 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. |
| `des` | string |  | Description |
| `enabled` | boolean |  | Whether enabled |
| `ip` | string |  | Node IP address (IPv4 or IPv6). |
| `monitor` | NodeMonitor |  | Node monitor probe settings (the control plane probes this IP at the interval configured in monitor.node). |
| `name` | string |  | Name |
| `parent_id` | integer |  | Primary node that owns this sub-IP; null = primary node. |
| `region_id` | integer |  | Region ID |
| `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. |

```sh
curl -X PUT "https://panel.example.com/api/v2/nodes/{id}" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"bandwidth_mbps":30}'
```

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

Delete node · Authentication: Admin only

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

### POST `/api/v2/nodes/{id}/config-readmit`

Re-push the config a node rolled back · Authentication: Admin only

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

### GET `/api/v2/nodes/{id}/local-addrs`

Node NIC addresses · Authentication: Admin only

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

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

Move node back to pending · Authentication: Admin only

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

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 enrolled; null for a manually added sub-IP. |
| `hints` | string[] |  | Hints (e.g. NIC addresses could not be obtained). |
| `hostname` | string |  | Node hostname, reported at enrollment. |
| `id` | integer |  | Record ID (read-only) |
| `ip` | string |  | Node IP address (IPv4 or IPv6). |
| `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 |
| `role` | string |  | Node role: edge for an edge node, l2 for a second-tier cache node. 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. |
| `status` | string |  | ok = result available; pending = command queued, fetch again later; unknown = node offline. Values: ["ok","pending","unknown"] |
| `updated_at` | string |  | Updated at (read-only) |
| `v4` | string[] |  | IPv4 addresses on the node's NICs. |
| `v6` | string[] |  | IPv6 addresses on the node's NICs. |
