Feature

Notifications and alerts
for the people who carry the work

Findings move, engagements change status, documents land, invoices send, comments post. SecPortal fans those events out into per-user notifications scoped by tenant and role, so the people who carry the work see the change without polling the activity log.

No credit card required. Free plan available forever.

The change reaches the people who carry the work, not the chat thread next to it

Security work changes hands across roles. A scanner module surfaces a critical finding and the AppSec lead needs to see it. A client portal user posts a comment on the finding and the engagement lead needs to read it before the next standup. A retest closes a long-open finding and the assigned owner needs the closure record without refreshing the dashboard. Programmes that route those changes through email threads, chat channels, and standup readouts lose signal at every hop. The information is correct in one place and stale in the other places, and the people who need it most often hear about it last.

SecPortal fans every workspace event into per-user notifications scoped by tenant and by membership. A finding update reaches the workspace operators on the engagement, the client portal users tied to the parent client, and the named assignee for assignment events. Reads and mark-read state live on the recipient's own inbox. The fan-out happens on the same record the work happens on through the activity log, so the inbox surface and the audit trail share one source rather than two systems that diverge between reporting cycles.

Six event classes that fan out into the inbox

The platform fans out the events that carry operational meaning across roles. The list below is what the Postgres triggers and the targeted-assignment helper actually fire. Other entity changes still record on the activity log for retrospective review; the events below also write a per-user notification row for live signal.

Finding lifecycle

A finding is created, its severity changes, its status transitions (open, in progress, resolved, accepted), or it is deleted. Workspace operators in the same tenant get a notification, the client portal users tied to the parent client get a notification, and the user the finding is assigned to gets a targeted notification on top of the broadcast.

Engagement transitions

An engagement is created, its status moves through scoping, in progress, delivered, or closed, or scope is updated. Workspace operators see the transition and the client portal users on the parent client see the transition through their portal view, so the engagement timeline does not depend on a status email someone forgot to send.

Document uploads and deletes

An evidence file, a SOW, a scanner export, an attestation letter, or a debrief deck is attached to an engagement. The upload event fans out to workspace operators and to client portal users tied to the engagement, with the file name and the actor identity in the metadata so the recipient can open straight to the engagement record.

Comment activity on findings

A comment is posted on a finding. The thread is attached to the finding, not to a parallel chat channel, and the notification points to the finding so the conversation reads in context. Workspace operators and client portal users on the same engagement both receive the notification.

Invoice sent and invoice paid

An invoice moves from draft to sent or from sent to paid. The notification reaches workspace operators on those material transitions only. Draft creation and intermediate state changes are recorded on the activity log without firing notifications, so the inbox stays signal rather than noise.

Targeted finding assignment

A finding is assigned to a specific user by email. The assigned user receives a single targeted notification rather than the workspace broadcast, so the named owner sees their queue grow without competing with workspace-wide noise. Assignment is recorded in the activity log alongside the broadcast events.

How fan-out is computed

Every event lands on the activity log first. The fan-out functions then insert one notification row per intended recipient, keyed to the originating activity. The model is small enough that the boundary between application code, database trigger, and per-user inbox is easy to inspect.

  • Each event writes one row into the activities table, then a Postgres function inserts one row into the notifications table per intended recipient, keyed to that activity. The fan-out is computed from the workspace and client membership at the moment the event happens, so a notification is delivered to the people who held the role at that point in time.
  • Workspace fan-out covers users with the consultant role on the workspace and excludes the actor of the event by default, so a security operator does not get a notification for their own action. Self-actor exclusion is enforced both in fan-out and in the read endpoints.
  • Client portal fan-out covers the client_users tied to the affected client and excludes the actor. A client lead reading the portal sees a finding update notification at the same time the workspace operators do, so the engagement transparency does not lag a status email.
  • Targeted assignment fan-out covers exactly one user (the assignee) for finding.assigned events. The assigned user gets a single record rather than the workspace broadcast, so the named owner sees their assignment without it being lost in the broader feed.
  • A unique index on (activity_id, user_id) prevents duplicate notifications when fan-out logic and targeted assignment overlap. A user assigned to a finding they themselves created does not see two records for the same event.

Tenant isolation, RBAC, and self-actor exclusion

Notifications inherit the same multi-tenant model as the rest of the platform. Read and update access run through team management with RBAC, and the AAL2 session promotion from multi-factor authentication gates the dashboard reach itself. The notifications surface does not invent a parallel access model.

  • Row-level security on the notifications table restricts SELECT to user_id = auth.uid(). The same policy gates UPDATE, so a user can mark their own notifications read but cannot read or modify another user holder.
  • Workspace_id is recorded on every notification row alongside user_id, so an operational query that filters by workspace returns only the notifications belonging to that tenant. A multi-tenant operator cannot see activity from a workspace they no longer belong to even if their auth session is otherwise valid.
  • Activities themselves carry their own RLS: workspace operators read activities scoped to the tenant they belong to; client portal users read activities tied to a client they have a client_users record for through metadata->>client_id. The notifications view is gated through both layers.
  • Self-actor exclusion runs at the API boundary as a safety net on top of the fan-out function. The notifications GET endpoint applies an OR filter (actor_id IS NULL OR actor_id != current user) so a user does not see notifications for actions they took even if a custom workflow tries to insert one.
  • Notifications inherit the workspace MFA enforcement: AAL2 session promotion is required before the dashboard becomes reachable, so reading notifications is gated by the same second-factor requirement as the rest of the workspace.

Where notifications surface for the recipient

Recipients meet notifications in three places, all reading from the same record. The bell is the live indicator; the dropdown is the recent inbox; the activity page is the retrospective view.

Header bell with unread count

A bell icon in the workspace header polls the unread count every ten seconds. The count is capped at 99+ for display so a long-overdue inbox does not break the layout. Clicking the bell opens a dropdown of the ten most recent notifications with a relative timestamp on each row.

Per-row mark read and mark all read

Each notification row has a one-click mark-read control. A header-level mark-all-read clears every unread row in the workspace inbox. Reads are optimistically applied in the UI and persisted through the read endpoint with a brief poll-skip so a stale poll cannot overwrite the optimistic state.

Direct deep-link to the source

Each notification carries enough metadata to deep-link to the source object. A finding notification opens the engagement page scoped to the finding, an engagement notification opens the engagement page, a document notification opens the engagement, an invoice notification opens the engagement, a team change opens the team page. The recipient lands on the work, not on a digest.

Workspace bell and client portal bell

The same notification system serves both the workspace dashboard and the client portal. A workspace operator and a client portal user looking at the same finding update see it in their respective bells, scoped by tenant and by client membership rather than by separate inbox plumbing.

Activity feed at /dashboard/activity

For longer-form review, the dashboard activity page renders the underlying timeline with filters (findings, engagements, clients, invoices, documents, comments, team). Notifications point at events and the activity feed shows the history of those events in context. Both views read from the same source of truth.

Idempotent and de-duplicated

A unique index on (activity_id, user_id) means a duplicate fan-out attempt or a custom workflow that races with the trigger does not produce a doubled inbox. The on conflict do nothing clause makes idempotency a property of the storage layer rather than a hope at the call site.

Retention and the daily cleanup

Notification retention follows the activity-log retention model rather than living on a separate clock. The daily cleanup cron removes activities older than the plan retention window and the notifications fanned out from those activities at the same time, so the inbox does not outlive the audit record it points at.

  • Notifications inherit the activity log retention model: thirty days on the Starter plan, ninety days on the Pro plan, and three hundred and sixty-five days on the Team plan. The daily cleanup cron removes activities older than the retention window and the notifications that fan out from those activities at the same time.
  • Retention boundaries are operational, not delivery-related. A notification reaches the inbox when the event happens; the retention window controls how long the historical record remains queryable from the activity feed and the notifications list.
  • A workspace that needs the audit observation period to span a full year picks the Team plan; a workspace that runs a quarterly assessment cycle is well-served by the Pro plan; a Starter workspace covers the most recent month for short-cycle work and trial use.
  • There is no per-user opt-out toggle. The platform decides which events fan out and to whom; recipients control read state on their own inbox. This keeps the notification surface predictable across the workspace and avoids notifications going missing because a user disabled a category.
  • There is no per-event mute or per-finding subscribe. The signal is determined by membership (workspace operators, client portal users on the affected client, assigned user for assignment events) rather than by individual subscriptions, so a member who joins mid-engagement starts receiving the events from their first action onward.

How the design keeps signal high

Notification systems fail when they fan out too much. The platform applies six deliberate constraints so the inbox stays the live state of the work rather than a running ledger of every keystroke. None of them are configurable; they are the default behaviour, so a workspace cannot accidentally fan out itself into noise.

  • Self-actor exclusion: workspace operators do not see notifications for events they themselves caused, applied both at fan-out and at the read endpoint. The inbox is what other people did, not what you did.
  • Material transitions only on invoices: invoice fan-out fires on sent and paid transitions rather than on every status change, so finance recipients see the inflection points instead of the full ledger.
  • Status changes only on findings broadcast: finding update notifications fire on status transitions and severity changes rather than on every text edit, so the inbox tracks the lifecycle rather than the typing.
  • Messages do not fan out as notifications: messages still write into the activity feed for the engagement timeline, but they do not generate per-user notifications because high-volume conversation would crowd out signal events.
  • Targeted assignment is one record per assignment event: a user assigned to a finding gets one row, not a broadcast plus an assignment. The single row carries the assignment metadata and points to the finding directly.
  • Cap and elide for display: the unread count caps at 99+ in the header bell so a long-overdue inbox does not break layout. The full count is still queryable through the count endpoint for any internal reporting.

Questions an enterprise reviewer should be able to answer in five minutes

Procurement and security architecture reviews ask the same questions about any platform that fans out internal events. The answers below come from the engagement screen, the activity log, and the notifications endpoint rather than from a dashboard widget that runs on its own clock.

  • When a critical finding is created on engagement X, who knows about it within ten seconds, and is the named owner one of those people?
  • When a client portal user comments on a finding, do the workspace operators on that engagement see the comment in their bell without polling chat?
  • When a user is assigned to a finding by email, do they get a single targeted record in their bell rather than competing with the workspace broadcast?
  • When a workspace operator marks all their notifications as read, is the change applied immediately and recorded with a read timestamp on each row?
  • When an actor takes an action, does the same actor see a notification for their own action? The answer is no, by both the fan-out function and the read endpoint.
  • When a notification is older than the workspace retention window, has it been removed by the daily cleanup cron, and does the activity it was fanned from get removed at the same time?

Boundaries: what notifications are and what they are not

Naming the boundary up front keeps the notifications surface focused on its job and points teams at the right tool when they reach the edge.

Do

Treat the bell as the live state of the work. Open the dropdown to see what changed since you last looked, click straight through to the source object, and use mark-all-read when you are caught up rather than letting the unread count grow.

Do

Use the activity feed at /dashboard/activity for longer-form review. The bell shows the latest ten events; the activity page exposes filter tabs (findings, engagements, clients, invoices, documents, comments, team) for retrospective questions across the retention window.

Do not

Treat notifications as a paging or alerting system. SecPortal does not send SMS, push, or external chat for activity events. For runbook-style alerting, route from your alerting platform; SecPortal is the engagement record and the activity inbox.

Do not

Rely on email for activity events. SecPortal sends transactional email for signup, invitation, password reset, and invoice flows; activity events (finding state changes, comments, document uploads) reach the recipient through the in-app bell, not through email.

Where notifications fit in the rest of the platform

Notifications are the live read of the same record that findings management operates on, engagement management organises, and document management attaches evidence to. The inbox is signal; the activity log is history; the engagement record is the work itself.

Where the bell points is what makes the surface useful. A finding-status notification points to the engagement scoped to the finding, so the named owner runs through remediation tracking and vulnerability SLA management on the same record the bell linked them to. The leadership-level cadence layered on top of the same record runs through security leadership reporting, so the live inbox and the recurring report draw from one source.

The same fan-out also reaches the client portal bell for client portal users tied to the engagement, so the engagement transparency does not lag behind a status email someone forgot to send. The notifications surface is the same on both sides; the membership filter is what differs.

For users who prefer the keyboard over the bell, the dashboard global search palette opens with Cmd+K and resolves a finding, engagement, or client by name in two characters. The notification points the recipient at the right record; the palette is the parallel path for an operator who already knows which finding they need to open.

For the audit-side framing of how the activity log keeps the underlying record defensible, see the audit evidence half-life research. For the buyer-side framing of who consumes this discipline, see the audience pages for internal security teams, security operations leaders, and vulnerability management teams. Each one runs the live signal against a different operating cadence, and the notifications surface stays the same underneath.

Stop chasing status updates across chat threads

Every finding, engagement, document, and comment that matters reaches the right inbox. No setup, no rules engine, no extra tab.

No credit card required. Free plan available forever.