NewWacht Bench is live — AI-assisted development for Wacht
GuidesAPI Auth

Building API Auth Observability Screens

Build operator screens for API key traffic from the audit log, analytics, and timeseries hooks.

Building API Auth Observability Screens

API Auth ships three read hooks over the audit data — a paginated log, an analytics rollup, and a timeseries. This page is how you turn them into operator screens that answer real questions: which key is being abused, why requests are getting blocked, and whether a rotate or revoke actually fixed it.

The audience is your support and on-call engineers, not your customers. Design every screen against a question they'll ask under pressure, then wire the hook that answers it.

The audit log

useApiAuthAuditLogs returns one row per request decision — key_id, outcome (allowed/blocked), blocked_by_rule, path, and timestamp — with cursor pagination via next_cursor.

const logs = useApiAuthAuditLogs({
  limit: 50,
  outcome: "blocked",
  key_id,
  start_date,
  end_date,
  cursor,
});

The three filters that matter during an incident: outcome to pull just the blocked traffic, key_id to isolate one key, and start_date/end_date to bound the window. Cursor pagination is ordered — page forward with next_cursor, don't offset-paginate, or rows shift under you as new traffic lands.

The analytics rollup

useApiAuthAuditAnalytics returns counts and the top-N breakdowns for a window. Request only the sections you render — each include_* flag is a separate aggregation.

const analytics = useApiAuthAuditAnalytics({
  start_date,
  end_date,
  include_top_keys: true,
  include_top_paths: true,
  include_blocked_reasons: true,
  include_rate_limits: true,
  top_limit: 10,
});

You always get total_requests, allowed_requests, blocked_requests, success_rate, and keys_used_24h. The flagged sections add top_keys (key id, name, count), top_paths, blocked_reasons (rule, count, percentage), and rate_limit_stats. The summary numbers are the headline cards; blocked_reasons is what turns "traffic is failing" into "this rule is firing." top_keys is your fastest path from a blocked-traffic spike to the key behind it.

The timeseries

useApiAuthAuditTimeseries returns per-bucket points (timestamp, total/allowed/blocked counts, success_rate) at hour or day granularity.

const timeseries = useApiAuthAuditTimeseries({
  start_date,
  end_date,
  interval: "hour",
  key_id,
});

Use hour for a 24–72h incident window — fine enough to see when a spike started and whether it's receding. Use day for week-over-week behavior. Pass key_id to chart a single suspect key against its own history. Keep the timeseries window aligned with the analytics window so the headline numbers and the chart describe the same span; a mismatched range is the most common reason these two screens disagree.

Triage to action, on one screen

The point of putting all three on one surface is that the operator never leaves it to act. The path is: a blocked spike shows in the timeseries, top_keys or blocked_reasons names the cause, the operator filters the log to that key_id to confirm the pattern, then rotates or revokes the key from the same view. After the action, the next timeseries window shows whether blocked traffic dropped. If it didn't, the key wasn't the source.

So the action rail (rotate / revoke / open key detail) belongs next to the log and analytics, not on a separate page — context is lost the moment they navigate away.

Production notes

  • Persist filters in the URL. outcome, key_id, and the date range belong in query state so an operator can paste a link to the exact view during an incident handoff.
  • Handle 401/403 explicitly. A ticket can expire or fail your RBAC mid-session. Render the policy boundary as its own state, not a generic error — the operator needs to know it's permission, not an outage.
  • Refetch on active incidents. The hooks don't poll. Add a manual refresh or a timer while an incident is open so the operator sees recovery without reloading.
  • Log the action elsewhere. Wacht records the key event; the operator's rationale ("revoked per ticket #4821") belongs in your own audit trail.

Before you ship, confirm the three windows stay aligned, that cursor paging holds order under live traffic, that a rotate/revoke shows up in the next audit window, and that a user without the ticket can't reach any of these views.

On this page