## Tiered origin (L2) (`l2`)

Edges fetch through L2 nodes in their region first, so cached content no longer hits the origin. Paid feature l2; inactive when not licensed.

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

### POST `/api/v2/l2/enable-batch`

Add L2 nodes in batch · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `apply_sites` | boolean |  | Also enable on existing sites (only those never set; sites turned off manually, or whose tenant package does not include tiered origin, stay unchanged). |
| `node_ids` | integer[] | Yes | Edge node IDs to turn into L2. |

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

### GET `/api/v2/l2/groups`

List L2 groups · Authentication: Admin only

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

### POST `/api/v2/l2/groups`

Create L2 group · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `all_regions` | boolean |  | Serve all regions (requires cross_region). |
| `balance` | string |  | Distribution: url_hash pins by URL, rr round-robin. Values: ["url_hash","rr"] |
| `cross_region` | boolean |  | Allow cross-region use (required for nodes outside the regions or all regions; adds about 50-200 ms). |
| `name` | string | Yes | Group name, unique platform-wide. |
| `node_ids` | integer[] |  | Edge node IDs to turn into L2. |
| `regions` | integer[] |  | Served region IDs; ignored when all_regions is true. |
| `rules` | L2Rule[] |  | Group rules (saved with the group). |

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

### GET `/api/v2/l2/groups/{id}`

Get L2 group · Authentication: Admin only

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

### PATCH `/api/v2/l2/groups/{id}`

Update L2 group · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `all_regions` | boolean |  | Serve all regions (requires cross_region). |
| `balance` | string |  | Distribution: url_hash pins by URL, rr round-robin. Values: ["url_hash","rr"] |
| `cross_region` | boolean |  | Allow cross-region use (required for nodes outside the regions or all regions; adds about 50-200 ms). |
| `name` | string |  | Group name, unique platform-wide. |
| `node_ids` | integer[] |  | Edge node IDs to turn into L2. |
| `regions` | integer[] |  | Served region IDs; ignored when all_regions is true. |
| `rules` | L2Rule[] |  | Group rules (saved with the group). |

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

### DELETE `/api/v2/l2/groups/{id}`

Delete L2 group · Authentication: Admin only

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

### POST `/api/v2/l2/groups/{id}/copy`

Copy L2 group · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | Group name, unique platform-wide. |

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

### POST `/api/v2/l2/groups/{id}/reset-sites`

Reset sites of a group to region default · Authentication: Admin only

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

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

List L2 nodes · Authentication: Admin only

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

### PATCH `/api/v2/l2/nodes/{id}`

Update L2 node · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `access_addr` | string |  | Private address edges use to reach it (IP only, no port; IPv4-mapped IPv6 is stored as IPv4); empty means the public IP. |
| `role` | string |  | Primary or backup: backups stand by while a primary in the same group is up. Values: ["primary","backup"] |
| `weight` | integer |  | Weight 1-100; multiple primaries share URLs by weight. |

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

### POST `/api/v2/l2/nodes/{id}/disable`

Disable L2 node · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `reason` | string |  | Reason for disabling. |

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

### POST `/api/v2/l2/nodes/{id}/enable`

Enable L2 node · Authentication: Admin only

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

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

Impact before revert or disable · Authentication: Admin only

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

### POST `/api/v2/l2/nodes/{id}/mark-allowed`

Mark origins as allowing the node · Authentication: Admin only

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

### POST `/api/v2/l2/nodes/{id}/probe-access`

Check whether origins allow the node · Authentication: Admin only

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

### POST `/api/v2/l2/nodes/{id}/revert`

Revert to edge node · Authentication: Admin only

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

### POST `/api/v2/l2/pause`

Pause tiered origin network-wide · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `auto_resume_at` | string |  | Automatic resume time (must be in the future); omit for manual resume. |

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

### GET `/api/v2/l2/recommend`

One-click enable recommendation · Authentication: Admin only

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

### POST `/api/v2/l2/resume`

Resume tiered origin network-wide · Authentication: Admin only

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

### GET `/api/v2/l2/revoke-impact`

License loss impact and fallback window · 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 |

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

### GET `/api/v2/l2/stats`

Tiered origin statistics · Authentication: Admin only

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `start` | string | Yes | Start time (inclusive), RFC 3339 |
| `end` | string | Yes | End time (exclusive), RFC 3339 |

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

### GET `/api/v2/l2/stats/nodes`

Statistics by L2 node · Authentication: Admin only

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `start` | string | Yes | Start time (inclusive), RFC 3339 |
| `end` | string | Yes | End time (exclusive), RFC 3339 |

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

### GET `/api/v2/l2/stats/sites`

Tiered origin statistics by site · Authentication: Admin only

Query parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `start` | string | Yes | Start time (inclusive), RFC 3339 |
| `end` | string | Yes | End time (exclusive), RFC 3339 |
| `page` | integer |  | Page number, starting from 1 Default: 1 |
| `page_size` | integer |  | Items per page Default: 20 |
| `domain` | string |  | Substring of the primary domain, case-insensitive. |

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

### GET `/api/v2/l2/status`

Tiered origin overview · Authentication: Admin only

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

### GET `/api/v2/l2/tasks/{id}`

Get L2 task progress · Authentication: Admin only

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

Tiered origin (L2) object:

| Field | Type | Required | Description |
|---|---|---|---|
| `access_addr` | string |  | Private address edges use to reach it (IP only, no port; IPv4-mapped IPv6 is stored as IPv4); empty means the public IP. |
| `all_regions` | boolean |  | Serve all regions (requires cross_region). |
| `allow` | string |  | Origin allowlisting: empty if not applicable, pending (cacheable requests only), or allowed. |
| `allow_since` | string |  | Pending since; turns allowed after 24 hours. |
| `apply_sites` | boolean |  | Also enable on existing sites (only those never set; sites turned off manually, or whose tenant package does not include tiered origin, stay unchanged). |
| `auto_resume_at` | string |  | Automatic resume time (must be in the future); omit for manual resume. |
| `balance` | string |  | Distribution: url_hash pins by URL, rr round-robin. Values: ["url_hash","rr"] |
| `checked_at` | string |  | Checked at. |
| `created_at` | string |  | Started at. |
| `cross_region` | boolean |  | Allow cross-region use (required for nodes outside the regions or all regions; adds about 50-200 ms). |
| `defaults` | L2Defaults |  | Defaults (change via PUT /configs/l2.defaults). |
| `disabled_at` | string |  | Disabled at. |
| `disabled_reason` | string |  | Reason for disabling. |
| `edge_origin_requests` | integer |  | Edge origin requests (hit rate denominator). |
| `empty_group` | integer[] |  | Groups left without a serving L2 (their sites will bypass temporarily). |
| `enabled` | boolean |  | Enabled; a disabled node stays L2 and keeps its cache. |
| `feature` | string |  | Paid feature l2 level: on; limited when frozen, being revoked, or in the fallback window after the license lapses (still pushed, management only; see GET /l2/revoke-impact); off when not licensed (not pushed). Values: ["on","limited","off"] |
| `finished_at` | string |  | Finished at. |
| `groups` | integer[] |  | IDs of L2 groups containing it. |
| `hit_rate` | number |  | Hit rate 0-1; 0 when there were no origin requests. |
| `host` | string |  | Primary domain of the site. |
| `id` | integer |  | Group ID. |
| `ip` | string |  | Public IP. |
| `is_region_default` | boolean |  | Region default group (one per region; cannot be deleted, only its distribution can change). |
| `items` | L2Group[] |  | L2 groups. |
| `kind` | string |  | Task kind: enable adds L2, revert returns to edge. Values: ["enable","revert"] |
| `l2_group_id` | integer |  | L2 group chosen by the site; null means region default. |
| `l2_group_name` | string |  | L2 group name; empty for region default. |
| `l2_hit_requests` | integer |  | Origin requests served by L2 (hit rate numerator). |
| `l2_pick` | string |  | Effective L2 pick method (site value, else the default): region or near. Values: ["region","near"] |
| `low_hit` | boolean |  | Hit rate below 30% with at least 1000 origin requests (mostly dynamic content; consider turning L2 off). |
| `manual_off_sites` | integer |  | Sites whose tiered origin was turned off manually (left alone when enabling on existing sites; IP sites excluded). |
| `name` | string | Yes | Group name, unique platform-wide. |
| `node_id` | integer |  | Node ID. |
| `node_ids` | integer[] | Yes | Edge node IDs to turn into L2. |
| `nodes_pending` | integer |  | L2 nodes awaiting origin allowlisting. |
| `nodes_serving` | integer |  | L2 nodes serving (enabled and not reverting). |
| `nodes_total` | integer |  | Total L2 nodes (including disabled and reverting). |
| `online` | boolean |  | Online (heartbeat within 2 minutes). |
| `only_block` | boolean |  | Some sites allow only L2 at the origin (revoke its allowlisting after revert). |
| `only_block_sites` | integer |  | Sites with origin allowing only L2 (greater than 0 when only_block is true). |
| `origin_l2_only` | boolean |  | Origin allows only L2; after the window ends the site goes down unless the origin allows edge nodes. |
| `origin_requests` | integer |  | Requests that reached the origin. |
| `origin_requests_est` | integer |  | Estimate without tiering (= edge origin requests). |
| `owner_id` | integer |  | Owner user ID. |
| `owner_name` | string |  | Owner username. |
| `paused` | boolean |  | Paused network-wide. |
| `paused_at` | string |  | Paused at. |
| `progress` | integer |  | Progress 0-100. |
| `reason` | string |  | Reason for disabling. |
| `region_id` | integer |  | Region of a region default group; 0 for custom groups. |
| `region_last` | boolean |  | It is the last enabled L2 in its region. |
| `regions` | integer[] |  | Served region IDs; ignored when all_regions is true. |
| `revert_at` | string |  | Revert started at. |
| `role` | string |  | Primary or backup: backups stand by while a primary in the same group is up. Values: ["primary","backup"] |
| `rules` | L2Rule[] |  | Group rules (saved with the group). |
| `saved_bytes` | integer |  | Origin traffic saved (bytes served from L2 cache). |
| `site_id` | integer |  | Site ID. |
| `sites` | integer |  | Number of sites using this group; for a region default group, the sites with L2 enabled and no group chosen (same number for every region default group). |
| `sites_only_l2` | integer |  | Sites with origin allowing only L2 (shown in the pause and add-node warnings). |
| `state` | string |  | State: active, or reverting to edge. Values: ["active","reverting"] |
| `thresholds` | L2Thresholds |  | Detection thresholds (read-only). |
| `updated_at` | string |  | Last modified time. |
| `weight` | integer |  | Weight 1-100; multiple primaries share URLs by weight. |
