Audit and notifications
Read the audit log, export records, manage notification preferences, and query dashboard stats — endpoints under /api/audit/, /api/notifications/, and /api/dashboard/.
Three related surfaces grouped together: the immutable audit log (who did what), user-facing notifications (what Fabrik wants to tell you), and dashboard aggregates (the counters shown on the home page).
Audit log
All endpoints under /api/audit/. Read-only from the API — audit entries are written exclusively by the backend through AuditService.log().
GET /api/audit/logs/
Paginated log list. Admin-only.
Filters:
?category=<value>— one of the 20 categories (authentication,user_management,query_execution,awx_execution, etc.).?action=<value>— specific action within a category.?user=<id>or?username=<name>— filter by actor. Username is denormalized into the log row, so it's searchable even if the user was later deleted.?ip=<address>— filter by source IP (matches rightmostX-Forwarded-Forentry).?created_after=<iso>/?created_before=<iso>— time window.?search=<term>— full-text over thedescriptionfield.?ordering=-created_at— most recent first (default).
Resource shape:
{
"id": 48291,
"category": "query_execution",
"action": "query_run",
"username": "alice",
"user_id": 1,
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 …",
"description": "Executed saved query \"Tenant BD health\" against apic-prod",
"target_type": "SavedQuery",
"target_id": "af3…",
"metadata": { "row_count": 142, "duration_ms": 1840 },
"content_compressed": false,
"content_size_bytes": 412,
"created_at": "2026-04-22T08:00:00Z"
}Entries with full request/response bodies store them separately — content_compressed: true means the content is gzip-compressed (applied to payloads over 1 MB).
GET /api/audit/logs/<id>/
Single log with full content. For compressed entries, the content is decompressed on the fly before returning.
GET /api/audit/logs/stats/
Aggregate counts for the admin dashboard.
Query params: ?window=24h|7d|30d.
Response 200:
{
"window": "7d",
"total": 48291,
"by_category": { "authentication": 1024, "query_execution": 24812, "awx_execution": 812, "user_management": 14, … },
"failed_logins": 8,
"unique_actors": 42
}GET /api/audit/logs/export/
Streaming CSV export. Same filters as the list endpoint. Hard cap of 10 000 rows per export — if your filter would match more, tighten it.
Response 200: text/csv with a Content-Disposition: attachment header.
Exports are themselves logged (category: admin_action, action: audit_export). Exporting the audit log is an auditable event, on purpose.
GET / PATCH /api/audit/settings/
Audit retention and capture configuration. Single-resource — there's only one AuditLogSettings row.
Fields:
- Per-category enabled toggles (12 switches — one per category group).
- Per-category retention in days (from 30 days for noisy categories like
api_accessto 365 days foruser_managementandauthentication). content_max_bytes— per-entry content cap (default 10 MB).compress_threshold_bytes— gzip threshold (default 1 MB).
Admin-only.
Login attempts
GET /api/audit/login-attempts/
Separate table from the audit log — every successful and failed login attempt, including failures from usernames that don't exist. Used for brute-force monitoring.
Filters: ?username=<name>, ?success=false, ?ip=<address>, time-window filters.
Resource shape:
{
"id": 9182,
"username": "alice",
"success": false,
"failure_reason": "invalid_password",
"ip_address": "203.0.113.42",
"user_agent": "…",
"attempted_at": "2026-04-22T08:00:00Z"
}Admin-only.
Notifications
All endpoints under /api/notifications/. Real-time delivery is over WebSocket — these REST endpoints handle list, read state, and preferences.
GET /api/notifications/notifications/
Paginated list of the authenticated user's notifications.
Filters:
?unread=true— only unread notifications.?category=<value>—scheduled_task,awx_execution,time_machine,system.?severity=<value>—info,warning,error.
Resource shape:
{
"id": "…",
"category": "scheduled_task",
"severity": "warning",
"title": "Scheduled task \"Daily BD audit\" failed",
"body": "APIC connection timed out after 30 seconds.",
"link": "/scheduled-tasks/…",
"read_at": null,
"created_at": "2026-04-22T02:01:00+03:00"
}POST /api/notifications/notifications/<id>/mark_read/
Mark one notification as read. Idempotent.
POST /api/notifications/notifications/mark_all_read/
Mark every unread notification for the calling user as read. Returns { "updated": <n> }.
DELETE /api/notifications/notifications/<id>/
Delete a notification. Soft delete — hidden from the list but retained in the database for 30 days.
GET /api/notifications/notifications/unread_count/
Cheap counter for the bell badge in the header. Returns { "count": 7 }.
Notification preferences
GET / PATCH /api/notifications/preferences/
Per-user delivery preferences. Single-resource.
Fields:
in_app_enabled— master switch for in-app notifications.email_enabled— master switch for email.categories— object with per-category toggles and channels:{ "scheduled_task": { "in_app": true, "email": true }, "awx_execution": { "in_app": true, "email": false }, "time_machine": { "in_app": true, "email": false }, "system": { "in_app": true, "email": true } }quiet_hours_start/quiet_hours_end— 24h times in the user's timezone. Email is suppressed during quiet hours; in-app notifications still arrive but the system doesn't send email until the window ends.digest_enabled— if true, batch email notifications into a daily digest instead of per-event.
Quiet hours apply only to email. In-app notifications always arrive in real time — they aren't disruptive because you see them when you look.
Dashboard
All endpoints under /api/dashboard/. Small surface — just the aggregates that power the home page.
GET /api/dashboard/stats/
Counters for the authenticated user.
Response 200:
{
"saved_queries": 42,
"scheduled_tasks": { "total": 8, "enabled": 7, "failing": 1 },
"automation_requests_7d": 12,
"snapshots_7d": 168,
"unread_notifications": 3,
"last_login": "2026-04-22T08:00:00Z"
}GET /api/dashboard/platform-info/
Platform-level info (available to all authenticated users).
Response 200:
{
"version": "1.0.0",
"apic_version": "6.0.8",
"mim_installed_at": "2026-04-01T12:00:00Z",
"uptime_seconds": 482931,
"services": {
"postgres": "healthy",
"neo4j": "healthy",
"redis": "healthy",
"rabbitmq": "healthy"
}
}Health fields reflect each service's Docker healthcheck state at the moment of the request — same data as docker compose ps.