Every guarded request writes one row to the
security_events table (input and output are separate rows). Self-hosted deployments own that Postgres database directly, so you can point psql, a BI tool, or your warehouse’s foreign-data wrapper at it and run these recipes as-is. On PromptGuard Cloud, pull the same data through the Interactions API and load it into your own store.There is no pre-aggregated roll-up table today — these recipes run directly against the raw
security_events (and, for configuration/auth activity, audit_events) event tables. The Daily and hourly roll-ups section shows how to build your own materialized view if you want cheaper dashboards.The tables you’ll query
security_events — the request log
One row per policy evaluation. The columns that matter for analytics:
audit_events — the compliance trail
Configuration changes, authentication, and data access. Hash-chained and never purged. Columns: created_at, organization_id, user_id, event_type, category (security | authentication | data_access | configuration | billing), action, outcome (success | failure | denied | error), resource_type, resource_id, ip_address, details (jsonb).
Latency: p50 / p95 / p99
Engine processing time by percentile over the last 24 hours. Usepercentile_cont for interpolated percentiles.
surface to compare the developer proxy against Shadow AI scans:
processing_time_ms is the time PromptGuard’s engine spent, not end-to-end request time. On the proxy path it excludes the upstream provider call. See Latency Budgets for what each detector contributes.Token volume by model
Grouped by upstream model. Tokens are NULL on/guard-only scans (they never call a provider), so filter them out.
High-risk activity by user
Ranks callers by blocked-request volume. Usesend_user_id (populated when you send the X-End-User header); fall back to user_id for account-level attribution.
Blocked over time
Hourly blocked-vs-total counts for a time-series chart:date_trunc('hour', …) for date_trunc('day', …) and widen the interval for a daily trend.
Threats by detector
decision/threat_type tell you what was caught; the detector that caught it lives in event_metadata->>'detector' (values include regex, ml, agentic, and specific detector names). This recipe attributes blocks to the detection layer that fired — useful for tuning which layers earn their latency.
Daily and hourly roll-ups
There is no roll-up table shipped, so long-window dashboards scan raw events every load. If that gets expensive, materialize a daily summary yourself. This view is safe toREFRESH on a schedule (nightly, or hourly with CONCURRENTLY):
INSERT-based accumulator that survives purges:
pg_cron (self-host / Supabase) or any external scheduler:
Audit trail (configuration & auth)
Config changes and authentication live inaudit_events, not security_events. Recent denied or failed sensitive actions:
Next steps
Latency Budgets
Per-detector p99 budgets behind
processing_time_msAudit Logs
Export the same data through the Interactions API
Usage Tracking
Plan quotas, request counts, and spend
Dashboard
Prebuilt analytics in the app