2026-07-04 21:17:56 +02:00
# 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-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-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 " ,
2026-07-06 05:57:47 +02:00
" One of: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen. " ,
2026-07-04 21:17:56 +02:00
) ,
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-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 " } ,
) ,
] ,
}