Use the management API to automate your own panel from scripts, bots and integrations. Users are called keys in the API: the Users screen corresponds to /api/v1/keys.
Create a personal API token
Sign in to your panel and open Settings → API tokens. Choose a name, an expiry and the permissions your integration needs. The default duration is 30 days; you can choose an integer from 1 to 365 days. Each account can have up to 50 active tokens. Viewers can create read-only tokens.
Copy the tgwp_… secret before closing the creation dialog: it appears only once. The panel stores its SHA-256 digest and a public prefix, so the token list cannot recover it. Store the secret in your integration’s secret manager or environment; avoid source code, logs and shared screenshots.
Send requests over HTTPS to your installed panel, for example https://panel.example.com/api/v1. tgproxypanel.com hosts this documentation and is not your management API endpoint.
Use one Authorization: Bearer <token> header. A validated personal Bearer token does not need cookies or X-CSRF-Token. Invalid or duplicate Authorization headers do not fall back to a signed-in browser session. Token support does not change the panel’s CORS policy; use these examples from a server or command line.
Revoke a token in Settings → API tokens when it is no longer needed. Expired and revoked tokens stop working. Changing your password revokes all your personal tokens; deleting the account invalidates its tokens. The token list shows expiry and last use; last-use timestamps update at most once per minute.
Permissions and account roles
Every request must be allowed by both the token’s scopes and the account’s current role. Write scopes do not automatically include read scopes. Role changes take effect on subsequent requests. Read-only and full-rights presets select scopes; full rights still follows the account’s role and the endpoint catalog below.
| Resource | Scopes | What it covers |
|---|---|---|
nodes | nodes:readnodes:write | Servers, configuration, installation and applies |
users | users:readusers:write | Users, bindings and subscription links; routes use /keys |
monitoring | monitoring:readmonitoring:write | Dashboard, monitoring and alerts |
sites | sites:readsites:write | Cover websites |
branding | branding:readbranding:write | Panel and subscription page appearance |
settings | settings:readsettings:write | Automation settings allowed by the account role |
audit | audit:read | Activity log |
Reading sensitive values requires write permission even when the HTTP method is GET: this includes user secrets, proxy links and QR codes, node installation commands and registration secrets. A read scope does not grant these values. Use the catalog to check the scope for each route.
Token creation, listing and revocation, account administration, password and 2FA changes, backups and subscription-service credentials require a browser session. A personal token cannot create another token or manage these operations. Agent, probe, metrics and subscription-service tokens have separate purposes and cannot be replaced by a personal token.
Mutation audit entries include the personal token’s ID in api_token_id, so you can identify the integration that made a change without storing its secret in the audit log.
curl: read a server and manage users
Replace the example address and UUIDs with values from your panel. Load your token from your secret store; the value below is a placeholder. Reading a node and listing users requires nodes:read and users:read. Creating, disabling and enabling users requires users:write and an owner or admin account.
Set the connection values
export TGWP_PANEL_URL='https://panel.example.com'
export TGWP_API_TOKEN='tgwp_replace_with_your_token'
export TGWP_NODE_ID='replace-with-node-uuid'
export TGWP_KEY_ID='replace-with-user-uuid'Read a node and list active users
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $TGWP_API_TOKEN" \
"$TGWP_PANEL_URL/api/v1/nodes/$TGWP_NODE_ID"
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $TGWP_API_TOKEN" \
"$TGWP_PANEL_URL/api/v1/keys?state=active&page=1&per_page=50"Create personal access
curl --fail-with-body --silent --show-error \
-X POST "$TGWP_PANEL_URL/api/v1/keys" \
-H "Authorization: Bearer $TGWP_API_TOKEN" \
-H 'Content-Type: application/json' \
--data "{\"label\":\"Automation example\",\"type\":\"PERSONAL\",\"node_ids\":[\"$TGWP_NODE_ID\"],\"expires_at\":null}"label, type and at least one node_ids UUID are required. type is uppercase PERSONAL or SHARED. expires_at: null means no user access expiry; supply a future RFC 3339 timestamp for a time limit. This is separate from the API token’s expiry. The response is HTTP 201 with a user object; copy its id into TGWP_KEY_ID. Creation responses can contain user secrets and links: handle them privately.
Pause and resume access
curl --fail-with-body --silent --show-error \
-X POST -H "Authorization: Bearer $TGWP_API_TOKEN" \
"$TGWP_PANEL_URL/api/v1/keys/$TGWP_KEY_ID/disable"
curl --fail-with-body --silent --show-error \
-X POST -H "Authorization: Bearer $TGWP_API_TOKEN" \
"$TGWP_PANEL_URL/api/v1/keys/$TGWP_KEY_ID/enable"Both calls return HTTP 200 with the updated user. These examples leave the user enabled. Resuming access does not remove an expired access date or undo revocation.
Python without external packages
This script uses the same environment variables and permissions as the curl examples. It reads a node, paginates through active users, then creates a user with seven days of access and disables and enables that new user. Save it as example.py and run python3 example.py. It prints identifiers and state rather than the complete creation response.
import json
import os
from datetime import datetime, timedelta, timezone
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
base = os.environ["TGWP_PANEL_URL"].rstrip("/") + "/api/v1"
token = os.environ["TGWP_API_TOKEN"]
node_id = os.environ["TGWP_NODE_ID"]
if not base.startswith("https://"):
raise ValueError("Use the HTTPS address of your own panel")
def api(method, path, body=None):
headers = {"Authorization": "Bearer " + token}
data = None
if body is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(body).encode("utf-8")
req = Request(base + path, data=data, headers=headers, method=method)
try:
with urlopen(req, timeout=30) as response:
raw = response.read()
return json.loads(raw) if raw else None
except HTTPError as exc:
raw = exc.read()
try:
error = json.loads(raw).get("error", {})
except (ValueError, AttributeError):
error = {"code": "http_error", "message": "Non-JSON response"}
raise RuntimeError(
f"HTTP {exc.code}: {error.get('code')}: "
f"{error.get('message')}; fields={error.get('fields', {})}"
) from None
node = api("GET", "/nodes/" + node_id)
print(node["name"], node["status"])
page = 1
while True:
result = api("GET", "/keys?" + urlencode({
"state": "active", "page": page, "per_page": 50
}))
for user in result["items"]:
print(user["id"], user["label"], user["state"])
if not result["items"] or page * result["per_page"] >= result["total"]:
break
page += 1
expires = datetime.now(timezone.utc) + timedelta(days=7)
user = api("POST", "/keys", {
"label": "Automation example", "type": "PERSONAL",
"node_ids": [node_id], "expires_at": expires.isoformat()
})
print("Created user:", user["id"])
api("POST", "/keys/" + user["id"] + "/disable")
api("POST", "/keys/" + user["id"] + "/enable")Lists, filters and pagination
List responses use items; do not assume that every list accepts the same pagination parameters.
| Route / parameter | Behavior |
|---|---|
/keys: page | Starts at 1; default 1. |
/keys: per_page | 1–200; default 50. Values outside that range use 50. |
/keys: q | Personal tokens without users:write search only label, owner_label and note; hidden subscription slugs are excluded. Tokens with users:write and browser sessions also search subscription slugs. |
/keys: type | PERSONAL or SHARED. |
/keys: status | Stored status: pending, active or revoked. |
/keys: state | active, pending, expiring, expired, disabled or revoked. expiring means within seven days. |
/keys: node | Filter by a node UUID; the parameter is node, not node_id. |
/nodes | Returns {items, total}; no page or per_page parameters. |
/nodes/{id}/jobs: limit | 1–100; default 20. Values outside that range use 20. |
GET /keys returns {items, total, page, per_page}. Fetch pages until you reach total or receive an empty items array. Lists can change while you paginate; avoid changing the filtered users in the middle of a scan.
Applying changes and retrying requests
Creating or changing a user schedules configuration changes on its nodes; a successful HTTP response does not mean that the proxy has applied them yet. Inspect the user’s nodes[].profile_sync, and the node’s dirty, last_apply_at and GET /nodes/{id}/jobs. Job history requires nodes:write because it includes raw logs and errors. Jobs include status, started_at, finished_at and error; statuses are queued, running, ok, failed and rolled_back.
POST /nodes/{id}/apply requires nodes:write and returns HTTP 202 with {"queued": true}. It does not return a job ID. Poll the node and recent jobs with a reasonable delay, such as five seconds, and an overall timeout. A failed job needs inspection; repeatedly queuing applies will not explain the failure.
Use request timeouts. Retry reads after transient network or server failures with a bounded delay. Do not blindly retry user creation, secret rotation or other mutations: a lost response can follow a successful operation. Check the resulting state first to avoid duplicate users or unintended changes.
HTTP statuses and error responses
Successful responses can be JSON or another documented format, such as a PNG or Prometheus text. HTTP 204 has no response body. Errors use this JSON envelope; fields is present for field validation errors:
{
"error": {
"code": "validation",
"message": "invalid input",
"fields": {
"node_ids": "bind at least one node"
}
}
}| Status | Meaning |
|---|---|
| 200 / 201 / 202 / 204 | Read or update succeeded / resource created / queued / success without a body. |
| 400 | Malformed request or JSON; unknown JSON fields are rejected. |
| 401 | Missing, invalid, expired or revoked credentials. |
| 403 | Scope, current role or session-only route denies the operation. |
| 404 | Resource absent, or identifier not visible to the caller. |
| 409 | Conflict with existing state; reread before changing it. |
| 422 | Validation failed; inspect error.fields. |
| 500 / 502 / 503 | Internal error, node operation failure or offline node; inspect the error code and state. |
Endpoint and scope catalog
Each path below is relative to your panel origin and includes /api/v1. The catalog and downloadable OpenAPI 3.0.3 specification come from the backend’s explicit personal-token route policy. Unlisted routes are unavailable to personal tokens. Owner-only restrictions apply in addition to scopes.
Nodes
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/fleet/updates | nodes:write | List fleet updates |
POST | /api/v1/fleet/updates | nodes:write | Start fleet update |
POST | /api/v1/fleet/updates/{id}/stop | nodes:write | Stop fleet update |
GET | /api/v1/nodes | nodes:read | List nodes |
POST | /api/v1/nodes | nodes:write | Create node |
PUT | /api/v1/nodes/order | nodes:write | Reorder nodes |
POST | /api/v1/nodes/preflight/dns | nodes:write | Check node DNS |
DELETE | /api/v1/nodes/{id} | nodes:write | Delete node |
GET | /api/v1/nodes/{id} | nodes:read | Get node |
PATCH | /api/v1/nodes/{id} | nodes:write | Update node |
POST | /api/v1/nodes/{id}/apply | nodes:write | Apply node configuration |
GET | /api/v1/nodes/{id}/blocklist | nodes:read | Get node blocklist |
PUT | /api/v1/nodes/{id}/blocklist | nodes:write | Update node blocklist |
POST | /api/v1/nodes/{id}/check | nodes:write | Check node |
GET | /api/v1/nodes/{id}/diagnostics | nodes:write | List node diagnostics |
POST | /api/v1/nodes/{id}/diagnostics/web | nodes:write | Run node web diagnostics |
GET | /api/v1/nodes/{id}/health | nodes:read | Get node health |
GET | /api/v1/nodes/{id}/install-command | nodes:write | Issue node install command |
GET | /api/v1/nodes/{id}/jobs | nodes:write | List node apply jobs |
GET | /api/v1/nodes/{id}/logs | nodes:write | Stream node logs |
GET | /api/v1/nodes/{id}/metrics | nodes:read | Get node metrics |
GET | /api/v1/nodes/{id}/probes | nodes:read | List node probes |
GET | /api/v1/nodes/{id}/profiles | nodes:read | List node profiles |
GET | /api/v1/nodes/{id}/registration-secret | nodes:write | Get node registration secret |
GET | /api/v1/nodes/{id}/reliability | nodes:read | Get node reliability |
PUT | /api/v1/nodes/{id}/reliability | nodes:write | Update node reliability |
POST | /api/v1/nodes/{id}/restart | nodes:write | Restart node |
GET | /api/v1/nodes/{id}/stats | nodes:read | Get node statistics |
GET | /api/v1/nodes/{id}/telemt-update | nodes:write | List node update jobs |
POST | /api/v1/nodes/{id}/telemt-update | nodes:write | Update node telemt |
GET | /api/v1/nodes/{id}/web-policy | nodes:read | Get node web policy |
PUT | /api/v1/nodes/{id}/web-policy | nodes:write | Update node web policy |
GET | /api/v1/nodes/{id}/web/carriers | nodes:read | Get node web carriers |
POST | /api/v1/nodes/{id}/web/lifecycle | nodes:write | Control node web lifecycle |
GET | /api/v1/status/update | nodes:read | Get panel update status |
Users
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/keys | users:read | List users |
POST | /api/v1/keys | users:write | Create user |
POST | /api/v1/keys/batch | users:write | Create users in batch |
POST | /api/v1/keys/bulk | users:write | Perform bulk user action |
POST | /api/v1/keys/import | users:write | Import users |
GET | /api/v1/keys/summary | users:read | Get user summary |
DELETE | /api/v1/keys/{id} | users:write | Delete user |
GET | /api/v1/keys/{id} | users:read | Get user |
PATCH | /api/v1/keys/{id} | users:write | Update user |
POST | /api/v1/keys/{id}/bindings | users:write | Bind user to node |
DELETE | /api/v1/keys/{id}/bindings/{node} | users:write | Unbind user from node |
POST | /api/v1/keys/{id}/disable | users:write | Disable user |
POST | /api/v1/keys/{id}/enable | users:write | Enable user |
GET | /api/v1/keys/{id}/links | users:write | Get user proxy links |
GET | /api/v1/keys/{id}/presence | users:read | Get people online for a user |
GET | /api/v1/keys/{id}/qr | users:write | Get user proxy QR code |
POST | /api/v1/keys/{id}/revoke | users:write | Revoke user |
POST | /api/v1/keys/{id}/rotate | users:write | Rotate user secret |
GET | /api/v1/keys/{id}/stats | users:read | Get user statistics |
DELETE | /api/v1/keys/{id}/subscription | users:write | Revoke user subscription |
POST | /api/v1/keys/{id}/subscription | users:write | Issue user subscription |
GET | /api/v1/keys/{id}/subscription/qr | users:write | Get user subscription QR code |
Monitoring
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/alerts | monitoring:read | List alerts |
POST | /api/v1/alerts/resolve | monitoring:write | Resolve alerts |
POST | /api/v1/alerts/{id}/resolve | monitoring:write | Resolve alert |
GET | /api/v1/dashboard/summary | monitoring:read | Get dashboard summary |
GET | /api/v1/dashboard/trends | monitoring:read | Get dashboard trends |
GET | /api/v1/monitoring/nodes/{id}/series | monitoring:read | Get node monitoring series |
GET | /api/v1/monitoring/overview | monitoring:read | Get monitoring overview |
GET | /api/v1/monitoring/web/carriers | monitoring:read | Get fleet web carriers |
GET | /api/v1/status/public | monitoring:read | Get public service status |
Sites
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/nodes/{id}/site | sites:read | Get node site |
POST | /api/v1/nodes/{id}/site | sites:write | Assign node site |
GET | /api/v1/nodes/{id}/site/preview | sites:read | Preview node site |
POST | /api/v1/nodes/{id}/site/upstream | sites:write | Assign node upstream site |
GET | /api/v1/site-templates | sites:read | List site templates |
POST | /api/v1/site-templates | sites:write | Create site template |
POST | /api/v1/site-templates/import | sites:write | Import site template |
POST | /api/v1/site-templates/validate | sites:write | Validate site template |
DELETE | /api/v1/site-templates/{id} | sites:write | Delete site template |
GET | /api/v1/site-templates/{id} | sites:read | Get site template |
PUT | /api/v1/site-templates/{id} | sites:write | Update site template |
POST | /api/v1/site-templates/{id}/customize | sites:write | Customize site template |
Branding
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/branding/profiles | branding:read | List branding profiles |
POST | /api/v1/branding/profiles | branding:write | Create branding profile |
DELETE | /api/v1/branding/profiles/{id} | branding:write | Delete branding profile |
PUT | /api/v1/branding/profiles/{id} | branding:write | Update branding profile |
POST | /api/v1/branding/profiles/{id}/activate | branding:write | Activate branding profile |
POST | /api/v1/branding/profiles/{id}/upload | branding:write | Upload branding asset |
Settings
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/settings | settings:read | Get panel settings |
PUT | /api/v1/settings | settings:writeOwner only | Update panel settings |
GET | /api/v1/settings/subscription-page/preview | settings:read | Preview subscription page |
POST | /api/v1/settings/telegram/test | settings:writeOwner only | Test Telegram notifications |
Audit
| Method | Endpoint | Permission | Operation |
|---|---|---|---|
GET | /api/v1/audit | audit:read | List audit events |
For installation, use the panel and telemt guide. The repository’s API guide also describes browser-session token management.