owpengram-server/docs/openapi.yaml
Astra c39011e542 admin: allow shorter operator-assigned usernames, add reference docs
Lower the username floor to 3 characters on the admin-only write path
(account/channel set-username), separate from the 5-character self-service
minimum, since a deliberately short operator handle isn't the squatting the
higher floor guards against. Channel admin username assignment previously
had no length/format validation at all; this adds it.

Also adds three reference docs: the client's deep-link (tg://, owpg://)
surface, an OpenAPI 3.1 spec for both admin HTTP APIs (built from source,
not the stale docs/admin-panel-api.en.md), and a starter reserved-username
blocklist.
2026-09-17 15:24:31 +01:00

3628 lines
165 KiB
YAML

openapi: 3.1.0
info:
title: owpengram-server Admin APIs
version: "1.1.0"
description: |
Two independently authenticated HTTP admin surfaces, run as **separate
listeners in the same process** (`cmd/telesrv-admin`):
1. **Admin API** (`internal/adminapi`) — a bearer-token JSON API under
`/v1/*`, listening on `TELESRV_ADMIN_API_ADDR`
(default `127.0.0.1:2599`). Closed unless configured
(`TELESRV_ADMIN_API_ADDR` empty ⇒ off). This is the raw write surface.
2. **Panel API** (`cmd/telesrv-admin`) — the cookie/CSRF-session JSON API
under `/api/*` behind the built admin SPA, listening on
`TELESRV_ADMIN_UI_ADDR` (default `127.0.0.1:2600`). Most reads go
straight to Postgres; every `/api/actions/*` mutation (plus
moderation and verification/bot-verification decisions) is relayed
through to the Admin API on your behalf, bearer-authenticated with
`TELESRV_ADMIN_API_TOKEN` — the panel itself only writes local state
directly for admin-operator accounts and server settings/identity.
Both APIs' `POST` routes return a **command result**
(`CommandResult` — shared by both surfaces, same Go type). Panel API
action routes wrap a relayed Admin API call, so most of them can also
surface a `502` if that upstream call itself fails — except the
verification/bot-verification decision routes, which preserve the
upstream's real status (404/409/429/400) instead of flattening to 502.
Panel API permissions are granular (`accounts.manage`,
`channels.read`, `content.manage`, …) — see each operation's
description for the exact permission it requires; `*` (only ever held
by the master/break-glass "owpengram" account or a token explicitly
granted it) satisfies every check.
Built entirely from reading source directly (no prose doc was used or
trusted for either surface — an earlier version of this spec was
generated from `docs/admin-panel-api.en.md` and found to document
several routes, like `grant-stars`, that don't exist, while missing
~40 real ones and understating the permission model; that doc should
be considered unreliable going forward):
`internal/adminapi/{server,rbac,verification,botverification}.go` and
`cmd/telesrv-admin/{server,security,session,adminauth,adminusers_api,
verification,botverification,serversettings}.go`.
contact:
name: owpengram-server
url: https://git.zio.sh/astra/owpengram
servers:
- url: http://127.0.0.1:2599
description: Admin API (internal/adminapi) — bearer token, TELESRV_ADMIN_API_ADDR
- url: http://127.0.0.1:2600
description: Panel API (cmd/telesrv-admin) — cookie session + CSRF, TELESRV_ADMIN_UI_ADDR
security: []
tags:
- { name: "Panel: Auth", description: "Panel API — login/logout/session/public branding" }
- { name: "Panel: Admin Operators", description: "Panel API — named admin_console_users accounts (requires admins.manage)" }
- { name: "Panel: Dashboard", description: "Panel API — dashboard summary (requires dashboard.read)" }
- { name: "Panel: Accounts", description: "Panel API — account browsing + actions (requires accounts.read / accounts.manage)" }
- { name: "Panel: Broadcasts", description: "Panel API — broadcast browsing + actions (requires broadcasts.read / broadcasts.send)" }
- { name: "Panel: Channels", description: "Panel API — channel/supergroup browsing + actions (requires channels.read / channels.manage)" }
- { name: "Panel: Bots", description: "Panel API — bot browsing + actions (requires bots.read / bots.manage / bots.token.read)" }
- { name: "Panel: Messages", description: "Panel API — message audit + deletion actions (requires messages.read / messages.manage)" }
- { name: "Panel: Content", description: "Panel API — custom emoji, sticker sets, GIF catalog (requires content.read / content.manage)" }
- { name: "Panel: Storage", description: "Panel API — storage stats + manual purge (requires storage.read / storage.manage)" }
- { name: "Panel: Moderation", description: "Panel API — moderation case queue, proxied to the Admin API (requires moderation.review)" }
- { name: "Panel: Usernames", description: "Panel API — reserved + collectible username lifecycle (requires usernames.read / usernames.manage)" }
- { name: "Panel: Official Verification", description: "Panel API — official verification queue (requires verification.review, revoke also needs verification.revoke)" }
- { name: "Panel: Bot Verification", description: "Panel API — third-party bot verification queue, hidden by default (requires botverification.review / botverification.manage)" }
- { name: "Panel: Server Settings", description: "Panel API — server identity, env, updates, restart (requires server.manage)" }
- { name: "AdminAPI: Accounts", description: "Admin API — account write actions" }
- { name: "AdminAPI: Channels", description: "Admin API — channel write actions" }
- { name: "AdminAPI: Bots & Broadcasts", description: "Admin API — bot/broadcast write actions" }
- { name: "AdminAPI: Messages", description: "Admin API — message deletion actions" }
- { name: "AdminAPI: Stickers", description: "Admin API — sticker set write actions" }
- { name: "AdminAPI: GIF Catalog", description: "Admin API — GIF catalog write actions" }
- { name: "AdminAPI: Storage", description: "Admin API — storage purge" }
- { name: "AdminAPI: Moderation", description: "Admin API — moderation case queue (raw)" }
- { name: "AdminAPI: Collectible Usernames", description: "Admin API — collectible username lifecycle" }
- { name: "AdminAPI: Reserved Usernames", description: "Admin API — operator username blocklist" }
- { name: "AdminAPI: Official Verification", description: "Admin API — official verification queue (raw)" }
- { name: "AdminAPI: Bot Verification", description: "Admin API — third-party bot verification queue (raw)" }
- { name: "AdminAPI: Health", description: "Admin API — unauthenticated health check" }
paths:
# ============================================================
# PANEL API (cmd/telesrv-admin) — cookie session + CSRF
# ============================================================
# --- Auth / session ---
/api/login:
post:
tags: ["Panel: Auth"]
summary: Log in
security: []
description: >-
Issues the signed telesrv_admin_session cookie and the
telesrv_admin_csrf cookie. The only mutating panel route without
CSRF (no session exists yet) — only the Origin header is checked.
`username` is always required: either the literal break-glass
username `"owpengram"` (checked against TELESRV_ADMIN_UI_PASSWORD/
_TOKEN — reserved, no DB account may take it), or a named account
in `admin_console_users`. Every failure mode (unknown user, wrong
password, disabled account) returns the same 401 with the same
generic message, and an unknown-user attempt still burns a dummy
bcrypt compare, so failure modes cost the same time.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [username, secret]
properties:
username: { type: string, description: "Break-glass username (\"owpengram\"), or a named admin_console_users account." }
secret: { type: string }
example: { username: "owpengram", secret: "letmein" }
responses:
"200": { description: OK, headers: { Set-Cookie: { schema: { type: string } } } }
"401": { $ref: "#/components/responses/PanelUnauthorized" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/logout:
post:
tags: ["Panel: Auth"]
summary: Log out
description: Requires an authenticated session; no specific permission.
security: [{ panelSession: [], panelCsrf: [] }]
responses:
"200": { description: OK }
/api/session:
get:
tags: ["Panel: Auth"]
summary: Current session info
description: Requires an authenticated session; no specific permission.
security: [{ panelSession: [] }]
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
actor: { type: string }
permissions: { type: array, items: { type: string } }
csrf_token: { type: string }
"401": { $ref: "#/components/responses/PanelUnauthorized" }
/api/public/branding:
get:
tags: ["Panel: Auth"]
summary: Public server branding (name/description), unauthenticated
description: Public route — shown on the login screen before signing in.
security: []
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
/api/public/icon:
get:
tags: ["Panel: Auth"]
summary: Public server icon, unauthenticated
security: []
responses:
"200": { description: Image bytes, content: { image/*: { schema: { type: string, format: binary } } } }
"404": { description: No icon set }
# --- Admin operator accounts (admins.manage) ---
/api/admin-users:
get:
tags: ["Panel: Admin Operators"]
summary: List admin operator accounts
description: Requires `admins.manage`.
security: [{ panelSession: [] }]
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
system: { type: object, description: "the break-glass account", properties: { username: { type: string, const: owpengram }, permissions: { type: array, items: { type: string } }, enabled: { type: boolean, const: true }, system: { type: boolean, const: true } } }
rows: { type: array, items: { type: object, additionalProperties: true } }
available_permissions: { type: array, items: { type: string } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/create-admin-operator:
post:
tags: ["Panel: Admin Operators"]
summary: Create a named admin operator account
description: Requires `admins.manage`. `reason` is mandatory (stricter than the generic action envelope).
security: [{ panelSession: [], panelCsrf: [] }]
requestBody: { $ref: "#/components/requestBodies/PanelAdminUserAction" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/set-admin-operator-access:
post:
tags: ["Panel: Admin Operators"]
summary: Edit a named admin operator's permissions/enabled state
description: Requires `admins.manage`. `reason` is mandatory.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody: { $ref: "#/components/requestBodies/PanelAdminUserAction" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/set-admin-operator-password:
post:
tags: ["Panel: Admin Operators"]
summary: Set a named admin operator's password
description: Requires `admins.manage`. `reason` is mandatory.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody: { $ref: "#/components/requestBodies/PanelAdminUserAction" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
# --- Dashboard ---
/api/dashboard:
get:
tags: ["Panel: Dashboard"]
summary: Dashboard summary
description: Requires `dashboard.read`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
# --- Accounts ---
/api/accounts:
get:
tags: ["Panel: Accounts"]
summary: List/search accounts
description: Requires `accounts.read`.
security: [{ panelSession: [] }]
parameters:
- { name: q, in: query, schema: { type: string } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_active_us, in: query, description: microseconds, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
query: { type: string }
limit: { type: integer }
rows: { type: array, items: { $ref: "#/components/schemas/AccountRow" } }
has_more: { type: boolean }
next_before_id: { type: integer, format: int64 }
next_before_active_us: { type: integer, format: int64 }
listing: { type: boolean }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/accounts/stats:
get:
tags: ["Panel: Accounts"]
summary: Account statistics
description: Requires `accounts.read`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/accounts/shared-devices:
get:
tags: ["Panel: Accounts"]
summary: Accounts grouped by shared device fingerprint
description: Requires `accounts.read`.
security: [{ panelSession: [] }]
parameters:
- { name: offset, in: query, schema: { type: integer } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
limit: { type: integer }
offset: { type: integer }
rows: { type: array, items: { type: object, additionalProperties: true } }
has_more: { type: boolean }
next_offset: { type: integer }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/accounts/{id}:
get:
tags: ["Panel: Accounts"]
summary: Account card (profile, flags, statistics)
description: Requires `accounts.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
"404": { $ref: "#/components/responses/PanelNotFound" }
/api/accounts/{id}/avatar:
get:
tags: ["Panel: Accounts"]
summary: Account avatar (binary, proxied from the Admin API)
description: Requires `accounts.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: Image bytes, content: { image/*: { schema: { type: string, format: binary } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
"404": { description: Not found }
/api/actions/set-frozen:
post:
tags: ["Panel: Accounts"]
summary: Freeze/unfreeze an account
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody: { $ref: "#/components/requestBodies/PanelSetFrozen" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/grant-premium:
post:
tags: ["Panel: Accounts"]
summary: Grant Premium months
description: Requires `premium.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id, months], properties: { user_id: { type: integer, format: int64 }, months: { type: integer } } }]
example: { command_id: "grant-premium-001", reason: "Incident compensation", confirm: true, user_id: 123456789, months: 12 }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-verified:
post:
tags: ["Panel: Accounts"]
summary: Set/clear the legacy verified badge on a user
description: >-
Requires `verification.review` (not `accounts.manage`) — this sets
the boolean verified flag directly, separate from the official
verification-applications workflow, but shares its permission.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id, verified], properties: { user_id: { type: integer, format: int64 }, verified: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-flags:
post:
tags: ["Panel: Accounts"]
summary: Set scam/fake flags on a user
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, scam: { type: boolean }, fake: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-support:
post:
tags: ["Panel: Accounts"]
summary: Set/clear the support flag on a user
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id, support], properties: { user_id: { type: integer, format: int64 }, support: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-username:
post:
tags: ["Panel: Accounts"]
summary: Set a user's username
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id, username], properties: { user_id: { type: integer, format: int64 }, username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-profile:
post:
tags: ["Panel: Accounts"]
summary: Set a user's first/last name
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, first_name: { type: string }, last_name: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-phone:
post:
tags: ["Panel: Accounts"]
summary: Set a user's phone number
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id, phone], properties: { user_id: { type: integer, format: int64 }, phone: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-login-email:
post:
tags: ["Panel: Accounts"]
summary: Set/clear a user's login email
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, email: { type: string, description: "empty clears it" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-avatar:
post:
tags: ["Panel: Accounts"]
summary: Set a user's avatar (static image)
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody: { $ref: "#/components/requestBodies/PanelUserAvatarUpload" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-avatar-video:
post:
tags: ["Panel: Accounts"]
summary: Set a user's avatar (animated video)
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata: { description: "JSON — common fields + user_id (int64), video_start_ts (float64)", type: string }
file: { type: string, format: binary }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-color:
post:
tags: ["Panel: Accounts"]
summary: Set a user's profile/name color
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/PeerColorInput" }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-account-emoji-status:
post:
tags: ["Panel: Accounts"]
summary: Set a user's emoji status
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/EmojiStatusInput" }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/revoke-sessions:
post:
tags: ["Panel: Accounts"]
summary: Revoke a user's authorized sessions
description: Requires `accounts.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, hash: { type: integer, format: int64 }, keep_hash: { type: integer, format: int64 }, revoke_all: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Broadcasts ---
/api/broadcasts:
get:
tags: ["Panel: Broadcasts"]
summary: List broadcasts
description: Requires `broadcasts.read`.
security: [{ panelSession: [] }]
parameters:
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties: { limit: { type: integer }, rows: { type: array, items: { type: object, additionalProperties: true } }, has_more: { type: boolean }, next_before_id: { type: integer, format: int64 } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/create-broadcast:
post:
tags: ["Panel: Broadcasts"]
summary: Create a broadcast
description: Requires `broadcasts.send`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [message, target_mode], properties: { message: { type: string }, target_mode: { type: string }, user_ids: { type: array, items: { type: integer, format: int64 }, description: "only for target_mode=selected" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Channels ---
/api/channels:
get:
tags: ["Panel: Channels"]
summary: List/search channels
description: Requires `channels.read`.
security: [{ panelSession: [] }]
parameters:
- { name: q, in: query, schema: { type: string } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_updated_us, in: query, description: microseconds, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200": { $ref: "#/components/responses/PanelCursorList" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/channels/{id}:
get:
tags: ["Panel: Channels"]
summary: Channel card
description: Requires `channels.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/channels/{id}/avatar:
get:
tags: ["Panel: Channels"]
summary: Channel avatar (binary, proxied from the Admin API)
description: Requires `channels.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: Image bytes, content: { image/*: { schema: { type: string, format: binary } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/set-channel-flags:
post:
tags: ["Panel: Channels"]
summary: Set scam/fake flags on a channel
description: Requires `channels.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [channel_id], properties: { channel_id: { type: integer, format: int64 }, scam: { type: boolean }, fake: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-channel-avatar:
post:
tags: ["Panel: Channels"]
summary: Set a channel's avatar
description: Requires `channels.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata: { description: "JSON — common fields + channel_id (int64)", type: string }
file: { type: string, format: binary }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-channel-settings:
post:
tags: ["Panel: Channels"]
summary: Partially update channel settings
description: >-
Requires `channels.manage`. Every field besides `channel_id` is an
optional pointer — a partial PATCH: only fields present in the JSON
body are changed.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [channel_id]
properties:
channel_id: { type: integer, format: int64 }
gigagroup: { type: [boolean, "null"] }
antispam: { type: [boolean, "null"] }
participants_hidden: { type: [boolean, "null"] }
noforwards: { type: [boolean, "null"] }
join_to_send: { type: [boolean, "null"] }
join_request: { type: [boolean, "null"] }
slowmode_seconds: { type: [integer, "null"] }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-channel-username:
post:
tags: ["Panel: Channels"]
summary: Set a channel's username
description: Requires `channels.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [channel_id, username], properties: { channel_id: { type: integer, format: int64 }, username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-channel-color:
post:
tags: ["Panel: Channels"]
summary: Set a channel's profile/name color
description: Requires `channels.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [channel_id], properties: { channel_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/PeerColorInput" }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-channel-emoji-status:
post:
tags: ["Panel: Channels"]
summary: Set a channel's emoji status
description: Requires `channels.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [channel_id], properties: { channel_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/EmojiStatusInput" }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-channel-verified:
post:
tags: ["Panel: Channels"]
summary: Set/clear the verified badge on a channel
description: Requires `verification.review` (not `channels.manage`).
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [channel_id, verified], properties: { channel_id: { type: integer, format: int64 }, verified: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Bots ---
/api/bots:
get:
tags: ["Panel: Bots"]
summary: List/search bots
description: Requires `bots.read`.
security: [{ panelSession: [] }]
parameters:
- { name: q, in: query, schema: { type: string } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200": { $ref: "#/components/responses/PanelCursorList" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/bots/{id}:
get:
tags: ["Panel: Bots"]
summary: Bot card
description: Requires `bots.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/create-bot:
post:
tags: ["Panel: Bots"]
summary: Create a bot account
description: Requires `bots.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [owner_user_id, name, username], properties: { owner_user_id: { type: integer, format: int64 }, name: { type: string }, username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/delete-bot:
post:
tags: ["Panel: Bots"]
summary: Delete a bot account
description: Requires `bots.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [bot_user_id], properties: { bot_user_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/export-bot-token:
post:
tags: ["Panel: Bots"]
summary: Export a bot's auth token
description: Requires `bots.token.read`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [bot_user_id], properties: { bot_user_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Messages ---
/api/messages:
get:
tags: ["Panel: Messages"]
summary: Private message audit
description: Requires `messages.read`. Only queries when both `owner_user_id` and `peer_id` are > 0.
security: [{ panelSession: [] }]
parameters:
- { name: owner_user_id, in: query, schema: { type: integer, format: int64 } }
- { name: peer_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_date, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties: { owner_user_id: { type: integer, format: int64 }, peer_id: { type: integer, format: int64 }, before_date: { type: integer, format: int64 }, before_id: { type: integer }, limit: { type: integer }, rows: { type: array, items: { type: object, additionalProperties: true } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/messages/detail:
get:
tags: ["Panel: Messages"]
summary: Private message detail
description: Requires `messages.read`. Both query params required.
security: [{ panelSession: [] }]
parameters:
- { name: owner_user_id, in: query, required: true, schema: { type: integer, format: int64 } }
- { name: msg_id, in: query, required: true, schema: { type: integer } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/messages/groups:
get:
tags: ["Panel: Messages"]
summary: Channel/group message audit
description: Requires `messages.read`.
security: [{ panelSession: [] }]
parameters:
- { name: channel_id, in: query, required: true, schema: { type: integer, format: int64 } }
- { name: before_date, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties: { channel_id: { type: integer, format: int64 }, before_date: { type: integer, format: int64 }, before_id: { type: integer }, limit: { type: integer }, rows: { type: array, items: { type: object, additionalProperties: true } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/messages/groups/detail:
get:
tags: ["Panel: Messages"]
summary: Channel/group message detail
description: Requires `messages.read`.
security: [{ panelSession: [] }]
parameters:
- { name: channel_id, in: query, required: true, schema: { type: integer, format: int64 } }
- { name: msg_id, in: query, required: true, schema: { type: integer } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/delete-messages:
post:
tags: ["Panel: Messages"]
summary: Delete specific private messages
description: Requires `messages.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [owner_user_id, peer_id, ids], properties: { owner_user_id: { type: integer, format: int64 }, peer_id: { type: integer, format: int64 }, ids: { type: array, items: { type: integer } }, revoke: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/delete-history:
post:
tags: ["Panel: Messages"]
summary: Delete a private chat's history
description: Requires `messages.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [owner_user_id, peer_id]
properties:
owner_user_id: { type: integer, format: int64 }
peer_id: { type: integer, format: int64 }
max_id: { type: integer }
min_date: { type: integer }
max_date: { type: integer }
max_batches: { type: integer }
just_clear: { type: boolean }
revoke: { type: boolean }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Content: emoji ---
/api/emoji:
get:
tags: ["Panel: Content"]
summary: List/search custom emoji
description: Requires `content.read`.
security: [{ panelSession: [] }]
parameters:
- { name: q, in: query, schema: { type: string } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
responses:
"200": { $ref: "#/components/responses/PanelCursorList" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/emoji/{id}/animation:
get:
tags: ["Panel: Content"]
summary: Emoji Lottie animation
description: Requires `content.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: Animation JSON bytes }
"403": { $ref: "#/components/responses/PanelForbidden" }
# --- Content: stickers ---
/api/stickers:
get:
tags: ["Panel: Content"]
summary: List sticker sets
description: Requires `content.read`.
security: [{ panelSession: [] }]
parameters:
- { name: kind, in: query, schema: { type: string } }
responses:
"200":
description: OK
content:
application/json:
schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } }, max_items: { type: integer } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/stickers/{id}/documents:
get:
tags: ["Panel: Content"]
summary: Document ids in a sticker set
description: Requires `content.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { document_ids: { type: array, items: { type: integer, format: int64 } } } } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/stickers/documents/{id}/animation:
get:
tags: ["Panel: Content"]
summary: Sticker animation
description: Requires `content.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: Animation bytes }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/set-sticker-set-archived:
post:
tags: ["Panel: Content"]
summary: Archive/unarchive a sticker set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [set_id, archived], properties: { set_id: { type: string, description: "int64, json:\",string\"" }, archived: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-sticker-set-sort-order:
post:
tags: ["Panel: Content"]
summary: Reorder a sticker set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [set_id, sort_order], properties: { set_id: { type: string, description: "int64, json:\",string\"" }, sort_order: { type: integer } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/rename-sticker-set:
post:
tags: ["Panel: Content"]
summary: Rename a sticker set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [set_id, title], properties: { set_id: { type: string, description: "int64, json:\",string\"" }, title: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/delete-sticker-set:
post:
tags: ["Panel: Content"]
summary: Delete a sticker set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [set_id], properties: { set_id: { type: string, description: "int64, json:\",string\"" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/create-sticker-set:
post:
tags: ["Panel: Content"]
summary: Create a sticker set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata: { description: "JSON — common fields + title, short_name, kind, emoji, keywords (opt.)", type: string }
file: { type: string, format: binary }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/add-sticker-to-set:
post:
tags: ["Panel: Content"]
summary: Add a sticker to a set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata: { description: "JSON — common fields + set_id (int64, json:\",string\"), emoji, keywords (opt.)", type: string }
file: { type: string, format: binary }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/remove-sticker-from-set:
post:
tags: ["Panel: Content"]
summary: Remove a sticker from a set
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [set_id, document_id], properties: { set_id: { type: string, description: "int64, json:\",string\"" }, document_id: { type: string, description: "int64, json:\",string\"" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Content: GIF catalog ---
/api/gif-catalog:
get:
tags: ["Panel: Content"]
summary: GIF catalog
description: Requires `content.read`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/gif-catalog/documents/{id}/preview:
get:
tags: ["Panel: Content"]
summary: GIF preview
description: Requires `content.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: Preview bytes }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/create-gif-catalog-entry:
post:
tags: ["Panel: Content"]
summary: Add a GIF catalog entry
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata: { description: "JSON — common fields + title", type: string }
file: { type: string, format: binary }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-gif-catalog-enabled:
post:
tags: ["Panel: Content"]
summary: Enable/disable a GIF catalog entry
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [id, enabled], properties: { id: { type: string, description: "int64, json:\",string\"" }, enabled: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-gif-catalog-sort-order:
post:
tags: ["Panel: Content"]
summary: Reorder a GIF catalog entry
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [id, sort_order], properties: { id: { type: string, description: "int64, json:\",string\"" }, sort_order: { type: integer } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-gif-catalog-category:
post:
tags: ["Panel: Content"]
summary: Set a GIF catalog entry's category
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [id, category], properties: { id: { type: string, description: "int64, json:\",string\"" }, category: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/auto-categorize-gif-catalog:
post:
tags: ["Panel: Content"]
summary: Auto-categorize uncategorized GIFs
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/PanelCommandFields" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/delete-uncategorized-gifs:
post:
tags: ["Panel: Content"]
summary: Delete every uncategorized GIF catalog entry
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/PanelCommandFields" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/delete-gif-catalog-entry:
post:
tags: ["Panel: Content"]
summary: Delete a GIF catalog entry
description: Requires `content.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [id], properties: { id: { type: string, description: "int64, json:\",string\"" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Storage ---
/api/storage/stats:
get:
tags: ["Panel: Storage"]
summary: Object storage statistics
description: Requires `storage.read`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/storage/accounts:
get:
tags: ["Panel: Storage"]
summary: Storage usage broken down by account
description: Requires `storage.read`.
security: [{ panelSession: [] }]
parameters:
- { name: offset, in: query, schema: { type: integer } }
- { $ref: "#/components/parameters/Limit" }
- { name: order, in: query, schema: { type: string, enum: [asc, desc] } }
- { name: q, in: query, schema: { type: string } }
- { name: sort, in: query, schema: { type: string } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/storage-manual-purge:
post:
tags: ["Panel: Storage"]
summary: Manually purge storage by category
description: Requires `storage.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [categories]
properties:
categories: { type: array, items: { type: string, enum: [photo, video, round_video, gif, music, voice, file] } }
include_avatars: { type: boolean }
created_before: { type: [string, "null"], format: date-time, description: "RFC3339; null = no age filter" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Moderation (proxied to the Admin API) ---
/api/moderation/cases:
get:
tags: ["Panel: Moderation"]
summary: List moderation cases
description: Requires `moderation.review`. Entire query string forwarded to the Admin API's `GET /v1/moderation/cases` unchanged.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/moderation/cases/{id}:
get:
tags: ["Panel: Moderation"]
summary: Moderation case detail
description: Requires `moderation.review`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/moderation/reports/{id}:
get:
tags: ["Panel: Moderation"]
summary: Moderation report detail
description: Requires `moderation.review`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/moderation/cases/{id}/claim:
post:
tags: ["Panel: Moderation"]
summary: Claim a moderation case
description: Requires `moderation.review`. Relayed to the Admin API — `actor` is injected from the session, `command_id` minted if absent.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/moderation/cases/{id}/decide:
post:
tags: ["Panel: Moderation"]
summary: Decide a moderation case
description: Requires `moderation.review`. Relayed to the Admin API's `POST /v1/moderation/cases/{id}/decide`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/moderation/cases/{id}/appeals/{appeal_id}/review:
post:
tags: ["Panel: Moderation"]
summary: Review an appeal on a moderation case
description: Requires `moderation.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters:
- { $ref: "#/components/parameters/PathId" }
- { name: appeal_id, in: path, required: true, schema: { type: integer, format: int64 } }
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
# --- Usernames: reserved + collectible ---
/api/collectible-usernames:
get:
tags: ["Panel: Usernames"]
summary: List collectible usernames
description: Requires `usernames.read`.
security: [{ panelSession: [] }]
parameters:
- { name: status, in: query, schema: { type: string, enum: ["", vault, owned, burned] } }
- { name: owner_user_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: q, in: query, schema: { type: string } }
responses:
"200":
description: "OK — `next_before_id` is a string (int64 precision)."
content:
application/json:
schema:
type: object
properties:
rows: { type: array, items: { type: object, additionalProperties: true } }
has_more: { type: boolean }
next_before_id: { type: string }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/collectible-usernames/{id}:
get:
tags: ["Panel: Usernames"]
summary: Collectible username detail + transfer log
description: Requires `usernames.read`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
asset: { type: object, additionalProperties: true }
transfers: { type: array, items: { type: object, additionalProperties: true } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/reserved-usernames:
get:
tags: ["Panel: Usernames"]
summary: List reserved usernames
description: Requires `usernames.read`. Proxied to the Admin API's `GET /v1/reserved-usernames`.
security: [{ panelSession: [] }]
parameters:
- { name: q, in: query, schema: { type: string } }
- { $ref: "#/components/parameters/Limit" }
- { name: offset, in: query, schema: { type: integer } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/reserve-username:
post:
tags: ["Panel: Usernames"]
summary: Add a username to the operator reserved-username blocklist
description: Requires `usernames.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [username], properties: { username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/unreserve-username:
post:
tags: ["Panel: Usernames"]
summary: Remove a username from the operator reserved-username blocklist
description: Requires `usernames.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [username], properties: { username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/mint-collectible-username:
post:
tags: ["Panel: Usernames"]
summary: Mint a collectible username
description: Requires `usernames.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [username, currency, amount]
properties:
username: { type: string }
owner_user_id: { $ref: "#/components/schemas/FlexInt64" }
owner_channel_id: { $ref: "#/components/schemas/FlexInt64" }
currency: { type: string }
amount: { $ref: "#/components/schemas/FlexInt64" }
crypto_currency: { type: string }
crypto_amount: { $ref: "#/components/schemas/FlexInt64" }
url: { type: string }
purchase_date: { $ref: "#/components/schemas/FlexUnix" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/transfer-collectible-username:
post:
tags: ["Panel: Usernames"]
summary: Transfer a collectible username
description: Requires `usernames.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [username]
properties:
username: { type: string }
to_user_id: { $ref: "#/components/schemas/FlexInt64" }
to_channel_id: { $ref: "#/components/schemas/FlexInt64" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/revoke-collectible-username:
post:
tags: ["Panel: Usernames"]
summary: Revoke (or burn) a collectible username
description: >-
Requires `usernames.manage`. When `burn: true`, the effective
action name in the command envelope flips to
`burn-collectible-username` (same route/struct).
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [username], properties: { username: { type: string }, burn: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/delete-collectible-username:
post:
tags: ["Panel: Usernames"]
summary: Delete a collectible username
description: Requires `usernames.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [username], properties: { username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Official verification ---
/api/verification/applications:
get:
tags: ["Panel: Official Verification"]
summary: List official-verification applications
description: Requires `verification.review`.
security: [{ panelSession: [] }]
parameters:
- { name: status, in: query, schema: { type: string } }
- { name: target_type, in: query, schema: { type: string } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: reviewer, in: query, schema: { type: string } }
- { name: q, in: query, schema: { type: string } }
responses:
"200": { $ref: "#/components/responses/PanelCursorList" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/verification/applications/{id}:
get:
tags: ["Panel: Official Verification"]
summary: Application detail
description: Requires `verification.review`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/verification/counts:
get:
tags: ["Panel: Official Verification"]
summary: Application counts by status
description: Requires `verification.review`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/verification/applications/{id}/claim:
post:
tags: ["Panel: Official Verification"]
summary: Claim an application
description: Requires `verification.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/verification/applications/{id}/approve:
post:
tags: ["Panel: Official Verification"]
summary: Approve an application (grants the badge)
description: Requires `verification.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/verification/applications/{id}/reject:
post:
tags: ["Panel: Official Verification"]
summary: Reject an application
description: Requires `verification.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/actions/revoke-verification:
post:
tags: ["Panel: Official Verification"]
summary: Strip an official-verification badge
description: Requires BOTH `verification.review` and `verification.revoke`. Addresses the target directly, not an application id.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [target_type, target_id]
properties:
target_type: { type: string, description: "domain.VerificationTargetType, validated" }
target_id: { $ref: "#/components/schemas/FlexInt64" }
internal_note: { type: string }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
# --- Bot verification (feature-flagged: 404s while TELESRV_HIDE_THIRD_PARTY_VERIFICATION=true, the default) ---
/api/botverification/verifiers:
get:
tags: ["Panel: Bot Verification"]
summary: List third-party verifier bots
description: Requires `botverification.review`.
security: [{ panelSession: [] }]
parameters:
- { $ref: "#/components/parameters/Limit" }
- { name: enabled_only, in: query, schema: { type: string, description: "1/true/yes/on" } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } } } } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
"404": { description: "Third-party verification is disabled (TELESRV_HIDE_THIRD_PARTY_VERIFICATION)" }
/api/botverification/icons:
get:
tags: ["Panel: Bot Verification"]
summary: List verification icon catalog
description: Requires `botverification.review`.
security: [{ panelSession: [] }]
parameters:
- { $ref: "#/components/parameters/Limit" }
- { name: active_only, in: query, schema: { type: string } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } } } } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/botverification/marks:
get:
tags: ["Panel: Bot Verification"]
summary: List active third-party marks
description: Requires `botverification.review`.
security: [{ panelSession: [] }]
parameters:
- { name: peer_type, in: query, schema: { type: string, enum: ["", user, channel] } }
- { name: verifier_bot_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: q, in: query, schema: { type: string } }
responses:
"200": { $ref: "#/components/responses/PanelCursorList" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/botverification/requests:
get:
tags: ["Panel: Bot Verification"]
summary: List third-party verification requests
description: Requires `botverification.review`.
security: [{ panelSession: [] }]
parameters:
- { name: status, in: query, schema: { type: string } }
- { name: peer_type, in: query, schema: { type: string } }
- { name: verifier_bot_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: q, in: query, schema: { type: string } }
responses:
"200": { $ref: "#/components/responses/PanelCursorList" }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/botverification/requests/{id}:
get:
tags: ["Panel: Bot Verification"]
summary: Request detail
description: Requires `botverification.review`.
security: [{ panelSession: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
request: { type: object, additionalProperties: true }
verifier: { type: object, additionalProperties: true }
mark_active: { type: boolean }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/botverification/counts:
get:
tags: ["Panel: Bot Verification"]
summary: Request counts by status
description: Requires `botverification.review`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/botverification/requests/{id}/approve:
post:
tags: ["Panel: Bot Verification"]
summary: Approve a third-party verification request
description: Requires `botverification.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/botverification/requests/{id}/reject:
post:
tags: ["Panel: Bot Verification"]
summary: Reject a third-party verification request
description: Requires `botverification.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/botverification/requests/{id}/revoke:
post:
tags: ["Panel: Bot Verification"]
summary: Revoke a granted third-party verification
description: Requires `botverification.review`.
security: [{ panelSession: [], panelCsrf: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/PanelVersionedDecision" }
responses:
"200": { $ref: "#/components/responses/PanelActionOK" }
"403": { $ref: "#/components/responses/PanelForbidden" }
"409": { $ref: "#/components/responses/PanelConflict" }
/api/actions/grant-bot-verifier:
post:
tags: ["Panel: Bot Verification"]
summary: Appoint a bot as a third-party verifier
description: Requires `botverification.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [bot_id, company_name]
properties:
bot_id: { $ref: "#/components/schemas/FlexInt64" }
icon_document_id: { $ref: "#/components/schemas/FlexInt64" }
company_name: { type: string }
default_description: { type: string }
can_modify_custom_description: { type: boolean }
version: { $ref: "#/components/schemas/FlexInt64" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-bot-verifier-enabled:
post:
tags: ["Panel: Bot Verification"]
summary: Enable/disable a verifier bot
description: Requires `botverification.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [bot_id, enabled], properties: { bot_id: { $ref: "#/components/schemas/FlexInt64" }, enabled: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/revoke-bot-verifier:
post:
tags: ["Panel: Bot Verification"]
summary: Strip a bot's verifier status
description: Requires `botverification.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [bot_id], properties: { bot_id: { $ref: "#/components/schemas/FlexInt64" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/upsert-verification-icon:
post:
tags: ["Panel: Bot Verification"]
summary: Add/edit a verification-icon catalog entry
description: Requires `botverification.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [document_id, name], properties: { document_id: { $ref: "#/components/schemas/FlexInt64" }, name: { type: string }, owner_bot_id: { $ref: "#/components/schemas/FlexInt64" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/set-verification-icon-active:
post:
tags: ["Panel: Bot Verification"]
summary: Enable/disable a verification icon
description: Requires `botverification.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [icon_id, active], properties: { icon_id: { $ref: "#/components/schemas/FlexInt64" }, active: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
/api/actions/revoke-custom-verification:
post:
tags: ["Panel: Bot Verification"]
summary: Strip a third-party mark from a peer
description: Requires `botverification.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [verifier_bot_id, peer_type, peer_id], properties: { verifier_bot_id: { $ref: "#/components/schemas/FlexInt64" }, peer_type: { type: string, enum: [user, channel] }, peer_id: { $ref: "#/components/schemas/FlexInt64" } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "502": { $ref: "#/components/responses/PanelActionUpstreamError" } }
# --- Server settings ---
/api/server/identity:
get:
tags: ["Panel: Server Settings"]
summary: Server identity (name/description/icon presence)
description: Requires `server.manage`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/server/add-server-link:
get:
tags: ["Panel: Server Settings"]
summary: The ready-made owpg://addserver link for this server
description: Requires `server.manage`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/server/icon:
get:
tags: ["Panel: Server Settings"]
summary: Current server icon (binary)
description: Requires `server.manage`.
security: [{ panelSession: [] }]
responses:
"200": { description: Image bytes, content: { image/*: { schema: { type: string, format: binary } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
"404": { description: No icon set }
/api/actions/set-server-identity:
post:
tags: ["Panel: Server Settings"]
summary: Set the server's name/description
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, properties: { name: { type: string }, description: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/actions/set-welcome-message-templates:
post:
tags: ["Panel: Server Settings"]
summary: Set the welcome (phone/email) message templates
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, properties: { phone_template: { type: string }, email_template: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/actions/set-login-code-message-template:
post:
tags: ["Panel: Server Settings"]
summary: Set the login-code SMS/message template
description: >-
Requires `server.manage`. The template must contain exactly one
`{{code}}` placeholder if non-empty, else 422
(`domain.ValidateLoginCodeMessageTemplate`).
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, properties: { template: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" }, "422": { description: "template must contain exactly one {{code}}", content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } } }
/api/actions/upload-server-icon:
post:
tags: ["Panel: Server Settings"]
summary: Upload a new server icon
description: "Requires `server.manage`. File ≤ 2MiB, extension in {.png,.jpg,.jpeg,.webp,.gif}."
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata: { description: "JSON — common fields only", type: string }
file: { type: string, format: binary }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/actions/remove-server-icon:
post:
tags: ["Panel: Server Settings"]
summary: Remove the server icon
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/PanelCommandFields" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/actions/complete-setup:
post:
tags: ["Panel: Server Settings"]
summary: Mark first-run setup as complete
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/PanelCommandFields" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/server/env:
get:
tags: ["Panel: Server Settings"]
summary: Current .env values
description: Requires `server.manage`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/update-server-env:
post:
tags: ["Panel: Server Settings"]
summary: Update .env values
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/PanelCommandFields" }, { type: object, required: [values], properties: { values: { type: object, additionalProperties: { type: string } } } }]
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/server/status:
get:
tags: ["Panel: Server Settings"]
summary: Server process status
description: Requires `server.manage`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/server/docker-status:
get:
tags: ["Panel: Server Settings"]
summary: Live container/process status (Services tab)
description: >-
Requires `server.manage`. Backed by `procctl.Manager.DockerStatus`,
which shells out to `docker compose ps`. If the deployment actually
runs under podman (or `docker` isn't installed/running), this exec
fails and the tab renders as "nothing running" rather than a clear
error — the handler doesn't currently distinguish "no services
configured" from "couldn't ask Docker".
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/server/check-updates:
get:
tags: ["Panel: Server Settings"]
summary: Check for available server updates
description: Requires `server.manage`.
security: [{ panelSession: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"403": { $ref: "#/components/responses/PanelForbidden" }
/api/actions/restart-server:
post:
tags: ["Panel: Server Settings"]
summary: Restart the server process
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/PanelCommandFields" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
/api/actions/update-server:
post:
tags: ["Panel: Server Settings"]
summary: Update the server to the latest build
description: Requires `server.manage`.
security: [{ panelSession: [], panelCsrf: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/PanelCommandFields" }
responses: { "200": { $ref: "#/components/responses/PanelActionOK" }, "403": { $ref: "#/components/responses/PanelForbidden" } }
# ============================================================
# ADMIN API (internal/adminapi) — bearer token
# ============================================================
/healthz:
get:
tags: ["AdminAPI: Health"]
summary: Health check
security: []
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { status: { type: string, const: ok } } } } } }
/v1/accounts/set-frozen:
post:
tags: ["AdminAPI: Accounts"]
summary: Freeze/unfreeze an account
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [user_id, frozen]
properties:
user_id: { type: integer, format: int64 }
frozen: { type: boolean }
freeze_until: { type: string, format: date-time }
freeze_appeal_url: { type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "400": { $ref: "#/components/responses/AdminApiCommandError" } }
/v1/accounts/set-restricted:
post:
tags: ["AdminAPI: Accounts"]
summary: Restrict/unrestrict an account (spam sanction — join/message gate)
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [user_id, restricted]
properties:
user_id: { type: integer, format: int64 }
restricted: { type: boolean }
restricted_until: { type: string, format: date-time }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "400": { $ref: "#/components/responses/AdminApiCommandError" } }
/v1/accounts/{id}/avatar:
get:
tags: ["AdminAPI: Accounts"]
summary: Account avatar (binary)
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: Image bytes, content: { image/*: { schema: { type: string, format: binary } } } }
"404": { description: Not found }
/v1/accounts/grant-premium:
post:
tags: ["AdminAPI: Accounts"]
summary: Grant Premium months
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id, months], properties: { user_id: { type: integer, format: int64 }, months: { type: integer } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-verified:
post:
tags: ["AdminAPI: Accounts"]
summary: Set/clear verified badge
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id, verified], properties: { user_id: { type: integer, format: int64 }, verified: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-flags:
post:
tags: ["AdminAPI: Accounts"]
summary: Set scam/fake flags
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, scam: { type: boolean }, fake: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-support:
post:
tags: ["AdminAPI: Accounts"]
summary: Set/clear support flag
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id, support], properties: { user_id: { type: integer, format: int64 }, support: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-username:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's username
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id, username], properties: { user_id: { type: integer, format: int64 }, username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-profile:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's first/last name
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, first_name: { type: string }, last_name: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-phone:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's phone
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id, phone], properties: { user_id: { type: integer, format: int64 }, phone: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-login-email:
post:
tags: ["AdminAPI: Accounts"]
summary: Set/clear a user's login email
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, email: { type: string, description: "empty clears it" } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-avatar:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's avatar (static image)
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, metadata]
properties:
file: { type: string, format: binary }
metadata: { description: "JSON — CommandMeta + user_id (int64)", type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-avatar-video:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's avatar (animated video)
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, metadata]
properties:
file: { type: string, format: binary }
metadata: { description: "JSON — CommandMeta + user_id (int64), video_start_ts (float64)", type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-color:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's profile/name color
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/PeerColorInput" }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/set-emoji-status:
post:
tags: ["AdminAPI: Accounts"]
summary: Set a user's emoji status
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/EmojiStatusInput" }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/accounts/revoke-sessions:
post:
tags: ["AdminAPI: Accounts"]
summary: Revoke a user's authorized sessions
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [user_id], properties: { user_id: { type: integer, format: int64 }, hash: { type: integer, format: int64 }, keep_hash: { type: integer, format: int64 }, revoke_all: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/{id}/avatar:
get:
tags: ["AdminAPI: Channels"]
summary: Channel avatar (binary)
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses: { "200": { description: Image bytes, content: { image/*: { schema: { type: string, format: binary } } } }, "404": { description: Not found } }
/v1/channels/set-avatar:
post:
tags: ["AdminAPI: Channels"]
summary: Set a channel's avatar
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, metadata]
properties:
file: { type: string, format: binary }
metadata: { description: "JSON — CommandMeta + channel_id (int64)", type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/set-verified:
post:
tags: ["AdminAPI: Channels"]
summary: Set/clear verified badge
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [channel_id, verified], properties: { channel_id: { type: integer, format: int64 }, verified: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/set-flags:
post:
tags: ["AdminAPI: Channels"]
summary: Set scam/fake flags
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [channel_id], properties: { channel_id: { type: integer, format: int64 }, scam: { type: boolean }, fake: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/set-settings:
post:
tags: ["AdminAPI: Channels"]
summary: Partially update channel settings
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [channel_id]
properties:
channel_id: { type: integer, format: int64 }
gigagroup: { type: [boolean, "null"] }
antispam: { type: [boolean, "null"] }
participants_hidden: { type: [boolean, "null"] }
noforwards: { type: [boolean, "null"] }
join_to_send: { type: [boolean, "null"] }
join_request: { type: [boolean, "null"] }
slowmode_seconds: { type: [integer, "null"] }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/set-username:
post:
tags: ["AdminAPI: Channels"]
summary: Set a channel's username
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [channel_id, username], properties: { channel_id: { type: integer, format: int64 }, username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/set-color:
post:
tags: ["AdminAPI: Channels"]
summary: Set a channel's profile/name color
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [channel_id], properties: { channel_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/PeerColorInput" }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/channels/set-emoji-status:
post:
tags: ["AdminAPI: Channels"]
summary: Set a channel's emoji status
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [channel_id], properties: { channel_id: { type: integer, format: int64 } } }, { $ref: "#/components/schemas/EmojiStatusInput" }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/bots/create:
post:
tags: ["AdminAPI: Bots & Broadcasts"]
summary: Create a bot account
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [owner_user_id, name, username], properties: { owner_user_id: { type: integer, format: int64 }, name: { type: string }, username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/broadcasts/create:
post:
tags: ["AdminAPI: Bots & Broadcasts"]
summary: Create a broadcast
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [message, target_mode], properties: { message: { type: string }, target_mode: { type: string }, user_ids: { type: array, items: { type: integer, format: int64 } } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/bots/delete:
post:
tags: ["AdminAPI: Bots & Broadcasts"]
summary: Delete a bot account
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [bot_user_id], properties: { bot_user_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/bots/export-token:
post:
tags: ["AdminAPI: Bots & Broadcasts"]
summary: Export a bot's auth token
description: Requires scoped permission `bots.token.read` (or master token).
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [bot_user_id], properties: { bot_user_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/messages/delete:
post:
tags: ["AdminAPI: Messages"]
summary: Delete specific private messages
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [owner_user_id, peer, ids]
properties:
owner_user_id: { type: integer, format: int64 }
peer: { $ref: "#/components/schemas/Peer" }
ids: { type: array, items: { type: integer } }
revoke: { type: boolean }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/messages/delete-history:
post:
tags: ["AdminAPI: Messages"]
summary: Delete a private chat's history
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [owner_user_id, peer]
properties:
owner_user_id: { type: integer, format: int64 }
peer: { $ref: "#/components/schemas/Peer" }
max_id: { type: integer }
min_date: { type: integer }
max_date: { type: integer }
just_clear: { type: boolean }
revoke: { type: boolean }
max_batches: { type: integer }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/set-archived:
post:
tags: ["AdminAPI: Stickers"]
summary: Archive/unarchive a sticker set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [set_id, archived], properties: { set_id: { type: integer, format: int64 }, archived: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/set-sort-order:
post:
tags: ["AdminAPI: Stickers"]
summary: Reorder a sticker set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [set_id, sort_order], properties: { set_id: { type: integer, format: int64 }, sort_order: { type: integer } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/rename:
post:
tags: ["AdminAPI: Stickers"]
summary: Rename a sticker set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [set_id, title], properties: { set_id: { type: integer, format: int64 }, title: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/delete:
post:
tags: ["AdminAPI: Stickers"]
summary: Delete a sticker set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [set_id], properties: { set_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/create:
post:
tags: ["AdminAPI: Stickers"]
summary: Create a sticker set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, metadata]
properties:
file: { type: string, format: binary }
metadata: { description: "JSON — CommandMeta + title, short_name, kind, emoji, keywords (opt.)", type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/add:
post:
tags: ["AdminAPI: Stickers"]
summary: Add a sticker to a set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, metadata]
properties:
file: { type: string, format: binary }
metadata: { description: "JSON — CommandMeta + set_id (int64), emoji, keywords (opt.)", type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/remove:
post:
tags: ["AdminAPI: Stickers"]
summary: Remove a sticker from a set
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [set_id, document_id], properties: { set_id: { type: integer, format: int64 }, document_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/stickers/documents/{id}/animation:
get:
tags: ["AdminAPI: Stickers"]
summary: Sticker animation
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses: { "200": { description: Animation bytes }, "400": { $ref: "#/components/responses/AdminApiCodedError" }, "404": { $ref: "#/components/responses/AdminApiCodedError" } }
/v1/gif-catalog/create:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Add a GIF catalog entry
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, metadata]
properties:
file: { type: string, format: binary }
metadata: { description: "JSON — CommandMeta + title, content_sha256 (opt.)", type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/set-enabled:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Enable/disable a GIF catalog entry
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [id, enabled], properties: { id: { type: integer, format: int64 }, enabled: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/set-sort-order:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Reorder a GIF catalog entry
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [id, sort_order], properties: { id: { type: integer, format: int64 }, sort_order: { type: integer } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/set-category:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Set a GIF catalog entry's category
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [id, category], properties: { id: { type: integer, format: int64 }, category: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/auto-categorize:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Auto-categorize uncategorized GIFs
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CommandMeta" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/delete-uncategorized:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Delete every uncategorized GIF catalog entry
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CommandMeta" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/delete:
post:
tags: ["AdminAPI: GIF Catalog"]
summary: Delete a GIF catalog entry
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [id], properties: { id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/gif-catalog/documents/{id}/preview:
get:
tags: ["AdminAPI: GIF Catalog"]
summary: GIF preview
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses: { "200": { description: Preview bytes }, "400": { $ref: "#/components/responses/AdminApiCodedError" }, "404": { $ref: "#/components/responses/AdminApiCodedError" } }
/v1/emoji/{id}/animation:
get:
tags: ["AdminAPI: Health"]
summary: Custom emoji Lottie animation
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses: { "200": { description: "Animation JSON bytes (Content-Type forced to application/json)" }, "400": { $ref: "#/components/responses/AdminApiCodedError" }, "404": { $ref: "#/components/responses/AdminApiCodedError" } }
/v1/storage/manual-purge:
post:
tags: ["AdminAPI: Storage"]
summary: Manually purge storage by category
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [categories]
properties:
categories:
type: array
items: { type: string, enum: [photo, video, round_video, gif, music, voice, file] }
include_avatars: { type: boolean }
created_before: { type: [string, "null"], format: date-time, description: "RFC3339; null = no age filter" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/moderation/cases:
get:
tags: ["AdminAPI: Moderation"]
summary: List moderation cases
security: [{ adminApiBearer: [] }]
parameters:
- { name: limit, in: query, schema: { type: integer, default: 50 } }
- { name: assigned_to, in: query, schema: { type: string } }
- { name: statuses, in: query, description: comma-separated, schema: { type: string } }
- { name: target_type, in: query, schema: { type: string } }
- { name: target_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_updated_at, in: query, schema: { type: string, format: date-time } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { cases: { type: array, items: { type: object, additionalProperties: true } } } } } } }
/v1/moderation/cases/{id}:
get:
tags: ["AdminAPI: Moderation"]
summary: Moderation case detail
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: "domain.ModerationCaseDetail (Decisions/Actions/Appeals nil-normalized to [])", content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"404": { $ref: "#/components/responses/AdminApiModerationError" }
/v1/moderation/reports/{id}:
get:
tags: ["AdminAPI: Moderation"]
summary: Moderation report detail
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200": { description: "domain.ModerationReport", content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"404": { $ref: "#/components/responses/AdminApiModerationError" }
/v1/moderation/cases/{id}/claim:
post:
tags: ["AdminAPI: Moderation"]
summary: Claim a moderation case
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [expected_version, actor]
properties: { expected_version: { type: integer, format: int64 }, actor: { type: string } }
responses:
"200": { description: "domain.ModerationCase", content: { application/json: { schema: { type: object, additionalProperties: true } } } }
"409": { $ref: "#/components/responses/AdminApiModerationError" }
/v1/moderation/cases/{id}/decide:
post:
tags: ["AdminAPI: Moderation"]
summary: Decide a moderation case
description: >-
`kind` = domain.ModerationDecisionKind. `actions[].kind` includes
`restrict_account`/`unrestrict_account` (the account spam-restriction
sanction), `freeze_account`/`unfreeze_account`, `mark_scam`,
`mark_fake`, `clear_peer_flags`, `delete_private_message`,
`delete_channel_message`, `delete_account`.
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [expected_version, actor, reason, kind]
properties:
expected_version: { type: integer, format: int64 }
actor: { type: string }
reason: { type: string }
command_id: { type: string }
kind: { type: string, description: "domain.ModerationDecisionKind" }
actions:
type: array
items:
type: object
properties:
kind: { type: string, description: "domain.ModerationActionKind, e.g. restrict_account" }
payload: { type: object, additionalProperties: true }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { created: { type: boolean }, case: { type: object, additionalProperties: true } } } } } }
"400": { $ref: "#/components/responses/AdminApiModerationError" }
"409": { $ref: "#/components/responses/AdminApiModerationError" }
/v1/moderation/cases/{id}/appeals:
post:
tags: ["AdminAPI: Moderation"]
summary: Submit an appeal on a moderation case
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [appellant_user_id, text]
properties: { appellant_user_id: { type: integer, format: int64 }, text: { type: string } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { created: { type: boolean }, appeal: { type: object, additionalProperties: true } } } } } }
/v1/moderation/cases/{id}/appeals/{appeal_id}/review:
post:
tags: ["AdminAPI: Moderation"]
summary: Review an appeal
security: [{ adminApiBearer: [] }]
parameters:
- { $ref: "#/components/parameters/PathId" }
- { name: appeal_id, in: path, required: true, schema: { type: integer, format: int64 } }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [expected_version, actor, reason, granted]
properties:
expected_version: { type: integer, format: int64 }
actor: { type: string }
reason: { type: string }
command_id: { type: string }
granted: { type: boolean, description: "maps to ModerationDecisionAppealGrant/Deny" }
actions:
type: array
items: { type: object, properties: { kind: { type: string }, payload: { type: object, additionalProperties: true } } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { created: { type: boolean }, case: { type: object, additionalProperties: true } } } } } }
"409": { $ref: "#/components/responses/AdminApiModerationError" }
/v1/collectible-usernames/mint:
post:
tags: ["AdminAPI: Collectible Usernames"]
summary: Mint a collectible username
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [username, currency, amount]
properties:
username: { type: string }
owner_user_id: { type: string, description: "int64 as decimal string; at most one owner_*" }
owner_channel_id: { type: string, description: "int64 as decimal string" }
currency: { type: string }
amount: { type: string, description: "int64 as decimal string" }
crypto_currency: { type: string }
crypto_amount: { type: string, description: "int64 as decimal string" }
url: { type: string }
purchase_date: { type: integer, format: int64, description: "unix seconds" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/collectible-usernames/transfer:
post:
tags: ["AdminAPI: Collectible Usernames"]
summary: Transfer a collectible username
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [username], properties: { username: { type: string }, to_user_id: { type: string, description: "int64 as decimal string" }, to_channel_id: { type: string, description: "int64 as decimal string" } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/collectible-usernames/revoke:
post:
tags: ["AdminAPI: Collectible Usernames"]
summary: Revoke (or burn) a collectible username
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [username], properties: { username: { type: string }, expected_owner_user_id: { type: string, description: "int64 as decimal string" }, burn: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/collectible-usernames/delete:
post:
tags: ["AdminAPI: Collectible Usernames"]
summary: Delete a collectible username
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [username], properties: { username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/collectible-usernames:
get:
tags: ["AdminAPI: Collectible Usernames"]
summary: List collectible usernames
description: All int64 ids/amounts in the response are decimal strings, to avoid float64 precision loss.
security: [{ adminApiBearer: [] }]
parameters:
- { name: status, in: query, schema: { type: string } }
- { name: q, in: query, schema: { type: string } }
- { name: owner_user_id, in: query, schema: { type: integer, format: int64 } }
- { name: owner_channel_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { assets: { type: array, items: { type: object, additionalProperties: true } } } } } } }
"400": { $ref: "#/components/responses/AdminApiCollectibleError" }
/v1/collectible-usernames/{id}:
get:
tags: ["AdminAPI: Collectible Usernames"]
summary: Collectible username detail + transfer log
security: [{ adminApiBearer: [] }]
parameters:
- { $ref: "#/components/parameters/PathId" }
- { $ref: "#/components/parameters/Limit" }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
asset: { type: object, additionalProperties: true }
transfers: { type: array, items: { type: object, additionalProperties: true } }
"404": { $ref: "#/components/responses/AdminApiCollectibleError" }
/v1/reserved-usernames/reserve:
post:
tags: ["AdminAPI: Reserved Usernames"]
summary: Add a username to the operator reserved-username blocklist
description: >-
`Username` is sent with a **capitalized, untagged** JSON key
(`{"Username": "..."}`, not `username`) — a source-level quirk
(missing `json:` struct tag), not a spec error. Every other request
body in this document uses lowercase `snake_case` keys.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [Username], properties: { Username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/reserved-usernames/unreserve:
post:
tags: ["AdminAPI: Reserved Usernames"]
summary: Remove a username from the operator reserved-username blocklist
description: Same `Username` (capitalized, untagged) quirk as `/v1/reserved-usernames/reserve`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [Username], properties: { Username: { type: string } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" } }
/v1/reserved-usernames:
get:
tags: ["AdminAPI: Reserved Usernames"]
summary: List reserved usernames
security: [{ adminApiBearer: [] }]
parameters:
- { name: q, in: query, schema: { type: string } }
- { $ref: "#/components/parameters/Limit" }
- { name: offset, in: query, schema: { type: integer } }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
reserved:
type: array
items:
type: object
properties:
username: { type: string }
reason: { type: string }
actor: { type: string }
created_at: { type: integer, format: int64, description: unix seconds }
"500": { description: "list failed", content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } } }
/v1/verification/applications:
get:
tags: ["AdminAPI: Official Verification"]
summary: List official-verification applications
security: [{ adminApiBearer: [] }]
parameters:
- { name: target_type, in: query, schema: { type: string } }
- { name: reviewer, in: query, schema: { type: string } }
- { name: q, in: query, schema: { type: string } }
- { name: status, in: query, description: comma-separated, schema: { type: string } }
- { $ref: "#/components/parameters/Limit" }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { applications: { type: array, items: { type: object, additionalProperties: true } } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/verification/applications/{id}:
get:
tags: ["AdminAPI: Official Verification"]
summary: Application detail
security: [{ adminApiBearer: [] }]
parameters:
- { $ref: "#/components/parameters/PathId" }
- { name: limit, in: query, description: events, schema: { type: integer } }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
application: { type: object, additionalProperties: true }
events: { type: array, items: { type: object, additionalProperties: true } }
target: { type: object, additionalProperties: true }
target_error: { type: string }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/verification/counts:
get:
tags: ["AdminAPI: Official Verification"]
summary: Application counts by status
security: [{ adminApiBearer: [] }]
responses:
"200": { description: OK, content: { application/json: { schema: { type: object, properties: { counts: { type: object, additionalProperties: { type: string } } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/verification/applications/{id}/claim:
post:
tags: ["AdminAPI: Official Verification"]
summary: Claim an application
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/AdminApiVersionedNote" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" }, "409": { $ref: "#/components/responses/AdminApiVerificationError" } }
/v1/verification/applications/{id}/approve:
post:
tags: ["AdminAPI: Official Verification"]
summary: Approve an application (grants the badge)
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/AdminApiVersionedNote" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" }, "409": { $ref: "#/components/responses/AdminApiVerificationError" } }
/v1/verification/applications/{id}/reject:
post:
tags: ["AdminAPI: Official Verification"]
summary: Reject an application
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/AdminApiVersionedNote" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" }, "409": { $ref: "#/components/responses/AdminApiVerificationError" } }
/v1/verification/revoke:
post:
tags: ["AdminAPI: Official Verification"]
summary: Strip an official-verification badge
description: Requires BOTH `verification.review` and `verification.revoke` (or master token). Addresses the target directly, not an application id.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [target_type, target_id]
properties:
target_type: { type: string, description: "domain.VerificationTargetType" }
target_id: { type: integer, format: int64 }
internal_note: { type: string }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/botverification/verifiers:
get:
tags: ["AdminAPI: Bot Verification"]
summary: List third-party verifier bots
security: [{ adminApiBearer: [] }]
parameters:
- { $ref: "#/components/parameters/Limit" }
- { name: enabled_only, in: query, schema: { type: string, description: "1/true/yes/on" } }
responses:
"200": { description: "PascalCase fields: BotID, IconDocumentID, CompanyName, ... (id/version fields as decimal strings)", content: { application/json: { schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/botverification/icons:
get:
tags: ["AdminAPI: Bot Verification"]
summary: List verification icon catalog
security: [{ adminApiBearer: [] }]
parameters:
- { $ref: "#/components/parameters/Limit" }
- { name: active_only, in: query, schema: { type: string } }
responses:
"200": { description: "PascalCase fields: ID, DocumentID, OwnerBotID, Name, Active, ...", content: { application/json: { schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/botverification/marks:
get:
tags: ["AdminAPI: Bot Verification"]
summary: List active third-party marks
security: [{ adminApiBearer: [] }]
parameters:
- { name: peer_type, in: query, schema: { type: string, enum: [user, channel] } }
- { name: verifier_bot_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: q, in: query, schema: { type: string } }
responses:
"200": { description: "PascalCase fields: ID, VerifierBotID, PeerType, PeerID, ...", content: { application/json: { schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } }, has_more: { type: boolean }, next_before_id: { type: string } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/botverification/requests:
get:
tags: ["AdminAPI: Bot Verification"]
summary: List third-party verification requests
security: [{ adminApiBearer: [] }]
parameters:
- { name: peer_type, in: query, schema: { type: string } }
- { name: status, in: query, description: comma-separated, schema: { type: string } }
- { name: verifier_bot_id, in: query, schema: { type: integer, format: int64 } }
- { name: before_id, in: query, schema: { type: integer, format: int64 } }
- { $ref: "#/components/parameters/Limit" }
- { name: q, in: query, schema: { type: string } }
responses:
"200": { description: "PascalCase fields incl. ApplicantUserID, PeerTitle/Username, Reason, Status, InternalNote, ...", content: { application/json: { schema: { type: object, properties: { rows: { type: array, items: { type: object, additionalProperties: true } }, has_more: { type: boolean }, next_before_id: { type: string } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/botverification/requests/{id}:
get:
tags: ["AdminAPI: Bot Verification"]
summary: Request detail
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
request: { type: object, additionalProperties: true }
verifier: { type: object, additionalProperties: true, description: "present even if verifier is gone" }
verifier_error: { type: string }
mark_active: { type: boolean }
mark_error: { type: string }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
"404": { $ref: "#/components/responses/AdminApiBotVerificationError" }
/v1/botverification/counts:
get:
tags: ["AdminAPI: Bot Verification"]
summary: Request counts by status
security: [{ adminApiBearer: [] }]
responses:
"200": { description: "counts for pending/approved/rejected/revoked, zero-filled", content: { application/json: { schema: { type: object, properties: { counts: { type: object, additionalProperties: { type: string } } } } } } }
"403": { $ref: "#/components/responses/AdminApiForbidden" }
/v1/botverification/requests/{id}/approve:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Approve a third-party verification request
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/AdminApiVersionedNote" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" }, "409": { $ref: "#/components/responses/AdminApiBotVerificationError" } }
/v1/botverification/requests/{id}/reject:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Reject a third-party verification request
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/AdminApiVersionedNote" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" }, "409": { $ref: "#/components/responses/AdminApiBotVerificationError" } }
/v1/botverification/requests/{id}/revoke:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Revoke a granted third-party verification
security: [{ adminApiBearer: [] }]
parameters: [{ $ref: "#/components/parameters/PathId" }]
requestBody: { $ref: "#/components/requestBodies/AdminApiVersionedNote" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" }, "409": { $ref: "#/components/responses/AdminApiBotVerificationError" } }
/v1/botverification/verifiers/grant:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Appoint a bot as a third-party verifier
description: Requires `botverification.manage`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [bot_id, company_name, version]
properties:
bot_id: { type: integer, format: int64 }
icon_document_id: { type: integer, format: int64 }
company_name: { type: string }
default_description: { type: string }
can_modify_custom_description: { type: boolean }
version: { type: integer, format: int64, description: "0 = new grant" }
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/botverification/verifiers/set-enabled:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Enable/disable a verifier bot
description: Requires `botverification.manage`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [bot_id, enabled], properties: { bot_id: { type: integer, format: int64 }, enabled: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/botverification/verifiers/revoke:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Strip a bot's verifier status
description: Requires `botverification.manage`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [bot_id], properties: { bot_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/botverification/icons/upsert:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Add/edit a verification-icon catalog entry
description: Requires `botverification.manage`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [document_id, name], properties: { document_id: { type: integer, format: int64 }, name: { type: string }, owner_bot_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/botverification/icons/set-active:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Enable/disable a verification icon
description: Requires `botverification.manage`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [icon_id, active], properties: { icon_id: { type: integer, format: int64 }, active: { type: boolean } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
/v1/botverification/marks/revoke:
post:
tags: ["AdminAPI: Bot Verification"]
summary: Strip a third-party mark from a peer
description: Requires `botverification.manage`.
security: [{ adminApiBearer: [] }]
requestBody:
required: true
content:
application/json:
schema:
allOf: [{ $ref: "#/components/schemas/CommandMeta" }, { type: object, required: [verifier_bot_id, peer_type, peer_id], properties: { verifier_bot_id: { type: integer, format: int64 }, peer_type: { type: string, enum: [user, channel] }, peer_id: { type: integer, format: int64 } } }]
responses: { "200": { $ref: "#/components/responses/AdminApiCommandOK" }, "403": { $ref: "#/components/responses/AdminApiForbidden" } }
components:
securitySchemes:
panelSession:
type: apiKey
in: cookie
name: telesrv_admin_session
description: Signed session cookie issued by POST /api/login. 12h default TTL, no server-side store.
panelCsrf:
type: apiKey
in: header
name: X-CSRF-Token
description: >-
Must match the telesrv_admin_csrf cookie's value. Required on every
mutating panel request except POST /api/login. Origin header is also
checked when present.
adminApiBearer:
type: http
scheme: bearer
description: >-
The master token (TELESRV_ADMIN_API_TOKEN, implicit "*" permission)
or a scoped token (TELESRV_ADMIN_SCOPED_TOKENS) carrying specific
permissions. Compared with subtle.ConstantTimeCompare against every
configured token unconditionally, to avoid timing side-channels.
parameters:
PathId:
name: id
in: path
required: true
schema: { type: integer, format: int64 }
Limit:
name: limit
in: query
schema: { type: integer }
requestBodies:
PanelVersionedDecision:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [version]
properties:
version: { $ref: "#/components/schemas/FlexInt64" }
internal_note: { type: string }
PanelUserAvatarUpload:
required: true
content:
multipart/form-data:
schema:
type: object
required: [metadata, file]
properties:
metadata:
description: "JSON — common fields + user_id (int64)"
type: string
file: { type: string, format: binary }
PanelAdminUserAction:
description: >-
Shared by create/update-access/set-password for a named admin
operator account. `reason` is mandatory here specifically
(`decodeAdminUserAction` 400s on blank reason), stricter than the
generic action envelope.
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
properties:
id: { type: integer, format: int64 }
username: { type: string }
password: { type: string }
permissions: { type: array, items: { type: string } }
enabled: { type: [boolean, "null"] }
PanelSetFrozen:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PanelCommandFields"
- type: object
required: [user_id, frozen]
properties:
user_id: { type: integer, format: int64 }
frozen: { type: boolean }
freeze_until: { type: string, format: date-time }
freeze_appeal_url: { type: string }
AdminApiVersionedNote:
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/CommandMeta"
- type: object
required: [version]
properties:
version: { type: integer, format: int64 }
internal_note: { type: string }
responses:
PanelUnauthorized:
description: >-
Missing/expired session (`{"error":"not authenticated"}`), or a
named-account session invalidated server-side since it was issued —
password changed, permissions edited, or account disabled — caught
on every request by re-checking `admin_console_users` against the
session's `epoch` claim (`{"error":"session is no longer valid"}`).
content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } }
PanelForbidden:
description: >-
Missing/mismatched CSRF cookie or header, Origin mismatch (plain
`ErrorEnvelope`), or an authenticated session lacking the required
permission (`PermissionErrorEnvelope`, adds a `permission` field —
`{"error":"permission accounts.manage is required","code":"FORBIDDEN","permission":"accounts.manage"}`).
content: { application/json: { schema: { $ref: "#/components/schemas/PermissionErrorEnvelope" } } }
PanelNotFound:
description: Not found
content: { application/json: { schema: { $ref: "#/components/schemas/ErrorEnvelope" } } }
PanelConflict:
description: >-
Optimistic-locking conflict — another reviewer already decided this
row, or `version` is stale. Only the verification/bot-verification
decision routes preserve this status from the upstream Admin API;
every other action route flattens upstream errors to 502 instead
(see `PanelActionUpstreamError`).
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
PanelActionOK:
description: >-
Command accepted. Response body is the same `CommandResult` shape
the Admin API returns — the panel relays it as-is from
`callAdminAPI`/`callAdminMultipart`/`callAdminCommand`, or builds an
equivalent `CommandResult` locally for the handful of actions that
operate on local state (admin operators, server settings) instead
of calling through to the Admin API.
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
PanelActionUpstreamError:
description: >-
The upstream Admin API call failed (transport error, or the command
itself failed). `writeCommandResultAPI` answers this as HTTP 502
with `CommandResult{Status:"failed", Error: <upstream message>}` —
NOT the upstream's own status code. Verification and bot-verification
decision routes are the exception: they preserve the real upstream
status (404/409/429/400) instead of flattening to 502.
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
PanelCursorList:
description: Cursor-paginated list
content:
application/json:
schema:
type: object
properties:
query: { type: string }
limit: { type: integer }
rows: { type: array, items: { type: object, additionalProperties: true } }
has_more: { type: boolean }
next_before_id: { type: integer, format: int64 }
listing: { type: object, additionalProperties: true }
AdminApiCommandOK:
description: >-
Command result. Success is HTTP 200; a failed command (err != nil)
is answered as HTTP 400 (not 500) by the shared writeCommandResult
helper, with the same CommandResult body shape (Status:"failed").
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
AdminApiCommandError:
description: Command failed (validation or execution error)
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
AdminApiForbidden:
description: Missing bearer token, or token lacks the required permission
content: { application/json: { schema: { $ref: "#/components/schemas/PermissionErrorEnvelope" } } }
AdminApiCodedError:
description: Coded error envelope
content: { application/json: { schema: { $ref: "#/components/schemas/CodedErrorEnvelope" } } }
AdminApiModerationError:
description: Moderation-family coded error (404/403/409/429/400/500 depending on admin.Code*)
content: { application/json: { schema: { $ref: "#/components/schemas/CodedErrorEnvelope" } } }
AdminApiCollectibleError:
description: Collectible-username coded error (404/409/400/500 depending on admin.Code*)
content: { application/json: { schema: { $ref: "#/components/schemas/CodedErrorEnvelope" } } }
AdminApiVerificationError:
description: >-
Official-verification coded error. Status derives from
admin.VerificationErrorCode: NotFound→404;
Conflict/TargetOccupied/TargetVerified→409;
{StatusInvalid,ReasonRequired,TargetInvalid,TargetNotPublic,TargetRestricted,TargetSystem,NotOwner,UserTargetsDisabled,Invalid}→400;
default→400 (forced down from 500 on the command path). On conflict,
`message` is overwritten with a "reload and decide again" prompt.
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
AdminApiBotVerificationError:
description: >-
Bot-verification coded error. Status derives from
admin.BotVerificationErrorCode: *NotFound codes→404;
{Conflict,Limit,RequestExists}→409; RateLimited→429;
{Forbidden,DescriptionForbidden,Invalid,IconInactive,IconInvalid,StatusInvalid,ReasonRequired,TargetInvalid,TargetSystem,VerificationInvalid}→400
(explicitly 400 not 403 — the refusal is about the *subject* bot/peer,
not the caller's authorization); default→400.
content: { application/json: { schema: { $ref: "#/components/schemas/CommandResult" } } }
schemas:
ErrorEnvelope:
type: object
required: [error]
properties:
error: { type: string }
code: { type: string }
CodedErrorEnvelope:
allOf:
- $ref: "#/components/schemas/ErrorEnvelope"
- type: object
required: [code]
PermissionErrorEnvelope:
allOf:
- $ref: "#/components/schemas/ErrorEnvelope"
- type: object
properties:
permission: { type: string, description: "present on 403s from a missing-permission check" }
PanelCommandFields:
type: object
description: Common fields on every Panel API POST /api/actions/* (and moderation/verification decision) body.
required: [reason, confirm]
properties:
command_id: { type: string, description: "Idempotency key; server-generated if omitted." }
reason: { type: string, description: "Mandatory audit reason." }
confirm: { type: boolean, description: "Operator confirmation; must be true." }
FlexInt64:
description: >-
Accepts either a JSON number or a numeric JSON string (custom
UnmarshalJSON on the Go side, `flexInt64`) — used for Telegram-scale
ids where a bare JSON number risks float64 precision loss in JS.
Distinct from a `json:",string"`-tagged field (see field
descriptions), which requires a JSON string specifically.
oneOf:
- { type: integer, format: int64 }
- { type: string }
FlexUnix:
description: >-
Accepts a JSON number (unix seconds), or a string — either a numeric
unix-seconds string or an RFC3339 date (`flexUnix`).
oneOf:
- { type: integer, format: int64, description: unix seconds }
- { type: string, description: "numeric unix-seconds string, or RFC3339" }
CommandMeta:
type: object
description: Common fields embedded in nearly every Admin API (internal/adminapi) request body.
properties:
command_id: { type: string, description: "Idempotency key." }
actor: { type: string, description: "Auto-filled from the scoped-token principal name if omitted." }
reason: { type: string }
dry_run: { type: boolean }
CommandResult:
type: object
description: Response shape for nearly every Admin API POST route.
properties:
command_id: { type: string }
action: { type: string }
status: { type: string }
already_executed: { type: boolean }
dry_run: { type: boolean }
target_user_id: { type: integer, format: int64 }
target_peer: { $ref: "#/components/schemas/Peer" }
message: { type: string }
details: { type: object, additionalProperties: true }
error: { type: string }
Peer:
type: object
properties:
Type: { type: string, description: "domain.PeerType, e.g. user/channel/chat" }
ID: { type: integer, format: int64 }
PeerColorInput:
type: object
description: Shared shape for every *-set-color request (accounts/channels, both APIs).
properties:
for_profile: { type: boolean }
has_color: { type: boolean }
color: { type: integer }
background_emoji_id: { type: integer, format: int64, description: "sent with json:\",string\" on the Admin API side" }
EmojiStatusInput:
type: object
description: Shared shape for every *-set-emoji-status request (accounts/channels, both APIs).
properties:
document_id: { type: integer, format: int64, description: "sent with json:\",string\" on the Admin API side" }
until: { type: integer, description: unix seconds }
AccountRow:
type: object
description: >-
`cmd/telesrv-admin/readstore.go` AccountRow — fields are NOT JSON-tagged,
so they serialize with Go's default PascalCase field names, not
snake_case like the rest of this API.
properties:
ID: { type: integer, format: int64 }
Phone: { type: string }
Username: { type: string }
FirstName: { type: string }
LastName: { type: string }
Collectibles:
type: array
description: Collectible usernames in display order; distinct from the editable Username slot.
items:
type: object
properties: { Username: { type: string }, Active: { type: boolean } }
CreatedAt: { type: string, format: date-time }
UpdatedAt: { type: string, format: date-time }
Frozen: { type: boolean }
Reason: { type: string, description: "The freeze reason given when Frozen was last set true. Empty if never frozen." }
Verified: { type: boolean }
Scam: { type: boolean }
Fake: { type: boolean }
PremiumUntil: { type: integer, format: int64 }
LastActiveAt: { type: string, format: date-time }
DeviceCount: { type: integer }
LoginEmail: { type: string }