API reference
The live OpenAPI spec is served at `/openapi.json` and a Swagger UI lives
The live OpenAPI spec is served at /openapi.json and a Swagger UI lives
at /docs on every running API instance. This page is the human map
above that.
Auth
Two layers, evaluated in order:
- Telegram
initDatainX-Telegram-Init-Data. HMAC-verified against the bot token; thetg_user_idfrom the payload becomes the identity. Required on every non-public route. - TOTP in
X-TOTP-Code. Only whenOWNER_TOTP_ENABLED=trueand the route is/v1/owner/*. See TOTP enrolment.
Public routes (no auth): /healthz, /metrics, /openapi.json,
/docs, /.well-known/*.
Routes
| Group | Routes | Auth |
|---|---|---|
| Identity | GET /v1/me | initData |
| Targets | GET /v1/targets · POST /v1/targets · DELETE /v1/targets/{id} | initData |
| Target settings | POST /v1/targets/{id}/mute · GET/PATCH /v1/targets/{id}/settings/{key} | initData |
| Analytics | GET /v1/targets/{id}/analytics · /heatmap · /daily | initData |
| Settings | GET /v1/settings/parameters/visible | initData |
| Owner — params | GET /v1/owner/parameters · PATCH .../{key}/override | initData + TOTP |
| Owner — audit | GET /v1/owner/audit | initData + TOTP |
| Owner — userbots | GET /v1/owner/userbots/ · POST .../import | initData + TOTP |
| Owner — personas | GET /v1/owner/personas/ · POST .../try-pick | initData + TOTP |
| Owner — GDPR | POST /v1/owner/gdpr/export · POST .../erase | initData + TOTP |
Rate limits
Token-bucket via Redis, evaluated per request. Two buckets:
- Per source IP — default 60 capacity, 1/s refill.
- Per verified Telegram identity — default 240 capacity, 4/s refill.
Activated only when
initDatais valid.
429 responses carry a Retry-After header. Public paths skip the
limiter entirely so health probes don't get throttled.
Defaults are parameters; tune via the admin panel without a deploy:
api.rate_limit.ip.capacity/.refill_per_secapi.rate_limit.identity.capacity/.refill_per_sec
Security headers
Every response carries:
X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: strict-origin-when-cross-originCross-Origin-Opener-Policy: same-origin-allow-popupsCross-Origin-Resource-Policy: same-siteStrict-Transport-Security,Content-Security-Policy,Permissions-Policy— sourced from parameters (api.security.{csp,hsts,permissions_policy}), with OWASP-style defaults.
Errors
Standard {"detail": "human-readable reason"} payloads with the right
HTTP status:
| Status | Meaning |
|---|---|
| 400 | malformed body / invalid parameter value |
| 401 | missing or invalid X-Telegram-Init-Data |
| 403 | authenticated but unauthorised (non-owner, wrong TOTP, …) |
| 404 | resource not visible to this client (often RLS-hidden) |
| 409 | conflict (duplicate persona, duplicate session, …) |
| 415 | unsupported session format on userbot import |
| 422 | value failed validators (cadence below floor, etc.) |
| 429 | rate limit exceeded — honour Retry-After |
Versioning
/v1 is the only versioned prefix today. Additions (new routes, new
fields) are non-breaking; renames or removals require a /v2 carve-out
that runs alongside /v1 for the deprecation window. None of those are
planned yet.