# Per-role permission audit — 2026-05-06 Focused review of UI/server permission divergence on the new endpoints shipped during the smart-archive / hard-delete / bulk-wizard / external-EOI / webhook-replay work bundle. Skips items already covered in `docs/audit-comprehensive-2026-05-06.md` (audit-log gating H6, residential_partner sidebar nav). The pattern hunted for: `` (or `usePermissions().can`) on the UI side hides a control under permission **X**, while the matching API route gates on permission **Y** (or doesn't gate at all, or gates strictly — producing 403 toast spam for users who can see the button but can't use it). Scope: 8 routes + 5 components + the seed permission matrix. Hard cap of 10 findings, ranked by impact. Critical/High/Medium/Low. --- ## CRITICAL _None._ The four new hard-delete endpoints all gate on `admin.permanently_delete_clients` on both layers (UI hides the button via `` in `client-detail-header.tsx:162` and via `canHardDelete = can('admin', 'permanently_delete_clients')` in `client-list.tsx:53`; the four routes all wrap with `withPermission('admin', 'permanently_delete_clients', …)`). The webhook-replay route gates on `admin.manage_webhooks` — see H1 below for the matching UI gap. --- ## HIGH ### H1. Webhook replay button has no UI permission gate (403 toast for non-admins) - **UI:** `src/components/admin/webhooks/webhook-delivery-log.tsx:118-131` — the Replay `