API Reference
Cookie-authenticated JSON API for bots and dashboards.
POST /api/login (keeps the zefira_session cookie), then send X-Requested-With: XMLHttpRequest on every mutating call. No OpenAPI explorer is exposed.Bots & integrations: create a long-lived token under Settings → API Tokens with scope full (default, everything) or bot (least-privilege for resellers: GET /api/me, GET /api/stats, GET /api/users, POST /api/users, GET /api/templates) and send it as Authorization: Bearer zfp_… — no CSRF header needed then. Tokens act as the admin that created them (password-gated actions still need the password), are shown once, and can be revoked anytime. Bot tokens can create users including start_on_first_use (bot-safe: expiry is server-computed) but get 403 on deletes, patches, backup/restore, settings, tokens and updates.
Ready-made reseller bots: Python · Node.js — plans keyboard, account creation, subscription link.
Auth & account
| Method | Endpoint | Notes |
|---|---|---|
| POST | /api/login | {username, password} |
| POST | /api/logout | Invalidates the session server-side (all devices, this panel has no per-session tracking) |
| GET POST DELETE | /api/api-tokens… · /api/api-tokens/{id} · /api/api-tokens/self-test | Bot tokens: list, create (once-only secret), revoke. Creating one requires the admin password; a new token expires after 180 days (expires_in_days, 0 = never, max 20 per admin). self-test POSTs the candidate secret back and reports whether it authenticates, so you can tell a working token from a typo without spending a real call on it. |
| GET | /api/me | Username, plus auth naming the credential that authenticated the call {type, name, scopes} — use it to tell your tokens apart |
| POST | /api/change-password | Strong-password enforced, kills other sessions (and revokes every API token — bots must re-auth with a new token) |
Users
| Method | Endpoint | Notes |
|---|---|---|
| GET | /api/stats | Counters: active, expired, expiring-soon, limited… |
| GET | /api/users?q=… | Up to 500, username/note search |
| GET | /api/users/by-username/{username} | Exact lookup — bots know usernames, not IDs |
| POST | /api/users | Create (protocols, volume, days, first-use, device limit) — or template_id alone to apply a saved plan |
| PATCH | /api/users/{id} | Change one user. Absolute fields set a value: used_gb, volume_gb, expires_at, note, device_limit, is_active, days (extend). Deltas: add_used_gb, add_volume_gb. Also reset_used: true. |
| DELETE | /api/users/{id} | — |
| POST | /api/users/{id}/reset-token | Rotates token + all secrets (bot scope OK) |
| POST | /api/users/{id}/reset-usage | Zeroes used traffic (for developers/bots) |
| POST | /api/users/{id}/reset | Developer combo: {"reset_usage": true, "reset_token": false} — one call for renew/top-up flows (token rotation kills the old link) |
| GET | /api/users/{id}/qr · /config | Sub URL + QR / config file or ZIP (config: full scope only) |
Server objects
| Method | Endpoint | Notes |
|---|---|---|
| GET PUT | /api/settings | Domain, ports (VLESS/VMess/Trojan/SS, Hysteria2, WireGuard, OpenVPN, L2TP, Cisco, SOCKS5), REALITY, obfuscation… |
| POST | /api/reality/generate | New X25519 keypair |
| GET | /api/reality/private | Audit-logged reveal |
| GET PUT | /api/appearance | Theme colors, brand, dashboard note (GET is public, PUT needs session) |
| GET | /theme.css | Generated theme overrides, text/css |
| GET PUT | /api/ai/settings | Provider, model, key state (key never returned) |
| POST | /api/ai/test | One cheap completion that checks provider, base URL, model and key together. Run it right after saving the settings: it costs a single chat turn, and it turns a dead config into an error at the settings page instead of at the first real question. |
| POST | /api/ai/chat | {messages[≤12]} → scoped assistant reply (30/hr). The assistant can also act: create/extend/top-up/reset/pause users, find users, stats, sub links (max 3 ops/turn, audit-logged; delete/reset/backup/update/settings/tokens never) |
| GET | /api/update/status | Current vs latest commit + incoming changelog |
| POST | /api/update/apply | Password-confirmed pull + pip + restart |
| GET | /api/ssl/status | Certbot presence, domains, expiry |
| POST | /api/ssl/issue · /ssl/renew | {domain, subdomain?, email} |
| GET POST DELETE | /api/server-nodes… | Register servers, health, latency, uptime |
| POST | /api/server-nodes/{id}/check | On-demand probe |
| GET POST DELETE | /api/inbounds · /api/inbounds/{id} | Extra ports per protocol (optional node pin) |
| GET POST DELETE | /api/templates… · /api/nodes… · /api/blocklist… · /api/blocklist/porn | Plans, tunnels, blocked sites. /api/blocklist/porn POSTs {porn_enabled: bool} and flips the built-in adult-domain preset in one call — the same switch as the button on the Site Blocker page. |
| POST | /api/nodes/{id}/check · /guide · /reveal-token · /regen-token | Tunnel ops |
| POST | /api/backup | Full JSON export, password-confirmed (download). {"encrypt": true} returns a scrypt + Fernet blob instead of plaintext |
| POST | /api/restore | Password-confirmed re-import (weak admin hashes / zero-volume users skipped, scopes preserved) |
| POST | /api/restore-encrypted | {password_confirm, salt, payload, backup_password?} — restores an encrypted backup |
| GET | /api/audit · /api/system · /api/telegram… · /api/telegram/test | Logs, CPU/RAM/disk, notifications. /api/telegram/test POSTs the token and chat id you are about to save and reports what the Bot API answered, so a bad chat id surfaces as a message from Telegram instead of as silence later. |
| GET PUT | /api/tunnel-settings | The two BackPack fields: public_url (the address the outer server dials back on) and trusted_proxies (CIDRs whose X-Forwarded-For is believed for rate limiting and audit logs). Same page as Settings → Tunnel; a mis-set proxy list makes every client look like one address. |
Public
| Method | Endpoint | Notes |
|---|---|---|
| GET | /sub/{token} | Browsers get the user dashboard; clients get Base64 sub, or Clash YAML with ?format=clash. Expired, disabled and out-of-volume (used_gb >= volume_gb) users 404 in all modes; device_limit is advisory only |
Example: create a user
curl -c jar.txt -H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-d '{"username":"admin","password":"…"}' \
https://vpn.example.com/api/login
curl -b jar.txt -H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-d '{"username":"buyer1","protocols":["vless","reality"],"volume_gb":50,"days":30}' \
https://vpn.example.com/api/users
# Or create straight from a saved plan: template_id fills protocols, volume,
# days, start_on_first_use and device_limit. Explicit body fields win.
curl -b jar.txt -H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-d '{"username":"buyer2","template_id":3,"days":60}' \
https://vpn.example.com/api/users
# PATCH sets absolute values; add_* are deltas. An unknown-only patch is a
# 422 (never a silent 200), and sending both forms of one field is a 422.
curl -X PATCH -b jar.txt -H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-d '{"used_gb": 12.5, "volume_gb": 100, "days": 30, "note": "VIP"}' \
https://vpn.example.com/api/users/1
curl -X PATCH -b jar.txt -H 'Content-Type: application/json' \
-H 'X-Requested-With: XMLHttpRequest' \
-d '{"add_volume_gb": 20, "add_used_gb": -2.5}' \
https://vpn.example.com/api/users/1
Example: bot renewal flow (bot-scoped token)
No cookies, no CSRF header — just Authorization: Bearer zfp_… from a bot-scoped token. Lookup by username, renew usage + link in one call:
# 1) find the buyer by username (bots know tg123, not numeric IDs)
curl -H 'Authorization: Bearer zfp_…' \
https://vpn.example.com/api/users/by-username/tg123
# 2) renew: zero the meter AND rotate token+secrets (old link dies)
curl -X POST -H 'Authorization: Bearer zfp_…' \
-H 'Content-Type: application/json' \
-d '{"reset_usage": true, "reset_token": true}' \
https://vpn.example.com/api/users/12/reset
# 3) usage-only top-up (link keeps working)
curl -X POST -H 'Authorization: Bearer zfp_…' \
-H 'Content-Type: application/json' \
-d '{"reset_usage": true, "reset_token": false}' \
https://vpn.example.com/api/users/12/reset
bot tokens get 200 on lookup, QR, reset-usage and reset — and 403 on deletes, patches, backup/restore, settings, tokens and updates.
ZEF