FabrikFabrik
FabrikAPI Reference

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 rightmost X-Forwarded-For entry).
  • ?created_after=<iso> / ?created_before=<iso> — time window.
  • ?search=<term> — full-text over the description field.
  • ?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_access to 365 days for user_management and authentication).
  • 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.