Admin Portal API Docs

PasarGuard-compatible and portal-native API reference.

Base URLs

https://admin.vsl247.com
https://admin2.vsl247.com

Authorization

Protected endpoints authenticate a portal account before checking its role.

  • Recommended: create an access token at POST /api/admin/token, then send Authorization: Bearer ACCESS_TOKEN.
  • Compatibility: /api/v1 routes also accept HTTP Basic authentication with the portal username and password.
  • The API token is valid for up to 30 days. Generate a new token after it expires.
  • The browser login session and API authentication are separate; do not send the browser session cookie as an API credential.

Quick start

1. Create a token. 2. Copy access_token. 3. Send it in the Authorization header.

curl -X POST 'https://admin.vsl247.com/api/admin/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=admin_username&password=admin_password'

curl -H 'Authorization: Bearer ACCESS_TOKEN' \
  https://admin.vsl247.com/api/users

Use a superadmin account for superadmin-only endpoints. A normal admin token is authenticated successfully but receives 403 Forbidden when its role is not allowed.

Authentication

Create a token and inspect the authenticated portal admin.

POST /api/admin/token Create a PasarGuard-style access token.

Authenticate with the portal username and password. The endpoint accepts either form data or a JSON object.

Form body
POST /api/admin/token
Content-Type: application/x-www-form-urlencoded

username=admin_username&password=admin_password
JSON body
POST /api/admin/token
Content-Type: application/json

{
  "username": "admin_username",
  "password": "admin_password"
}
Response 200
{
  "access_token": "...",
  "token_type": "bearer"
}
Use the token
curl -H 'Authorization: Bearer ACCESS_TOKEN' \
  'https://admin.vsl247.com/api/v1/me'
  • Use the exact token returned in access_token; do not include quotation marks around it.
  • Invalid credentials return 401 Unauthorized.
  • An inactive normal admin cannot receive an API token and returns 403 Forbidden.
GET /api/admin Return the authenticated admin.
Response 200
{
  "username": "admin_username",
  "is_sudo": false,
  "telegram_id": null,
  "discord_webhook": null,
  "users_usage": 0
}
HTTP Error responses Common authentication, authorization, and validation responses.
  • 400 Bad Request — the request is authenticated but a body, query parameter, or value is invalid.
  • 401 Unauthorized — credentials are missing, invalid, or the Bearer token has expired. The response includes a WWW-Authenticate header.
  • 403 Forbidden — the credentials identify a valid account, but that account's role is not allowed to use the endpoint.
  • Errors use the JSON shape {"ok": false, "error": "..."} on portal-native /api/v1 routes.

Users

Manage only the clients owned by the authenticated portal admin.

GET /api/users List owned clients with filters and pagination.
Example request
GET /api/users?offset=0&limit=10&username=client&status=active
Response 200
{
  "users": [],
  "total": 0
}
POST /api/user Create a user using the admin's remaining quota.
Request body
{
  "username": "client_username",
  "data_limit": 21474836480,
  "duration_days": 30,
  "status": "active",
  "note": "optional"
}
Response 201
{
  "username": "client_username",
  "status": "active",
  "used_traffic": 0,
  "data_limit": 21474836480,
  "subscription_url": "https://sub.vsl247.com/sub/..."
}
GET /api/user/<username> Return one owned client.
Response 200
{
  "username": "client_username",
  "status": "active",
  "used_traffic": 1073741824,
  "data_limit": 21474836480,
  "expire": 1780000000,
  "subscription_url": "https://sub.vsl247.com/sub/..."
}
PUT /api/user/<username> Increase quota or update expiry and status.
Request body
{
  "data_limit": 32212254720,
  "expire": 1780000000,
  "status": "active"
}
Response 200
{
  "username": "client_username",
  "status": "active",
  "data_limit": 32212254720,
  "expire": 1780000000
}
DELETE /api/user/<username> Delete an owned client through the panel API.
Response 200
{
  "detail": "User successfully deleted"
}
POST /api/user/<username>/reset Reset the client's used traffic.
Request
POST /api/user/client_username/reset
Authorization: Bearer ACCESS_TOKEN
Response 200
{
  "username": "client_username",
  "status": "active",
  "used_traffic": 0,
  "data_limit": 21474836480
}
POST /api/user/<username>/revoke_sub Revoke and regenerate the subscription link.
Request
POST /api/user/client_username/revoke_sub
Authorization: Bearer ACCESS_TOKEN
Response 200
{
  "username": "client_username",
  "status": "active",
  "subscription_url": "https://sub.vsl247.com/sub/NEW_TOKEN"
}

Activity

Inspect subscription fetches and traffic usage.

GET /api/user/<username>/sub_update Return subscription fetch history.
Example request
GET /api/user/client_username/sub_update?offset=0&limit=10
Response 200
{
  "updates": [
    {
      "created_at": "2026-05-29T10:25:55Z",
      "user_agent": "v2rayNG/1.8.5"
    }
  ]
}
GET /api/user/<username>/usage Return usage records for one owned client.
Example request
GET /api/user/client_username/usage?start=2026-06-01T00:00:00Z&end=2026-06-06T00:00:00Z
Response 200
{
  "username": "client_username",
  "usages": [
    {
      "node_id": 6,
      "used_traffic": 268435456,
      "created_at": "2026-06-05T12:00:00Z"
    }
  ]
}
GET /api/users/usage Return usage records for all owned clients.
Example request
GET /api/users/usage?start=2026-06-01T00:00:00Z&end=2026-06-06T00:00:00Z
Response 200
{
  "usages": [
    {
      "username": "client_username",
      "node_id": 6,
      "used_traffic": 268435456,
      "created_at": "2026-06-05T12:00:00Z"
    }
  ]
}

Superadmin

Reporting and admin-management endpoints restricted to authenticated superadmins.

GET /api/v1/superadmin/sales Return each admin's GB sales for a time range.
What this endpoint returns Gross GB sold by every portal admin within the requested time window. This is read-only reporting: it does not modify users, quotas, or audit records.
Authentication Bearer or Basic auth Send credentials with every request.
Required role superadmin Normal admin accounts are denied.
Success 200 OK Returns totals and one row per admin.
Access errors 401 403 Invalid credentials or insufficient role.

Query parameters

NameTypeRequiredDescriptionExample
start string or integer Required Beginning of the range, inclusive. Accepts an ISO 8601 date/time or a Unix timestamp in seconds or milliseconds. 2026-08-01T00:00:00+03:30
end string or integer Required End of the range, exclusive. It must be later than start. 2026-08-02T00:00:00+03:30
Time range rules ISO values without a timezone are interpreted as Asia/Tehran. For predictable integrations, send an explicit offset or use UTC with Z. The interval includes start and excludes end.

Request examples

Bearer tokenRecommended
curl --get 'https://admin.vsl247.com/api/v1/superadmin/sales' \
  -H 'Authorization: Bearer SUPERADMIN_ACCESS_TOKEN' \
  --data-urlencode 'start=2026-08-01T00:00:00+03:30' \
  --data-urlencode 'end=2026-08-02T00:00:00+03:30'

--data-urlencode safely encodes the + in the timezone offset.

Basic authenticationAlternative
curl --get 'https://admin.vsl247.com/api/v1/superadmin/sales' \
  --user 'superadmin_username:superadmin_password' \
  --data-urlencode 'start=2026-08-01' \
  --data-urlencode 'end=2026-08-02'

Basic authentication is supported for /api/v1 routes. The account must still be a superadmin.

Response body200 OK
{
  "ok": true,
  "range": {
    "start": "2026-07-31T20:30:00Z",
    "end": "2026-08-01T20:30:00Z",
    "start_timestamp": 1785529800,
    "end_timestamp": 1785616200,
    "end_exclusive": true,
    "naive_input_timezone": "Asia/Tehran"
  },
  "totals": {
    "sales_gb": 125.5,
    "new_client_gb": 100.0,
    "added_gb": 25.5,
    "new_clients": 8,
    "add_operations": 3,
    "unresolved_events": 0
  },
  "admins": [
    {
      "admin_id": 42,
      "username": "portal_admin",
      "is_active": true,
      "sales_gb": 55.5,
      "new_client_gb": 40.0,
      "added_gb": 15.5,
      "new_clients": 3,
      "add_operations": 2,
      "unresolved_events": 0
    }
  ]
}

Response fields

FieldTypeDescription
rangeobjectNormalized UTC range and the Unix-second boundaries used by the report.
range.start, range.endstringNormalized UTC ISO 8601 values. start is inclusive; end is exclusive.
range.start_timestamp, range.end_timestampintegerUnix timestamps in seconds.
range.end_exclusivebooleanAlways true for this endpoint.
range.naive_input_timezonestringTimezone applied to ISO input without an explicit offset: Asia/Tehran.
totalsobjectCombined totals across all returned admin rows.
totals.sales_gbnumberGross sales in GB: new_client_gb + added_gb.
totals.new_client_gb, totals.added_gbnumberGB assigned at client creation and GB added to existing clients.
totals.new_clients, totals.add_operationsintegerCounts of client-creation and GB-addition sale events.
totals.unresolved_eventsintegerLegacy events whose amount could not be reconstructed. Non-zero means the report is incomplete for those events.
admins[]arrayOne row per current admin, including zero-sale admins. Historical deleted actors may appear as admin #ID.
admins[].*objectEach row contains admin_id, username, is_active, the GB totals, and the event counts.

Sales calculation

sales_gb = new_client_gb + added_gb The report uses client-creation and GB-addition audit events. Deleted users or later quota changes do not retroactively remove the original sale event.

Validation errors

StatusWhen it happensExample body
400 Bad RequestMissing or malformed time values, a non-positive range, or an out-of-range timestamp.{"ok": false, "error": "end must be later than start"}
401 UnauthorizedCredentials are missing, invalid, or an access token has expired.{"detail": "Not authenticated"}
403 ForbiddenThe credentials are valid, but the account is not a superadmin.{"detail": "Insufficient role"}
Authorization behavior Authentication and role checks happen before the report is generated. A normal admin token or Basic-auth account cannot read another admin's sales.
PATCH /api/v1/admins/{admin_id} Charge an admin, reduce quota, or update admin settings.
What this endpoint does Updates a portal admin selected by admin_id. Quota changes are write operations and are recorded in the superadmin audit log.
Authentication Bearer or Basic auth Send credentials with every request.
Required role superadmin Normal admin accounts receive 403.
Request body JSON object Send one or more supported fields.
Success 200 OK Returns {"ok": true}.

Path parameter

NameTypeRequiredDescription
admin_idintegerRequiredID of the portal admin to update. Get IDs from GET /api/v1/admins.

Quota actions

FieldTypeRequiredEffectRules
add_gb number Optional Charges the admin by increasing their total quota by this amount. Must be greater than 0. It does not directly change any client allocation.
reduce_gb number Optional Reduces the admin's total quota by this amount. Must be greater than 0. The new total cannot be lower than quota already allocated to clients.
Safe reduction rule If an admin has 80 GB allocated to clients, their total quota cannot be reduced below 80 GB. The API rejects a reduction that would violate this rule.

Other supported updates

FieldTypeEffect
is_activebooleanActivate or deactivate the admin account.
min_client_gbnumberSet the minimum GB allowed when the admin creates a client; must be greater than 0.
subscription_hoststringSet the subscription hostname, such as sub.example.com.
hide_brandingbooleanEnable or disable branded subscription output.
portal_passwordstringChange the portal login password.
marzban_username, marzban_passwordstringUpdate the linked panel credentials; the credentials are validated before saving.

Request examples

Charge 25 GBQuota increase
curl -X PATCH 'https://admin.vsl247.com/api/v1/admins/42' \
  -H 'Authorization: Bearer SUPERADMIN_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"add_gb": 25}'
Reduce 10 GBQuota decrease
curl -X PATCH 'https://admin.vsl247.com/api/v1/admins/42' \
  -H 'Authorization: Bearer SUPERADMIN_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"reduce_gb": 10}'
Response body200 OK
{
  "ok": true
}

Errors

StatusWhen it happensExample
400 Bad RequestThe body is not a JSON object, a GB value is missing/invalid/non-positive, the admin does not exist, or a reduction would go below allocated quota.Cannot reduce below allocated quota (80 GB)
401 UnauthorizedCredentials are missing, invalid, or expired.{"ok": false, "error": "Authentication required"}
403 ForbiddenThe credentials are valid, but the account is not a superadmin.{"ok": false, "error": "Forbidden"}
Recommended usage Send one quota action per request: use either add_gb or reduce_gb. Each successful change is auditable by the superadmin.

Portal-Native API

Legacy routes that use the portal's ok response wrapper.

GET /api/v1/me Return the current portal account and quota.
Response 200
{
  "ok": true,
  "user": {
    "username": "admin_username",
    "role": "admin",
    "quota_bytes": 107374182400,
    "remaining_bytes": 85899345920
  }
}
GET /api/v1/clients Return the portal-native client list.
Example request
GET /api/v1/clients?page=1&per_page=10&search=client&live=1
Response 200
{
  "ok": true,
  "page": 1,
  "per_page": 10,
  "total": 1,
  "has_next": false,
  "clients": []
}
POST /api/v1/admins Create a portal admin and link a non-sudo panel admin.
Request body
{
  "username": "portal_admin",
  "portal_password": "portal-password",
  "quota_gb": 100,
  "min_client_gb": 0.5,
  "subscription_host": "sub.example.com",
  "marzban_admin_mode": "existing",
  "marzban_username": "panel_admin",
  "marzban_password": "panel-password"
}
Response 201
{
  "ok": true,
  "admin_id": 42
}

No matching endpoints.

Compatibility Notes

  • PasarGuard-compatible routes manage only clients owned by the authenticated portal admin.
  • Client creation and data-limit increases follow portal quota rules.
  • Deleting a user never returns quota to the admin.
  • PasarGuard-compatible responses do not use the portal-native ok wrapper.
  • Unsupported panel administration and system routes are not proxied.