# retoor <retoor@molodetz.nl>
from .._shared import endpoint, field
GROUP = {
"slug": "admin",
"title": "Admin API",
"admin": True,
"intro": """
# Admin API
Site administration endpoints for users, news curation, and settings. Every call requires an
**admin** account. Background service management lives on the
[Background Services](/docs/services.html) page.
Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html) (auth, content
negotiation, pagination, status codes); see [Authentication](/docs/authentication.html) for the
four ways to sign requests.
""",
"endpoints": [
endpoint(
id="admin-users",
method="GET",
path="/admin/users",
title="List users",
summary="Paginated user management page. Returns HTML.",
auth="admin",
interactive=True,
params=[field("page", "query", "int", False, "1", "Page number.")],
),
endpoint(
id="admin-media",
method="GET",
path="/admin/media",
title="Media trash",
summary=(
"Moderation view of deleted media. When a member or admin deletes an "
"attachment it is soft-deleted: hidden from the gallery and from its parent "
"object, but the row and file are kept and the relation to the parent is "
"preserved. This page lists every soft-deleted attachment, newest first, with "
"its uploader, so an admin can restore or permanently purge it. Returns HTML."
),
auth="admin",
interactive=True,
params=[field("page", "query", "int", False, "1", "Page number.")],
),
endpoint(
id="media-restore",
method="POST",
path="/media/{uid}/restore",
title="Restore media",
summary=(
"Restore a soft-deleted attachment. Because the parent relation is never "
"cleared, it reappears in the owner's Media tab and on its original post, "
"project, or gist immediately."
),
auth="admin",
destructive=True,
params=[
field(
"uid", "path", "string", True, "", "Soft-deleted attachment uid."
),
],
sample_response={"ok": True, "redirect": "/admin/media"},
),
endpoint(
id="admin-revoke-award",
method="POST",
path="/admin/awards/{uid}/revoke",
title="Revoke award",
summary=(
"Soft-delete a published award and its linked attachments, then recompute "
"receiver stats. Restorable from admin trash."
),
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "", "Award uid to revoke."),
],
sample_response={"ok": True, "redirect": "/profile/receiver?tab=awards"},
),
endpoint(
id="admin-media-purge",
method="POST",
path="/admin/media/{uid}/purge",
title="Purge media",
summary=(
"Permanently delete a soft-deleted attachment: removes the database row and "
"deletes the file from disk. This cannot be undone and is the only way media "
"is hard-deleted from the UI."
),
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "", "Attachment uid to purge."),
],
sample_response={"ok": True, "redirect": "/admin/media"},
),
endpoint(
id="admin-audit-log",
method="GET",
path="/admin/audit-log",
title="Audit log",
summary=(
"Paginated, filterable audit event list (newest first, 25 per page). "
"Negotiates HTML or JSON. Every state-changing action on the platform "
"is recorded here with actor, origin, target, change, and result."
),
auth="admin",
interactive=True,
params=[
field("page", "query", "int", False, "1", "Page number."),
field("event_key", "query", "string", False, "post.create", "Filter by exact event key."),
field("category", "query", "string", False, "admin", "Filter by category (auth, content, admin, container, ...)."),
field("actor_role", "query", "string", False, "admin", "Filter by actor role at action time."),
field("actor_uid", "query", "string", False, "", "Filter by acting user uid."),
field("origin", "query", "string", False, "web", "Filter by origin (web, api, devii, cli, service, scheduler)."),
field("result", "query", "string", False, "denied", "Filter by result (success, failure, denied)."),
field("q", "query", "string", False, "", "Free-text search over summary, event key, and target."),
field("date_from", "query", "string", False, "", "ISO date lower bound (inclusive)."),
field("date_to", "query", "string", False, "", "ISO date upper bound (inclusive)."),
],
sample_response={
"entries": [
{
"uid": "AUDIT_UID",
"created_at": "2026-06-11T20:00:00+00:00",
"event_key": "admin.setting.update",
"category": "admin",
"actor_username": "ADMIN",
"actor_role": "admin",
"origin": "web",
"via_agent": 0,
"target_type": "setting",
"old_value": "0",
"new_value": "1",
"result": "success",
}
],
"pagination": {"page": 1, "total": 1, "total_pages": 1},
"filters": {},
"options": {"category": ["admin", "auth", "content"]},
},
),
endpoint(
id="admin-audit-event",
method="GET",
path="/admin/audit-log/{uid}",
title="Audit event",
summary="A single audit event with its full row and every related-object link.",
auth="admin",
interactive=True,
params=[field("uid", "path", "string", True, "AUDIT_UID", "Audit event uid.")],
sample_response={
"event": {"uid": "AUDIT_UID", "event_key": "container.instance.start", "result": "success"},
"links": [
{"relation": "actor", "object_type": "user", "object_uid": "USER_UID"},
{"relation": "instance", "object_type": "instance", "object_uid": "INSTANCE_UID"},
],
},
),
endpoint(
id="admin-analytics",
method="GET",
path="/admin/analytics",
title="Site analytics",
summary=(
"One-call aggregate analytics, returned as JSON: total members, active users "
"in the last 24h/7d/30d, signed_in_now, new signups (24h/7d/30d), content "
"totals (posts, comments, gists, projects, news), and top authors. Use this "
"instead of paging the user list to count or measure activity."
),
auth="admin",
interactive=True,
params=[
field(
"top_n",
"query",
"int",
False,
"10",
"How many top authors to include (1-50).",
)
],
notes=[
"`signed_in_now` counts members holding an unexpired session (logged in within the session lifetime, default 7-30 days), not a real-time online/presence count; the response carries a `signed_in_definition` explaining this. `active_*` means created content in the window (`active_definition`).",
"This is the endpoint the Devii assistant calls as `site_analytics`; see [Devii internals](/docs/devii-internals.html).",
],
),
endpoint(
id="admin-statistics",
method="GET",
path="/admin/statistics/data",
title="Platform statistics",
summary=(
"Tabbed platform statistics with KPI cards, period-over-period deltas, "
"time-series data for charts, and breakdown tables. Covers visitors, members, "
"content, engagement, social, AI, Devii, services, containers, game, awards, "
"moderation, tools, and storage."
),
auth="admin",
interactive=True,
params=[
field(
"tab",
"query",
"string",
False,
"overview",
"Tab key (overview, visitors, members, content, ...).",
),
field(
"hours",
"query",
"int",
False,
"168",
"Lookback window in hours (24, 168, 720, 2160, or 0 for all time).",
),
field(
"compare",
"query",
"int",
False,
"1",
"Include previous-period comparison (1 or 0).",
),
field(
"top_n",
"query",
"int",
False,
"10",
"Rows in breakdown tables (1-50).",
),
],
notes=[
"The HTML dashboard lives at `/admin/statistics`. Visitor metrics require the statistics tracking middleware (hourly aggregation, 90-day retention).",
],
),
endpoint(
id="admin-statistics-page",
method="GET",
path="/admin/statistics",
title="Statistics dashboard",
summary="Admin HTML dashboard for platform statistics with charts and tabs.",
auth="admin",
interactive=False,
params=[
field(
"tab",
"query",
"string",
False,
"overview",
"Initial tab to render.",
),
field(
"hours",
"query",
"int",
False,
"168",
"Initial time window in hours.",
),
],
),
endpoint(
id="admin-ai-usage",
method="GET",
path="/admin/ai-usage/data",
title="AI gateway usage analytics",
summary=(
"One-call AI gateway metrics, returned as JSON for a bounded time window: request "
"volume and throughput, token usage with averages and percentiles (p50/p90/p95/p99), "
"latency (upstream, gateway overhead, queue wait, connection establishment), error "
"rates by category, cost in USD (per model, per caller, input vs output, projected "
"monthly burn, caching savings), caller behavior, and an hourly breakdown. Cost is "
"taken from the upstream native cost when present (OpenRouter) and computed from the "
"configured per-million pricing otherwise (DeepSeek)."
),
auth="admin",
interactive=True,
params=[
field(
"hours",
"query",
"int",
False,
"48",
"Lookback window in hours (1-168).",
),
field(
"top_n",
"query",
"int",
False,
"10",
"How many rows in each top-N breakdown.",
),
],
notes=[
"TTFT and inter-token latency are not reported: the gateway forwards non-streaming to the upstream."
],
),
endpoint(
id="admin-user-ai-usage",
method="GET",
path="/admin/users/{uid}/ai-usage",
title="Per-user AI usage",
summary=(
"One user's AI gateway usage over the last 24 hours, returned as JSON: request "
"volume, success and error rates, token totals, cost (window, per hour, per request, "
"and a 30-day projection from the full 24h spend), average latency and throughput, a "
"per-model breakdown, and an hourly cost series. Because Devii operates a signed-in "
"user's account with that user's own API key, this is the user's complete gateway "
"spend, whether driven through Devii or direct API calls. Shown admin-only on the "
"user's profile page."
),
auth="admin",
interactive=True,
params=[
field(
"uid", "path", "string", True, "USER_UID", "Target user UID."
),
field(
"hours",
"query",
"int",
False,
"24",
"Lookback window in hours (1-168).",
),
],
sample_response={
"owner_id": "USER_UID",
"window_hours": 24,
"requests": 42,
"success": 41,
"failed": 1,
"success_pct": 97.6,
"error_pct": 2.4,
"tokens": {"prompt": 120000, "completion": 38000, "total": 158000},
"cost": {
"window_usd": 0.214,
"per_hour_usd": 0.0089,
"per_request_usd": 0.0051,
"projected_30d_usd": 6.42,
},
"latency": {"avg_ms": 1830.0, "avg_tps": 41.2},
"by_model": [
{
"key": "molodetz",
"requests": 42,
"total_tokens": 158000,
"cost_usd": 0.214,
}
],
"hourly": [
{
"hour": "2026-06-08T16",
"requests": 6,
"cost_usd": 0.031,
"total_tokens": 22000,
}
],
},
),
endpoint(
id="admin-user-role",
method="POST",
path="/admin/users/{uid}/role",
title="Set a user role",
summary="Promote or demote a user. You cannot change your own role.",
auth="admin",
encoding="form",
destructive=True,
params=[
field(
"uid", "path", "string", True, "USER_UID", "Target user UID."
),
field(
"role",
"form",
"enum",
True,
"member",
"New role.",
["member", "admin"],
),
],
),
endpoint(
id="admin-user-password",
method="POST",
path="/admin/users/{uid}/password",
title="Reset a user password",
summary="Set a new password for a user.",
auth="admin",
encoding="form",
destructive=True,
params=[
field(
"uid", "path", "string", True, "USER_UID", "Target user UID."
),
field(
"password",
"form",
"string",
True,
"newpassword",
"New password, 6+ characters.",
),
],
),
endpoint(
id="admin-user-toggle",
method="POST",
path="/admin/users/{uid}/toggle",
title="Enable or disable a user",
summary="Toggle a user's active state. You cannot disable yourself.",
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "USER_UID", "Target user UID.")
],
),
endpoint(
id="admin-news-list",
method="GET",
path="/admin/news",
title="List news articles",
summary="Paginated news management page. Returns HTML.",
auth="admin",
interactive=True,
params=[field("page", "query", "int", False, "1", "Page number.")],
),
endpoint(
id="admin-news-toggle",
method="POST",
path="/admin/news/{uid}/toggle",
title="Toggle featured",
summary="Toggle an article's featured flag and lock it from the news service's auto-rotation.",
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
],
),
endpoint(
id="admin-news-publish",
method="POST",
path="/admin/news/{uid}/publish",
title="Toggle published",
summary="Switch an article between draft and published.",
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
],
),
endpoint(
id="admin-news-landing",
method="POST",
path="/admin/news/{uid}/landing",
title="Toggle landing",
summary="Toggle whether an article shows on the landing page and lock it from the news service's auto-rotation.",
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
],
),
endpoint(
id="admin-news-delete",
method="POST",
path="/admin/news/{uid}/delete",
title="Delete a news article",
summary="Delete an article and its images.",
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "NEWS_UID", "Article UID.")
],
),
endpoint(
id="admin-settings-get",
method="GET",
path="/admin/settings",
title="Read site settings",
summary="Return the current site and operational settings. Negotiates HTML or JSON.",
auth="admin",
interactive=True,
),
endpoint(
id="admin-settings",
method="POST",
path="/admin/settings",
title="Save site settings",
summary="Update operational and site settings. Empty fields are skipped.",
auth="admin",
encoding="form",
destructive=True,
params=[
field(
"site_name", "form", "string", False, "DevPlace", "Site name."
),
field(
"rate_limit_per_minute",
"form",
"string",
False,
"60",
"Requests per window.",
),
field(
"registration_open",
"form",
"enum",
False,
"1",
"Allow signups.",
["1", "0"],
),
field(
"maintenance_mode",
"form",
"enum",
False,
"0",
"Maintenance gate.",
["1", "0"],
),
],
notes=[
"Accepts every field on the admin settings form; only non-empty values are written."
],
),
endpoint(
id="admin-notification-default",
method="POST",
path="/admin/notifications",
title="Set a notification default",
summary="Set the platform-wide default for one notification type on one channel. Applies to users who have not customized that notification; explicit user choices are unaffected.",
auth="admin",
encoding="form",
destructive=True,
params=[
field(
"notification_type",
"form",
"string",
True,
"vote",
"One of: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen.",
),
field(
"channel",
"form",
"string",
True,
"push",
"One of in_app, push or telegram (telegram is off by default and requires a paired Telegram account).",
),
field(
"value",
"form",
"boolean",
False,
"1",
"1 to enable this notification by default, 0 to disable.",
),
],
sample_response={
"ok": True,
"redirect": "/admin/notifications",
"data": {
"notification_type": "vote",
"channel": "push",
"value": False,
},
},
),
endpoint(
id="admin-user-reset-ai-quota",
method="POST",
path="/admin/users/{uid}/reset-ai-quota",
title="Reset user AI quota",
summary="Delete a specific user's AI gateway ledger rows, resetting their quota.",
auth="admin",
destructive=True,
params=[
field(
"uid", "path", "string", True, "USER_UID", "Target user UID."
),
],
),
endpoint(
id="admin-ai-quota-reset-guests",
method="POST",
path="/admin/ai-quota/reset-guests",
title="Reset guest AI quotas",
summary="Reset AI quota for all anonymous guest users.",
auth="admin",
destructive=True,
),
endpoint(
id="admin-ai-quota-reset-all",
method="POST",
path="/admin/ai-quota/reset-all",
title="Reset all AI quotas",
summary="Reset AI quota for every user (members and guests).",
auth="admin",
destructive=True,
),
endpoint(
id="admin-gateway-quota-rules",
method="GET",
path="/admin/gateway/quota-rules",
title="List AI gateway quota rules",
summary=(
"List every rolling-24h USD quota rule on /openai/v1/*, each scoped by any "
"combination of role, specific user uid, and app_reference label, plus the "
"global per-role default caps that apply when no rule matches."
),
auth="admin",
interactive=True,
),
endpoint(
id="admin-gateway-quota-rule-set",
method="POST",
path="/admin/gateway/quota-rules",
title="Create or update an AI gateway quota rule",
summary=(
"Caps rolling-24h USD spend on /openai/v1/*. At least one of owner_kind, "
"owner_id, app_reference must be set; leaving a dimension blank makes it a "
"wildcard, and the most specific active match wins over other rules and over "
"the global default. Pass uid to update an existing rule."
),
auth="admin",
params=[
field("uid", "json", "string", False, "", "Existing rule uid to update; omit to create a new rule."),
field("owner_kind", "json", "string", False, "user", "internal, key, user, admin, or anonymous. Blank = any role."),
field("owner_id", "json", "string", False, "", "Specific user uid. Blank = any caller of the matched role."),
field("app_reference", "json", "string", False, "devplace-bots-v-1-0-0", "App label (the X-App-Reference header). Blank = any app."),
field("limit_usd", "json", "number", True, "2.5", "Rolling 24h USD cap. 0 = unlimited."),
field("is_active", "json", "boolean", False, "true", "Whether the rule is enforced."),
field("label", "json", "string", False, "", "Optional admin-facing note."),
],
),
endpoint(
id="admin-gateway-quota-rule-delete",
method="DELETE",
path="/admin/gateway/quota-rules/{uid}",
title="Delete an AI gateway quota rule",
summary="Delete a quota rule; callers it covered fall back to the next most specific rule or the global default.",
auth="admin",
destructive=True,
params=[
field("uid", "path", "string", True, "RULE_UID", "Quota rule uid."),
],
),
endpoint(
id="admin-bots-monitor",
method="GET",
path="/admin/bots",
title="Bot monitor page",
summary="Live low-quality screenshot grid of every running bot persona, one frame per bot.",
auth="admin",
interactive=True,
),
endpoint(
id="admin-bots-data",
method="GET",
path="/admin/bots/data",
title="Bot monitor data",
summary="JSON of every running bot slot with its label, persona, current action/status, and latest frame URL for polling.",
auth="admin",
sample_response={
"enabled": True,
"service_status": "running",
"frames": [
{
"slot": 0,
"username": "bytewren",
"persona": "grumpy_senior",
"action": "POST: rant [3]",
"status": "page load",
"url": "https://example.com/feed",
"label": "bytewren",
"captured_at": 1718366400,
"age_seconds": 2,
"has_image": True,
"active": True,
"frame_url": "/admin/bots/0/frame.jpg?t=1718366400",
}
],
},
),
endpoint(
id="admin-bots-frame",
method="GET",
path="/admin/bots/{slot}/frame.jpg",
title="Bot frame image",
summary="The latest low-quality JPEG screenshot for one bot slot (no-store).",
auth="admin",
encoding="none",
interactive=False,
params=[
field(
"slot",
"path",
"integer",
True,
"0",
"Bot fleet slot index.",
)
],
),
endpoint(
id="admin-backups",
method="GET",
path="/admin/backups",
title="Backups dashboard",
summary=(
"Storage usage, every backup archive, and every backup schedule. Returns HTML "
"(or JSON with Accept: application/json)."
),
auth="admin",
interactive=True,
),
endpoint(
id="admin-backups-data",
method="GET",
path="/admin/backups/data",
title="Backups data",
summary=(
"JSON dashboard payload: per-path storage usage, total data and backup size, "
"disk usage, the backup list, and the schedule list."
),
auth="admin",
),
endpoint(
id="admin-backups-run",
method="POST",
path="/admin/backups/run",
title="Create backup",
summary=(
"Enqueue an async backup job for a target (database, uploads, keys, full). "
"Returns the job uid and a status_url."
),
auth="admin",
params=[
field(
"target",
"form",
"string",
True,
"full",
"One of database, uploads, keys, full.",
)
],
sample_response={
"ok": True,
"uid": "JOB_UID",
"backup_uid": "BACKUP_UID",
"status_url": "/admin/backups/jobs/JOB_UID",
},
),
endpoint(
id="admin-backups-job",
method="GET",
path="/admin/backups/jobs/{uid}",
title="Backup job status",
summary="Status of one backup job (pending, running, done, failed) with archive stats.",
auth="admin",
params=[field("uid", "path", "string", True, "", "Backup job uid.")],
sample_response={
"uid": "JOB_UID",
"kind": "backup",
"status": "done",
"target": "full",
"backup_uid": "BACKUP_UID",
"download_url": "/admin/backups/BACKUP_UID/download",
"bytes_out": 10485760,
"file_count": 1240,
"sha256": "…",
},
),
endpoint(
id="admin-backups-download",
method="GET",
path="/admin/backups/{uid}/download",
title="Download backup",
summary="Stream a completed backup archive as a tar.gz file. Restricted to the primary administrator (the first user created with the Admin role); every other administrator receives 403 Forbidden.",
auth="admin",
encoding="none",
params=[field("uid", "path", "string", True, "", "Backup uid.")],
),
endpoint(
id="admin-backups-delete",
method="POST",
path="/admin/backups/{uid}/delete",
title="Delete backup",
summary="Permanently delete a backup archive and reclaim its disk space.",
auth="admin",
destructive=True,
params=[field("uid", "path", "string", True, "", "Backup uid.")],
sample_response={"ok": True, "redirect": "/admin/backups"},
),
endpoint(
id="admin-backups-schedule-create",
method="POST",
path="/admin/backups/schedules/create",
title="Create backup schedule",
summary=(
"Create a recurring backup. kind=interval uses every_seconds; kind=cron uses a "
"5-field cron expression. keep_last rotates older backups of the schedule."
),
auth="admin",
params=[
field("name", "form", "string", True, "Nightly full", "Schedule name."),
field("target", "form", "string", True, "full", "Backup target."),
field("kind", "form", "string", True, "interval", "interval or cron."),
field("every_seconds", "form", "int", False, "86400", "Seconds between runs (kind=interval)."),
field("cron", "form", "string", False, "0 3 * * *", "Cron expression (kind=cron)."),
field("keep_last", "form", "int", False, "7", "Keep only the newest N (0 = all)."),
],
sample_response={"ok": True, "redirect": "/admin/backups"},
),
endpoint(
id="admin-backups-schedule-edit",
method="POST",
path="/admin/backups/schedules/{uid}/edit",
title="Edit backup schedule",
summary="Update a backup schedule's target, trigger, and retention.",
auth="admin",
params=[
field("uid", "path", "string", True, "", "Schedule uid."),
field("name", "form", "string", True, "Nightly full", "Schedule name."),
field("target", "form", "string", True, "full", "Backup target."),
field("kind", "form", "string", True, "interval", "interval or cron."),
field("every_seconds", "form", "int", False, "86400", "Seconds between runs (kind=interval)."),
field("cron", "form", "string", False, "0 3 * * *", "Cron expression (kind=cron)."),
field("keep_last", "form", "int", False, "7", "Keep only the newest N (0 = all)."),
],
sample_response={"ok": True, "redirect": "/admin/backups"},
),
endpoint(
id="admin-backups-schedule-toggle",
method="POST",
path="/admin/backups/schedules/{uid}/toggle",
title="Toggle backup schedule",
summary="Enable or disable a backup schedule.",
auth="admin",
params=[field("uid", "path", "string", True, "", "Schedule uid.")],
sample_response={"ok": True, "redirect": "/admin/backups"},
),
endpoint(
id="admin-backups-schedule-run",
method="POST",
path="/admin/backups/schedules/{uid}/run",
title="Run backup schedule now",
summary="Immediately enqueue a backup for a schedule without waiting for its next run.",
auth="admin",
params=[field("uid", "path", "string", True, "", "Schedule uid.")],
sample_response={"ok": True, "redirect": "/admin/backups"},
),
endpoint(
id="admin-backups-schedule-delete",
method="POST",
path="/admin/backups/schedules/{uid}/delete",
title="Delete backup schedule",
summary="Delete a backup schedule. Existing archives are kept.",
auth="admin",
destructive=True,
params=[field("uid", "path", "string", True, "", "Schedule uid.")],
sample_response={"ok": True, "redirect": "/admin/backups"},
),
endpoint(
id="admin-game",
method="GET",
path="/admin/game",
title="Code Farm Era management",
summary="View the current Code Farm Era status.",
auth="admin",
sample_response={"era_active": False, "era_name": ""},
),
endpoint(
id="admin-game-era-start",
method="POST",
path="/admin/game/era/start",
title="Start an Era",
summary="Start a new Code Farm Era: resets every farm's visible Era coins/harvests counters to zero. Real coins, prestige, stars, Legacy, and Mastery are never touched.",
auth="admin",
params=[
field("name", "form", "string", True, "Genesis", "Era name."),
field("duration_days", "form", "int", False, "28", "Planned Era length in days."),
],
sample_response={"ok": True, "redirect": "/admin/game"},
),
endpoint(
id="admin-game-era-end",
method="POST",
path="/admin/game/era/end",
title="End the running Era",
summary="End the current Era: ranks every participating farm by Era score, awards Stars to the top 10 (and an Era-exclusive cosmetic when available), and permanently records the results.",
auth="admin",
destructive=True,
sample_response={"ok": True, "redirect": "/admin/game"},
),
],
}