Remote Management API Reference
HeartbeatSocks includes a built-in REST API service for remote SOCKS5 account management and user information queries. The API is compatible with the CCProxy ProxyAccount_Controller interface specification.
Overview
┌─────────────────────────────────────────────────────────────────┐
│ Remote Management API Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Client Request ──▶ IP Whitelist ──▶ Basic Auth ──▶ Route │
│ │ │ │ │
│ Deny 403 Deny 401 Endpoint │
│ │ │
│ ┌───────────────────────────────────────────────────┤ │
│ │ │ │
│ │ /ProxyAccount │ │
│ │ ├── GET /GetProxyAccountList List all accounts │ │
│ │ ├── GET /GetProxyAccountByID Get account by ID │ │
│ │ ├── POST /AddProxyAccount Create account │ │
│ │ ├── POST /UpdateProxyAccount Update account │ │
│ │ ├── POST /DeleteProxyAccount Delete (cascade) │ │
│ │ └── GET /GetCurrentUser Current user │ │
│ │ (no Basic Auth) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Stack: ASP.NET Core Minimal API + Kestrel │
│ Format: JSON │
│ Auth: HTTP Basic Authentication │
│ │
└─────────────────────────────────────────────────────────────────┘
Basic Information
Base URL
http://{BindIP}:{Port}
Default: http://localhost:8080
Authentication
The API uses HTTP Basic Authentication. Include Base64-encoded admin credentials in the Authorization header.
Authorization: Basic {base64(username:password)}
Example (username admin, password 123456):
Authorization: Basic YWRtaW46MTIzNDU2
/ProxyAccount/GetCurrentUser does not require Basic Auth. It identifies users via the X-Socks5-Username HTTP header.
Security Mechanisms
| Mechanism | Description |
|---|---|
| IP Whitelist | When configured, only whitelisted IPs can access; supports wildcards (e.g., 192.168.1.*) |
| Basic Auth | All endpoints except GetCurrentUser require admin authentication |
| Password Hashing | Both admin and account passwords are stored as SHA256 hashes |
| Brute Force Protection | Server delays response on auth failure (random 500-1500ms) |
| Private Network Only | GetCurrentUser only allows local/private network IPs |
Request/Response Format
- Content-Type:
application/json - Encoding: UTF-8
- Date Format: ISO 8601 (e.g.,
2025-01-01T00:00:00) - ID Format: Guid (CCProxy compatible, internally converted to/from int via
GuidHelper)
Endpoint Details
1. List All Accounts
Retrieves all SOCKS5 accounts with their data pool statistics.
GET /ProxyAccount/GetProxyAccountList
Auth: Basic Auth required
Parameters: None
Response: 200 OK
[
{
"AID": "00000000-0000-0000-0000-000000000001",
"Username": "player1",
"IsEnabled": true,
"Remarks": "Test account",
"CreatedAt": "2025-01-15T08:30:00",
"LastLoginAt": "2025-06-01T14:22:33",
"ExpiresAt": null,
"DataCount": 152,
"LatestGameId": "A1B2C3D4E5"
},
{
"AID": "00000000-0000-0000-0000-000000000002",
"Username": "player2",
"IsEnabled": false,
"Remarks": "Disabled",
"CreatedAt": "2025-02-20T10:00:00",
"LastLoginAt": "2025-05-15T09:10:00",
"ExpiresAt": "2025-12-31T23:59:59",
"DataCount": 0,
"LatestGameId": null
}
]
Response Fields:
| Field | Type | Description |
|---|---|---|
AID | Guid | Account unique identifier (CCProxy compatible) |
Username | string | Username |
IsEnabled | bool | Whether the account is enabled |
Remarks | string? | Notes |
CreatedAt | DateTime | Creation time |
LastLoginAt | DateTime? | Last login time (null if never logged in) |
ExpiresAt | DateTime? | Expiration time (null = never expires) |
DataCount | int | Heartbeat data entries in the data pool for this account |
LatestGameId | string? | Most recently collected GameID for this account |
Error Responses:
| Status | Description |
|---|---|
401 Unauthorized | Authentication failed |
403 Forbidden | IP not in whitelist |
500 Internal Server Error | Internal server error |
2. Get Account by ID
Retrieves a single account's details and data pool statistics by ID.
GET /ProxyAccount/GetProxyAccountByID?AID={guid}
Auth: Basic Auth required
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
AID | Guid | Yes | Account unique identifier |
Request Example:
GET /ProxyAccount/GetProxyAccountByID?AID=00000000-0000-0000-0000-000000000001
Response: 200 OK
{
"AID": "00000000-0000-0000-0000-000000000001",
"Username": "player1",
"IsEnabled": true,
"Remarks": "Test account",
"CreatedAt": "2025-01-15T08:30:00",
"LastLoginAt": "2025-06-01T14:22:33",
"ExpiresAt": null,
"DataCount": 152,
"LatestGameId": "A1B2C3D4E5"
}
Error Responses:
| Status | Description |
|---|---|
400 Bad Request | Invalid account ID format |
404 Not Found | Account does not exist |
3. Add Account
Creates a new SOCKS5 proxy account.
POST /ProxyAccount/AddProxyAccount
Auth: Basic Auth required
Request Body:
{
"Username": "new_player",
"Password": "my_secure_password",
"IsEnabled": true,
"Remarks": "New user",
"ExpiresAt": "2026-12-31T23:59:59"
}
Request Fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
Username | string | Yes | — | Username (must be unique) |
Password | string | Yes | — | Password (plaintext, stored as SHA256 hash on server) |
IsEnabled | bool | No | true | Whether the account is enabled |
Remarks | string? | No | null | Notes |
ExpiresAt | DateTime? | No | null | Expiration time (null = never expires) |
Success Response: 200 OK
"添加账号成功"
Error Responses:
| Status | Description |
|---|---|
400 Bad Request | Username/password empty, or username already exists |
Business Rules:
- Username cannot be empty and must be unique
- Password cannot be empty
- Password is automatically SHA256-hashed before storage
- CreatedAt is automatically set to current UTC time
4. Update Account
Modifies an existing account's information.
POST /ProxyAccount/UpdateProxyAccount
Auth: Basic Auth required
This endpoint uses POST instead of PUT for CCProxy API compatibility.
Request Body:
{
"AID": "00000000-0000-0000-0000-000000000001",
"Password": "new_password",
"IsEnabled": true,
"Remarks": "Updated notes",
"ExpiresAt": null
}
Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
AID | Guid | Yes | Account ID to update |
Password | string | No | New password (empty/omitted = no change) |
IsEnabled | bool | No | Enabled status |
Remarks | string? | No | Notes |
ExpiresAt | DateTime? | No | Expiration time |
Success Response: 200 OK
"更新账号成功"
Error Responses:
| Status | Description |
|---|---|
400 Bad Request | AID empty, invalid ID format, or account not found |
Business Rules:
Passwordis not modified when empty or whitespace-onlyIsEnabled,Remarks,ExpiresAtare always updated (even if not provided, defaults apply)
5. Delete Account
Deletes the specified account and all associated data.
POST /ProxyAccount/DeleteProxyAccount?AID={guid}
Auth: Basic Auth required
This endpoint uses POST instead of DELETE for CCProxy API compatibility.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
AID | Guid | Yes | Account ID to delete |
Request Example:
POST /ProxyAccount/DeleteProxyAccount?AID=00000000-0000-0000-0000-000000000001
Success Response: 200 OK
"删除账号成功"
Error Responses:
| Status | Description |
|---|---|
400 Bad Request | Invalid ID format or account not found |
Deleting an account also deletes all associated heartbeat data from both memory and database. This operation is irreversible.
Cascade Delete Flow:
- Delete all heartbeat data associated with the username from database
- Remove all data for the username from memory data pool
- Delete the account itself
6. Get Current User
Retrieves the current SOCKS5 authenticated user's information via HTTP header. Designed for client applications to query their own account status.
GET /ProxyAccount/GetCurrentUser
Auth: No Basic Auth required (identifies user via HTTP header)
Request Headers:
| Header | Required | Description |
|---|---|---|
X-Socks5-Username | Yes | SOCKS5 authenticated username |
Request Example:
GET /ProxyAccount/GetCurrentUser
X-Socks5-Username: player1
Success Response: 200 OK
{
"Username": "player1",
"IsEnabled": true,
"CreatedAt": "2025-01-15T08:30:00",
"LastLoginAt": "2025-06-01T14:22:33",
"ExpiresAt": null,
"IsExpired": false,
"DataCount": 152,
"LatestGameId": "A1B2C3D4E5"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
Username | string | Username |
IsEnabled | bool | Whether the account is enabled |
CreatedAt | DateTime | Creation time |
LastLoginAt | DateTime? | Last login time |
ExpiresAt | DateTime? | Expiration time (null = never expires) |
IsExpired | bool | Whether the account is currently expired |
DataCount | int | Heartbeat data entries in the data pool |
LatestGameId | string? | Most recently collected GameID |
Error Responses:
| Status | Description |
|---|---|
401 Unauthorized | Missing X-Socks5-Username header |
403 Forbidden | Source IP is not local or private network |
404 Not Found | User does not exist |
Security Restrictions:
- Only the following IP ranges are allowed:
127.0.0.0/8(loopback)10.0.0.0/8(Class A private)172.16.0.0/12(Class B private)192.168.0.0/16(Class C private)fc00::/7(IPv6 Unique Local Address)fe80::/10(IPv6 Link-Local)
Endpoint Summary
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /ProxyAccount/GetProxyAccountList | Basic Auth | List all accounts (with data pool stats) |
GET | /ProxyAccount/GetProxyAccountByID?AID={guid} | Basic Auth | Get single account details |
POST | /ProxyAccount/AddProxyAccount | Basic Auth | Create new account |
POST | /ProxyAccount/UpdateProxyAccount | Basic Auth | Update account info |
POST | /ProxyAccount/DeleteProxyAccount?AID={guid} | Basic Auth | Delete account (cascade) |
GET | /ProxyAccount/GetCurrentUser | Header | Get current SOCKS5 user info |
Usage Examples
cURL
List all accounts:
curl -u admin:password123 \
http://localhost:8080/ProxyAccount/GetProxyAccountList
Add account:
curl -u admin:password123 \
-X POST \
-H "Content-Type: application/json" \
-d '{"Username":"player1","Password":"pass123","IsEnabled":true,"Remarks":"New user"}' \
http://localhost:8080/ProxyAccount/AddProxyAccount
Update account:
curl -u admin:password123 \
-X POST \
-H "Content-Type: application/json" \
-d '{"AID":"00000000-0000-0000-0000-000000000001","IsEnabled":false,"Remarks":"Disabled"}' \
http://localhost:8080/ProxyAccount/UpdateProxyAccount
Delete account:
curl -u admin:password123 \
-X POST \
"http://localhost:8080/ProxyAccount/DeleteProxyAccount?AID=00000000-0000-0000-0000-000000000001"
Get current user (local/private network only):
curl -H "X-Socks5-Username: player1" \
http://localhost:8080/ProxyAccount/GetCurrentUser
PowerShell
# Set up authentication
$cred = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("admin:password123"))
$headers = @{ Authorization = "Basic $cred" }
# List all accounts
Invoke-RestMethod -Uri "http://localhost:8080/ProxyAccount/GetProxyAccountList" `
-Headers $headers
# Add account
$body = @{
Username = "player1"
Password = "pass123"
IsEnabled = $true
Remarks = "New user"
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://localhost:8080/ProxyAccount/AddProxyAccount" `
-Method Post -Headers $headers -ContentType "application/json" -Body $body
C# HttpClient
using var client = new HttpClient();
// Set up Basic Auth
var credentials = Convert.ToBase64String(
Encoding.UTF8.GetBytes("admin:password123"));
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", credentials);
// List all accounts
var response = await client.GetAsync(
"http://localhost:8080/ProxyAccount/GetProxyAccountList");
var accounts = await response.Content
.ReadFromJsonAsync<List<AccountResponse>>();
// Add account
var request = new
{
Username = "player1",
Password = "pass123",
IsEnabled = true,
Remarks = "New user"
};
await client.PostAsJsonAsync(
"http://localhost:8080/ProxyAccount/AddProxyAccount", request);
Error Handling
Common HTTP Status Codes
| Status | Description | Common Causes |
|---|---|---|
200 OK | Operation succeeded | — |
400 Bad Request | Request parameter error | Missing required fields, invalid ID format, duplicate username |
401 Unauthorized | Authentication failed | No credentials, wrong username/password |
403 Forbidden | Access denied | IP not in whitelist, external access to restricted endpoint |
404 Not Found | Resource not found | Account ID doesn't exist, username doesn't exist |
500 Internal Server Error | Server error | Database exception, internal processing failure |
Error Response Format
Error messages are returned as plain strings (CCProxy compatible):
"用户名和密码不能为空"
"该用户名已存在"
IP Whitelist Configuration
Configure allowed API access IPs in the IP whitelist field on the "Settings" page.
Format
Multiple IPs separated by commas:
192.168.1.100, 192.168.1.101, 10.0.0.50
Wildcard Support
192.168.1.* # Matches 192.168.1.0/24 subnet
10.* # Matches 10.0.0.0/8 subnet
* # Allow all IPs (same as empty whitelist)
Notes
- Empty whitelist allows all IPs
- Supports both IPv4 and IPv6 addresses
- IPv4-mapped IPv6 addresses (e.g.,
::ffff:192.168.1.100) are automatically converted to IPv4 for matching - Changes take effect on next request without restarting the API service (uses internal cache for performance)