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: }` — 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 }