Skip to main content

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
Special Endpoint

/ProxyAccount/GetCurrentUser does not require Basic Auth. It identifies users via the X-Socks5-Username HTTP header.

Security Mechanisms

MechanismDescription
IP WhitelistWhen configured, only whitelisted IPs can access; supports wildcards (e.g., 192.168.1.*)
Basic AuthAll endpoints except GetCurrentUser require admin authentication
Password HashingBoth admin and account passwords are stored as SHA256 hashes
Brute Force ProtectionServer delays response on auth failure (random 500-1500ms)
Private Network OnlyGetCurrentUser 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:

FieldTypeDescription
AIDGuidAccount unique identifier (CCProxy compatible)
UsernamestringUsername
IsEnabledboolWhether the account is enabled
Remarksstring?Notes
CreatedAtDateTimeCreation time
LastLoginAtDateTime?Last login time (null if never logged in)
ExpiresAtDateTime?Expiration time (null = never expires)
DataCountintHeartbeat data entries in the data pool for this account
LatestGameIdstring?Most recently collected GameID for this account

Error Responses:

StatusDescription
401 UnauthorizedAuthentication failed
403 ForbiddenIP not in whitelist
500 Internal Server ErrorInternal 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:

ParameterTypeRequiredDescription
AIDGuidYesAccount 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:

StatusDescription
400 Bad RequestInvalid account ID format
404 Not FoundAccount 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:

FieldTypeRequiredDefaultDescription
UsernamestringYesUsername (must be unique)
PasswordstringYesPassword (plaintext, stored as SHA256 hash on server)
IsEnabledboolNotrueWhether the account is enabled
Remarksstring?NonullNotes
ExpiresAtDateTime?NonullExpiration time (null = never expires)

Success Response: 200 OK

"添加账号成功"

Error Responses:

StatusDescription
400 Bad RequestUsername/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

Note

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:

FieldTypeRequiredDescription
AIDGuidYesAccount ID to update
PasswordstringNoNew password (empty/omitted = no change)
IsEnabledboolNoEnabled status
Remarksstring?NoNotes
ExpiresAtDateTime?NoExpiration time

Success Response: 200 OK

"更新账号成功"

Error Responses:

StatusDescription
400 Bad RequestAID empty, invalid ID format, or account not found

Business Rules:

  • Password is not modified when empty or whitespace-only
  • IsEnabled, Remarks, ExpiresAt are 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

Note

This endpoint uses POST instead of DELETE for CCProxy API compatibility.

Query Parameters:

ParameterTypeRequiredDescription
AIDGuidYesAccount ID to delete

Request Example:

POST /ProxyAccount/DeleteProxyAccount?AID=00000000-0000-0000-0000-000000000001

Success Response: 200 OK

"删除账号成功"

Error Responses:

StatusDescription
400 Bad RequestInvalid ID format or account not found
Cascade Delete

Deleting an account also deletes all associated heartbeat data from both memory and database. This operation is irreversible.

Cascade Delete Flow:

  1. Delete all heartbeat data associated with the username from database
  2. Remove all data for the username from memory data pool
  3. 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:

HeaderRequiredDescription
X-Socks5-UsernameYesSOCKS5 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:

FieldTypeDescription
UsernamestringUsername
IsEnabledboolWhether the account is enabled
CreatedAtDateTimeCreation time
LastLoginAtDateTime?Last login time
ExpiresAtDateTime?Expiration time (null = never expires)
IsExpiredboolWhether the account is currently expired
DataCountintHeartbeat data entries in the data pool
LatestGameIdstring?Most recently collected GameID

Error Responses:

StatusDescription
401 UnauthorizedMissing X-Socks5-Username header
403 ForbiddenSource IP is not local or private network
404 Not FoundUser 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

MethodPathAuthDescription
GET/ProxyAccount/GetProxyAccountListBasic AuthList all accounts (with data pool stats)
GET/ProxyAccount/GetProxyAccountByID?AID={guid}Basic AuthGet single account details
POST/ProxyAccount/AddProxyAccountBasic AuthCreate new account
POST/ProxyAccount/UpdateProxyAccountBasic AuthUpdate account info
POST/ProxyAccount/DeleteProxyAccount?AID={guid}Basic AuthDelete account (cascade)
GET/ProxyAccount/GetCurrentUserHeaderGet 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

StatusDescriptionCommon Causes
200 OKOperation succeeded
400 Bad RequestRequest parameter errorMissing required fields, invalid ID format, duplicate username
401 UnauthorizedAuthentication failedNo credentials, wrong username/password
403 ForbiddenAccess deniedIP not in whitelist, external access to restricted endpoint
404 Not FoundResource not foundAccount ID doesn't exist, username doesn't exist
500 Internal Server ErrorServer errorDatabase 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)