## Users (`users`)

Admins manage platform accounts: create, view, update, enable/disable, reset passwords, turn off TOTP. An account must be disabled before it can be deleted.

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

### GET `/api/v2/users`

List users · 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` |
| `ids` | string |  | Only list these IDs: comma-separated positive integers, at most 200 after deduplication (same as the page_size limit; more returns 400); an empty value is the same as omitting it. Combined with other filters as an intersection; IDs you cannot see are silently left out. Pagination is unchanged: set page_size to at least the number of IDs to get them in one page |
| `email` | string |  | Email |
| `phone` | string |  | Phone number |
| `username` | string |  | Username |

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

### POST `/api/v2/users`

Create user · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | string |  | Email |
| `enabled` | boolean |  | Whether enabled |
| `login_ip_allow` | string[] |  | Login IP allowlist (IPs or CIDRs); empty means no restriction |
| `login_verify` | LoginVerify |  | Second-factor login: none means not required; email / sms also require an email/SMS code at login (the platform must allow it and the address must be bound) Values: ["none","email","sms"] |
| `password` | string |  | Required on create; on update, providing it resets the password and revokes all sessions of the user |
| `phone` | string |  | Phone number |
| `role` | string |  | Account role: admin or tenant Values: ["admin","tenant"] |
| `totp_enabled` | boolean |  | Whether TOTP is enabled |
| `username` | string | Yes | Username |

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

### POST `/api/v2/users/batch`

Batch enable, disable or delete users · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | string | Yes | enable, disable or delete. Values: ["enable","disable","delete"] |
| `ids` | integer[] | Yes | User IDs to operate on, 1 to 100 per call. |

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

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

Get user · Authentication: Admin only

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

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

Update user · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | string |  | Email |
| `enabled` | boolean |  | Whether enabled |
| `login_ip_allow` | string[] |  | Login IP allowlist (IPs or CIDRs); empty means no restriction |
| `login_verify` | LoginVerify |  | Second-factor login: none means not required; email / sms also require an email/SMS code at login (the platform must allow it and the address must be bound) Values: ["none","email","sms"] |
| `password` | string |  | Required on create; on update, providing it resets the password and revokes all sessions of the user |
| `phone` | string |  | Phone number |
| `role` | string |  | Account role: admin or tenant Values: ["admin","tenant"] |
| `totp_enabled` | boolean |  | Whether TOTP is enabled |
| `username` | string | Yes | Username |

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

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

Delete user · Authentication: Admin only

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

### GET `/api/v2/users/{user_id}/account`

Get user account · Authentication: Admin only

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

### PUT `/api/v2/users/{user_id}/account`

Freeze or unfreeze user account · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `freeze_reason` | string |  | Freeze reason; empty if not frozen. |
| `frozen` | boolean | Yes | Whether the account is frozen; deductions are rejected while frozen. |

```sh
curl -X PUT "https://panel.example.com/api/v2/users/{user_id}/account" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"freeze_reason":"风控核查","frozen":true}'
```

### GET `/api/v2/users/{user_id}/api-keys`

List a user's API keys · 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/users/{user_id}/api-keys" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### POST `/api/v2/users/{user_id}/api-keys`

Create an API key for a user · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `allow_ips` | ApiKeyAllowIps |  | Caller IP allowlist (single IPs only, at most 200); empty means no restriction. Calls from other IPs get 401 as invalid credentials |
| `expires_at` | string |  | Expires at |
| `name` | string | Yes | Name |
| `scopes` | string[] | Yes | Granted scopes, same values as scopes at creation |

```sh
curl -X POST "https://panel.example.com/api/v2/users/{user_id}/api-keys" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### PUT `/api/v2/users/{user_id}/api-keys/{id}`

Update a user's API key IP allowlist or reset it · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `allow_ips` | ApiKeyAllowIps |  | Caller IP allowlist (single IPs only, at most 200); empty means no restriction. Calls from other IPs get 401 as invalid credentials |
| `reset` | boolean |  | When true, issue a new key; the old one stops working at once |

```sh
curl -X PUT "https://panel.example.com/api/v2/users/{user_id}/api-keys/{id}" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reset":true}'
```

### DELETE `/api/v2/users/{user_id}/api-keys/{id}`

Delete a user's API key · Authentication: Admin only

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

### POST `/api/v2/users/{user_id}/recharges`

Recharge or deduct user balance · Authentication: Admin only

Request body:

| Field | Type | Required | Description |
|---|---|---|---|
| `amount_cents` | integer | Yes | Amount in cents; must be a positive integer. |
| `direction` | string |  | credit = recharge (default), debit = deduct. Values: ["credit","debit"] Default: "credit" |
| `remark` | string |  | Remark |

```sh
curl -X POST "https://panel.example.com/api/v2/users/{user_id}/recharges" \
  -H "X-API-Key: $GOCDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount_cents":10000,"direction":"credit","remark":"线下转账"}'
```

Users object:

| Field | Type | Required | Description |
|---|---|---|---|
| `action` | string | Yes | enable, disable or delete. Values: ["enable","disable","delete"] |
| `affected` | integer |  | Number of users processed. |
| `allow_ips` | ApiKeyAllowIps |  | Caller IP allowlist (single IPs only, at most 200); empty means no restriction. Calls from other IPs get 401 as invalid credentials |
| `amount_cents` | integer | Yes | Amount in cents; must be a positive integer. |
| `archived` | boolean |  | Moved into the archive table by the archiving task (read-only). |
| `balance_cents` | integer |  | Account balance in cents, same as balance_cents of GET /users/{user_id}/account (0 when there is no account). Only present in the GET /users list, not in single-user responses (read-only) |
| `certified` | boolean |  | Whether real-name verification has passed |
| `client_ip` | string |  | Source IP of this request, derived the same way as the login IP allowlist check (what the allowlist compares is what is shown). Only present in GET /me and PUT /me (read-only) |
| `created_at` | string |  | Created at (read-only) |
| `data` | object |  | Extra data (such as package purchase details). |
| `delegated_session` | boolean |  | Whether the current session was issued by superadmin sign-in or switch-to-user (if so, password, email, phone, login allowlist, login verification and real-name are read-only). Only present in GET /me and PUT /me (read-only) |
| `des` | string |  | Description |
| `direction` | string |  | credit = recharge (default), debit = deduct. Values: ["credit","debit"] Default: "credit" |
| `email` | string |  | Email |
| `enabled` | boolean |  | Whether enabled |
| `expires_at` | string |  | Expires at |
| `freeze_reason` | string |  | Freeze reason; empty if not frozen. |
| `frozen` | boolean |  | Whether the account is frozen; deductions are rejected while frozen. |
| `id` | integer |  | Record ID (read-only) |
| `ids` | integer[] | Yes | User IDs to operate on, 1 to 100 per call. |
| `last_used_at` | string |  | Time of last use; null if never used |
| `ledger_id` | integer |  | ID of the linked ledger entry; null if none. |
| `login_ip_allow` | string[] |  | Login IP allowlist (IPs or CIDRs); empty means no restriction |
| `login_verify` | LoginVerify |  | Second-factor login: none means not required; email / sms also require an email/SMS code at login (the platform must allow it and the address must be bound) Values: ["none","email","sms"] |
| `mch_order_no` | string |  | Merchant order number (the gateway-side order number); null for non-online payments. |
| `name` | string |  | Name |
| `paid_at` | string |  | Payment time; null if unpaid. |
| `password` | string |  | Required on create; on update, providing it resets the password and revokes all sessions of the user |
| `pay_type` | string |  | admin/balance/alipay/wxpay, etc.; empty = unpaid. |
| `phone` | string |  | Phone number |
| `prefix` | string |  | First few characters of the plaintext key, for recognition |
| `remark` | string |  | Remark |
| `reset` | boolean |  | When true, issue a new key; the old one stops working at once |
| `role` | string |  | Account role: admin or tenant Values: ["admin","tenant"] |
| `scopes` | string[] |  | Granted scopes, same values as scopes at creation |
| `secret` | string |  | Plaintext key (the X-API-Key value), returned only once at creation; only a hash is stored |
| `status` | string |  | Order status: unpaid or paid. Values: ["unpaid","paid"] |
| `tenant_id` | integer |  | Owner tenant (user) ID (read-only) |
| `totp_enabled` | boolean |  | Whether TOTP is enabled |
| `totp_recovery_left` | integer |  | Number of unused TOTP recovery codes. Only present in GET /me and PUT /me when TOTP is enabled (read-only) |
| `transaction_id` | string |  | Gateway-side transaction ID; empty if unpaid. |
| `type` | string |  | recharge (admin recharge) / deduct (admin deduction) / payment (online payment) / package (package purchase), etc. |
| `updated_at` | string |  | Updated at (read-only) |
| `user_id` | integer |  | User ID |
| `username` | string |  | Username |
