Admin panel: all routes and API contracts
Documentation of every route in the built-in telesrv admin panel — the SPA
page addresses and the API routes behind them. The panel server lives in
cmd/telesrv-admin; routes are declared in (*server).routes()
(server.go:50).
The panel is a plain HTTP server: it serves the built frontend (/) and a
JSON API under the /api/ prefix. Every API path requires a session (cookie),
and every mutating request additionally requires a CSRF token. Many routes also
check an operator permission (permission).
How to use this document
- For each GET endpoint, the request parameters (query) and a short response shape are listed.
- For each POST endpoint under
/api/actions/* and the verification decisions, the request body (JSON) is given. They all share common fields (see below) plus their own specific fields.
int64 in JSON may be sent either as a number or as a string (e.g. "user_id": "123" or "user_id": 123) — the server accepts both (flexInt64). flexUnix accepts Unix seconds or a date in 2006-01-02 / RFC3339 form.
- All mutating requests go to the Admin API. A command's response is either
{"status":..., "message":..., "command_id":...} on success (HTTP 200) or {"error":..., "code":...} on error. An optimistic-locking conflict is 409.
Authentication and session
| Method |
Path |
Description |
| POST |
/api/login |
Log in. Body: {"secret": "..."}. Validates the config secret, issues a signed session cookie and CSRF cookie. The only mutating route without a CSRF token (no session yet); only Origin is checked. |
| POST |
/api/logout |
Log out. Destroys the session and CSRF cookie. |
| GET |
/api/session |
Returns {"actor": "...", "permissions": [...], "csrf_token": "..."}. Called by the panel on startup. |
The session lives in a signed cookie (default TTL 12 hours); there is no
server-side session store. Every request except GET/HEAD/OPTIONS and
/api/login must present:
- the
telesrv_admin_csrf cookie;
- the
X-CSRF-Token header with the same value;
- an Origin matching the host (when Origin is present).
Violating any of the three — 403 Forbidden.
Permissions and their checks
The panel distinguishes plain authorization (requireAuthAPI) from a
permission check (requirePermission). Permission names match the strings in
TELESRV_ADMIN_UI_PERMISSIONS:
| Permission |
What it grants |
* |
All permissions (wildcard). |
premium.manage |
Managing the Premium plan catalog, granting Premium. |
bots.token.read |
Exporting a bot token (/api/actions/export-bot-token). |
verification.review |
Reading and deciding the official verification queue. |
verification.revoke |
Stripping the official badge (in addition to verification.review). |
botverification.review |
Reading and deciding the third-party (bot) verification queue. |
botverification.manage |
Appointing verifiers, editing the icon catalog, stripping a third-party mark. |
Official and third-party verification rights are intentionally independent. The
session's permission list is returned from /api/session so the UI can hide
unavailable sections.
Interface pages (SPA)
SPA routes are declarative: any section name is served as / (index.html), and
the frontend decides what to render (web/src/pages/Routes.tsx).
| Path |
Page |
/ |
Dashboard (counters, storage stats, section links). |
/accounts |
Account list (with search). |
/accounts/{id} |
Account card: profile, actions. |
/channels |
Supergroup and channel list. |
/channels/{id} |
Channel card. |
/bots |
Bot list. |
/bots/{id} |
Bot card. |
/broadcasts |
Broadcasts. |
/monetization, /premium |
Stars and Premium: plans, grants. Requires premium.manage. |
/moderation |
Complaints and moderation: case list. |
/moderation/{id} |
Moderation case details. |
/emoji |
Emoji set catalog. |
/stickers |
Sticker pack catalog. |
/gif-catalog |
GIF catalog. |
/messages, /messages/private |
Private message audit. |
/messages/detail, /messages/private/detail |
Private message detail (?owner_user_id=&msg_id=). |
/messages/groups |
Group/channel message audit. |
/messages/groups/detail |
Group message detail (?channel_id=&msg_id=). |
/gifts |
Star gifts: catalog, collectibles, auctions. |
/give-gifts |
Gift granting. |
/collectible-usernames |
Collectible usernames. |
/collectible-usernames/{id} |
Collectible username card. |
/collectible-phones |
Anonymous numbers. |
/account-ratings |
Account ratings. |
/account-ratings/{user_id} |
Account rating card. |
/storage |
Object storage: stats. |
/verification |
Official verification: application queue. Requires verification.review. |
/verification/{id} |
Official verification application details. Requires verification.review. |
/bot-verification |
Third-party verification: verifiers, icons, marks, queue. Requires botverification.review. |
/bot-verification/{id} |
Third-party verification request details. Requires botverification.review. |
An unknown API path returns 404 {"error":"api route not found"}; any unknown
frontend path is served as /.
Response conventions
- Successful reads —
200 + JSON; files (avatars, animations, previews) — the file itself.
- Errors — JSON of the form
{"error": "...", "code": "..."} with the corresponding HTTP status.
401 — missing/expired session; 403 — CSRF/Origin violation or missing permission (in requirePermission the body gets an added permission field); 409 — optimistic-locking conflict (moderation case, verification); 502 — Admin API unreachable.
- Errors from commands sent to the Admin API are returned as
{"status": ..., "message": ..., "error": ...}.
Common command fields (POST /api/actions/*)
Every mutating request carries in its body:
| Field |
Type |
Description |
command_id |
string |
Idempotency key for the command. Repeating the same command_id does not execute the action twice. If empty — generated by the server. |
reason |
string |
Mandatory operation reason (audit). |
confirm |
bool |
Operator confirmation (true). |
API: dashboard and storage
| Method |
Path |
Description |
| GET |
/api/dashboard |
Summary: counts, storage, and optionally host. |
| GET |
/api/storage/stats |
Object storage statistics. |
API: accounts
| Method |
Path |
Parameters / response |
| GET |
/api/accounts |
Params: q (search), before_id (int64), before_active_us (int64, microseconds), limit (int). Response: query, limit, rows, has_more, next_before_id, next_before_active_us, listing. |
| GET |
/api/accounts/{id} |
Account card: profile, flags, statistics. |
| GET |
/api/accounts/{id}/avatar |
Account avatar (file). |
| GET |
/api/account-ratings |
Params: q, min_level (int), user_id (int64), before_id (int64), limit (int). Response: rows, has_more, next_before_id. |
| GET |
/api/account-ratings/{user_id} |
Response: rating, events. |
API: channels and supergroups
| Method |
Path |
Parameters / response |
| GET |
/api/channels |
Params: q, before_id (int64), before_updated_us (int64, microseconds), limit (int). Response: query, limit, rows, has_more, next_before_id, next_before_updated_us, listing. |
| GET |
/api/channels/{id} |
Channel card. |
| GET |
/api/channels/{id}/avatar |
Channel avatar (file). |
API: bots and broadcasts
| Method |
Path |
Parameters / response |
| GET |
/api/bots |
Params: q, before_id (int64), limit (int). Response: query, limit, rows, has_more, next_before_id, listing. |
| GET |
/api/bots/{id} |
Bot card. |
| GET |
/api/broadcasts |
Params: before_id (int64), limit (int). Response: limit, rows, has_more, next_before_id. |
API: media catalogs
| Method |
Path |
Parameters / response |
| GET |
/api/emoji |
Params: q, before_id (int64), limit (int). Response: query, rows, has_more, next_before_id, listing. |
| GET |
/api/emoji/{id}/animation |
Emoji Lottie animation (file). |
| GET |
/api/stickers |
Param: kind (string, type filter). Response: rows, max_items. |
| GET |
/api/stickers/{id}/documents |
Response: document_ids. |
| GET |
/api/stickers/documents/{id}/animation |
Sticker animation (file). |
| GET |
/api/gif-catalog |
Response: proxied from Admin API (/v1/gif-catalog). |
| GET |
/api/gif-catalog/documents/{id}/preview |
GIF preview (file). |
API: message audit
| Method |
Path |
Parameters / response |
| GET |
/api/messages |
Params: owner_user_id (int64, required with peer_id), peer_id (int64), before_date (int64), before_id (int), limit (int). Response: owner_user_id, peer_id, before_date, before_id, limit, rows. |
| GET |
/api/messages/detail |
Params: owner_user_id (int64, req.), msg_id (int, req.). Response: message card. |
| GET |
/api/messages/groups |
Params: channel_id (int64, req.), before_date (int64), before_id (int), limit (int). Response: channel_id, before_date, before_id, limit, rows. |
| GET |
/api/messages/groups/detail |
Params: channel_id (int64, req.), msg_id (int, req.). Response: message card. |
API: star gifts and collectibles
| Method |
Path |
Parameters / response |
| GET |
/api/gifts |
Response: Gifts (gift list). |
| GET |
/api/auctions |
Response: Auctions — live state of all operator-authored auctions and scheduled drops. |
| GET |
/api/official-gifts |
Response: proxied from Admin API (/v1/official-gifts). |
| GET |
/api/official-gifts/{id}/animation |
Official gift animation (file). |
| GET |
/api/gifts/{id}/animation |
Gift animation (file). |
| GET |
/api/gifts/{id}/collectibles |
Response: proxied from Admin API (/v1/gifts/{id}/collectibles). |
| GET |
/api/gifts/{id}/collectibles/{kind}/{attribute_id}/animation |
Collectible attribute animation (file). kind ∈ {model, pattern}. |
| GET |
/api/collectible-usernames |
Params: status (`` |
| GET |
/api/collectible-usernames/{id} |
Response: asset, transfers. |
| GET |
/api/collectible-phones |
Params are passed through to the Admin API as-is (/v1/collectible-phones?...). |
| GET |
/api/collectible-phones/{id} |
Params are passed through to the Admin API (/v1/collectible-phones/{id}?...). |
API: Premium
All routes in this section require the premium.manage permission (checked both
at the panel and at the Admin API).
| Method |
Path |
Description |
| GET |
/api/premium/plans |
Premium plan catalog (proxied to Admin API /v1/premium/plans). |
API: moderation
All reads pass query parameters through to the Admin API as-is
(/v1/moderation/...). Decisions are POST (see below).
| Method |
Path |
Description |
| GET |
/api/moderation/cases |
Moderation case list. |
| GET |
/api/moderation/cases/{id} |
Moderation case. |
| GET |
/api/moderation/reports/{id} |
Report. |
| POST |
/api/moderation/cases/{id}/claim |
Claim the case. Body: common fields + version (int64), internal_note (string). |
| POST |
/api/moderation/cases/{id}/decide |
Decide the case. Body: common fields + version (int64), internal_note (string). |
| POST |
/api/moderation/cases/{id}/appeals/{appeal_id}/review |
Review an appeal. Body: common fields + version (int64), internal_note (string). |
API: official verification
All routes require the verification.review permission. Reads go straight to
PostgreSQL; decisions always go through the Admin API (command journal, state
machine, optimistic locking).
| Method |
Path |
Parameters / body |
| GET |
/api/verification/applications |
Params: status, target_type, reviewer, q, before_id, limit. Response: rows, has_more, next_before_id. |
| GET |
/api/verification/applications/{id} |
Response: application, events, applicant_controls_target, target_verified. |
| GET |
/api/verification/counts |
Application counts by status. |
| POST |
/api/verification/applications/{id}/claim |
Decision body: common fields + version (int64), internal_note (string). |
| POST |
/api/verification/applications/{id}/approve |
Body: common fields + version (int64), internal_note (string). Grants badge. |
| POST |
/api/verification/applications/{id}/reject |
Body: common fields + version (int64), internal_note (string). |
| POST |
/api/actions/revoke-verification |
Strip a badge. Requires verification.review and verification.revoke. Body — see actions section below. |
A "decided by another moderator" conflict is returned as 409 Conflict.
API: third-party (bot) verification
A separate mechanism with separate tables, permissions, and routes. Queue reads
and decisions require botverification.review; managing verifiers, the icon
catalog, and stripping marks requires botverification.manage.
| Method |
Path |
Parameters / body |
| GET |
/api/botverification/verifiers |
Params: enabled_only (bool), limit (int). Response: rows. |
| GET |
/api/botverification/icons |
Params: active_only (bool), limit (int). Response: rows. |
| GET |
/api/botverification/marks |
Params: peer_type, verifier_bot_id (int64), q, before_id (int64), limit (int). Response: rows, has_more, next_before_id. |
| GET |
/api/botverification/requests |
Params: status, peer_type, verifier_bot_id (int64), q, before_id (int64), limit (int). Response: rows, has_more, next_before_id. |
| GET |
/api/botverification/requests/{id} |
Response: request, verifier, mark_active. |
| GET |
/api/botverification/counts |
Request counts by status. Response: counts. |
| POST |
/api/botverification/requests/{id}/approve |
Decision body: common fields + version (int64), internal_note (string). |
| POST |
/api/botverification/requests/{id}/reject |
Body: common fields + version (int64), internal_note (string). |
| POST |
/api/botverification/requests/{id}/revoke |
Body: common fields + version (int64), internal_note (string). |
| POST |
/api/actions/grant-bot-verifier |
Appoint a bot as verifier. Requires botverification.manage. Body — see below. |
| POST |
/api/actions/set-bot-verifier-enabled |
Enable/disable a verifier. Requires botverification.manage. Body — see below. |
| POST |
/api/actions/revoke-bot-verifier |
Strip a bot's verifier status. Requires botverification.manage. Body — see below. |
| POST |
/api/actions/upsert-verification-icon |
Add/edit a catalog icon. Requires botverification.manage. Body — see below. |
| POST |
/api/actions/set-verification-icon-active |
Enable/disable an icon. Requires botverification.manage. Body — see below. |
| POST |
/api/actions/revoke-custom-verification |
Strip a third-party mark. Requires botverification.manage. Body — see below. |
API: account actions
All routes are POST /api/actions/..., require a session and CSRF. The request
body always contains the common command fields (command_id, reason,
confirm) plus the fields from the table below.
| Path |
Specific body fields |
Permission |
set-frozen |
user_id (int64), frozen (bool), freeze_until (time, opt.), freeze_appeal_url (string, opt.) |
— |
grant-premium |
user_id (int64), months (int) |
premium.manage |
upsert-premium-plan |
months (int), duration_days (int), amount_stars (int64), fiat_currency (string), fiat_amount (int64), store_product (string), store_quantity (int), enabled (bool), sort_order (int), label (string), expected_version (int64) |
premium.manage |
grant-stars |
user_id (int64), amount (int64) |
— |
set-verified |
user_id (int64), verified (bool) |
— |
set-account-flags |
user_id (int64), scam (bool), fake (bool) |
— |
set-support |
user_id (int64), support (bool) |
— |
set-account-username |
user_id (int64), username (string) |
— |
set-account-profile |
user_id (int64), first_name (string), last_name (string) |
— |
set-account-phone |
user_id (int64), phone (string) |
— |
set-account-login-email |
user_id (int64), email (string) |
— |
set-account-avatar |
multipart: metadata field (JSON with common fields + user_id (int64)) and a file in file |
— |
set-account-color |
user_id (int64), for_profile (bool), has_color (bool), color (int), background_emoji_id (int64) |
— |
set-account-emoji-status |
user_id (int64), document_id (int64), until (int, seconds) |
— |
revoke-sessions |
user_id (int64), hash (int64, opt.), keep_hash (int64, opt.), revoke_all (bool) |
— |
Example (granting Premium, the case from above):
POST /api/actions/grant-premium
Content-Type: application/json
X-CSRF-Token: <csrf from /api/session>
{
"command_id": "grant-premium-001",
"reason": "Incident compensation",
"confirm": true,
"user_id": 123456789,
"months": 12
}
API: channel actions
| Path |
Specific body fields |
Permission |
set-channel-flags |
channel_id (int64), scam (bool), fake (bool) |
— |
set-channel-settings |
channel_id (int64), gigagroup (*bool), antispam (*bool), participants_hidden (*bool), noforwards (*bool), join_to_send (*bool), join_request (*bool), slowmode_seconds (*int) — all optional pointers |
— |
set-channel-username |
channel_id (int64), username (string) |
— |
set-channel-color |
channel_id (int64), for_profile (bool), has_color (bool), color (int), background_emoji_id (int64) |
— |
set-channel-emoji-status |
channel_id (int64), document_id (int64), until (int) |
— |
set-channel-avatar |
multipart: metadata (JSON with common fields + channel_id (int64)) and a file file |
— |
set-channel-verified |
channel_id (int64), verified (bool) |
— |
API: bot actions
| Path |
Specific body fields |
Permission |
create-bot |
owner_user_id (int64), name (string), username (string) |
— |
delete-bot |
bot_user_id (int64) |
— |
export-bot-token |
bot_user_id (int64) |
bots.token.read |
create-broadcast |
message (string), target_mode (string), user_ids ([]int64, opt.) |
— |
API: sticker and emoji actions
| Path |
Specific body fields |
Permission |
create-sticker-set |
multipart: metadata (JSON: common fields + title, short_name, kind, emoji, keywords) and a file file |
— |
rename-sticker-set |
set_id (int64), title (string) |
— |
add-sticker-to-set |
multipart: metadata (JSON: common fields + set_id (string), emoji, keywords) and a file file |
— |
remove-sticker-from-set |
set_id (int64), document_id (int64) |
— |
set-sticker-set-archived |
set_id (int64), archived (bool) |
— |
set-sticker-set-sort-order |
set_id (int64), sort_order (int) |
— |
delete-sticker-set |
set_id (int64) |
— |
API: GIF catalog actions
| Path |
Specific body fields |
Permission |
create-gif-catalog-entry |
multipart: metadata (JSON: common fields + title) and a file file |
— |
set-gif-catalog-enabled |
id (int64, sent as string), enabled (bool) |
— |
set-gif-catalog-sort-order |
id (int64, string), sort_order (int) |
— |
delete-gif-catalog-entry |
id (int64, string) |
— |
API: gift and collectible actions
| Path |
Specific body fields |
Permission |
import-gift |
multipart: metadata (JSON: common fields + gift_id (int64), title, stars (int64), convert_stars (int64), enabled (bool), sort_order (int), auction (bool), auction_slug, gifts_per_round (int), auction_start_date (int), auction_round_duration (int), availability_total (int), locked_until_date (int)) and a file file |
— |
import-official-gift |
common fields + source_gift_id (string), gift_id (int64), title, stars (int64), convert_stars (int64), enabled (bool), sort_order (int), include_collectible (bool), upgrade_stars (int64), supply_total (int), slug_prefix (string), locked_until_date (int) |
— |
publish-gift-collectibles |
multipart, gift_id sent in query (?gift_id=): metadata (JSON: common fields + upgrade_stars (int64), supply_total (int), slug_prefix (string), models ([]object), patterns ([]object), backdrops ([]object)); animations are files keyed by the models/patterns entries |
— |
set-gift-enabled |
gift_id (int64), enabled (bool) |
— |
set-gift-sort-order |
gift_id (int64), sort_order (int) |
— |
give-gift |
common fields + sender_user_id (int64), user_id (int64), channel_id (int64), gift_id (int64), hide_name (bool), message (string), upgrade (bool), model_attribute_id (int64), pattern_attribute_id (int64), backdrop_attribute_id (int64) |
— |
API: collectible username actions
| Path |
Specific body fields |
Permission |
mint-collectible-username |
username (string), owner_user_id (int64), owner_channel_id (int64), currency (string), amount (int64), crypto_currency (string), crypto_amount (int64), url (string), purchase_date (int/unix or date) |
— |
transfer-collectible-username |
username (string), to_user_id (int64), to_channel_id (int64) |
— |
revoke-collectible-username |
username (string), burn (bool) |
— |
delete-collectible-username |
username (string) |
— |
API: anonymous number actions
| Path |
Specific body fields |
Permission |
mint-collectible-phone |
phone (string), tier (string), owner_user_id (int64), currency (string), amount (int64), crypto_currency (string), crypto_amount (int64), url (string), purchase_date (int/unix or date) |
— |
update-collectible-phone-price |
phone (string), currency (string), amount (int64), crypto_currency (string), crypto_amount (int64) |
— |
transfer-collectible-phone |
phone (string), to_user_id (int64) |
— |
revoke-collectible-phone |
phone (string), burn (bool) |
— |
delete-collectible-phone |
phone (string) |
— |
API: account rating actions
| Path |
Specific body fields |
Permission |
recompute-account-rating |
user_id (int64) |
— |
adjust-account-rating |
user_id (int64), amount (int64) |
— |
API: message deletion
| Path |
Specific body fields |
Permission |
delete-messages |
owner_user_id (int64), peer_id (int64), ids ([]int), revoke (bool) |
— |
delete-history |
owner_user_id (int64), peer_id (int64), max_id (int), min_date (int), max_date (int), max_batches (int), just_clear (bool), revoke (bool) |
— |
API: verification decisions (request bodies)
Official verification — revoke-verification:
| Field |
Type |
Description |
target_type |
string |
Target type (user / channel, etc., validated by domain.VerificationTargetType.Valid()). |
target_id |
int64 |
Target identifier (not the application!). |
internal_note |
string |
Operator-only internal note (opt.). |
| + common fields |
|
command_id, reason, confirm. |
Third-party verification — verifier/mark actions (botverification.manage):
| Path |
Specific body fields |
grant-bot-verifier |
bot_id (int64), icon_document_id (int64), company_name (string, req.), default_description (string), can_modify_custom_description (bool), version (int64, 0 for new) |
set-bot-verifier-enabled |
bot_id (int64), enabled (bool) |
revoke-bot-verifier |
bot_id (int64) |
upsert-verification-icon |
document_id (int64), name (string, req.), owner_bot_id (int64, opt., 0 = shared) |
set-verification-icon-active |
icon_id (int64), active (bool) |
revoke-custom-verification |
verifier_bot_id (int64), peer_type (string, validated), peer_id (int64) |
All of these also carry the common fields (command_id, reason, confirm).