Sitelet https://github.com/ansvisor/ansvisor/issues/540
Skip to content

Daily Pulse email — cloud-only per-brand daily digest with highlights and anomaly alerts #540

Description

@gkhngyk

Summary

Replace the "Anomaly alerts" README TODO with something broader: a Daily Pulse email per brand — a short digest sent after the daily tracking run that covers both the good news and the warnings, instead of an alerts-only "bad news machine". Anomaly detection becomes one section of the pulse.

Naming: the feature is deliberately named Pulse, not Brief or Report — brief collides with the existing Content Brief feature and report with the Reports feature, which would make the codebase painful to grep. Use pulse consistently: pulse_settings, sent_pulses, daily_pulse.created.

Cloud-only: the email leg is gated behind isCloud() — self-hosted deployments do not send Daily Pulse emails. The daily_pulse.created webhook event ships through the existing per-brand webhook system and works everywhere.

V1 scope

Content (per brand, generated after the daily tracking run):

  1. KPI strip — visibility rate (yesterday + 7-day trend), mentions, citations, sentiment. Reuse the same RPCs the Insights page uses (insights_aggregates, competitor_aggregates, prompt_visibility_summaries) so the email and the dashboard always agree.
  2. Highlights (when present) — first-time citations (prompt_target_urls.first_cited_at), top 2–3 prompts by visibility gain, a competitor overtaken on the leaderboard, first appearance on a new answer engine.
  3. Warnings (when present) — three detectors:
    • Sharp visibility drop: 7-day rate vs previous 7 days; alert when the drop is ≥15 points and ≥30% relative, with a minimum data floor (≥10 prompts with results).
    • Competitor surge: a competitor's rate up ≥15 points week-over-week, or crossing above the brand's rate for the first time.
    • High-volume prompt lost citations: a prompt with volume data that was cited in ≥60% of runs over the last 14 days drops to 0 citations for 3 consecutive runs.
    • Platform-outage guard: if a platform's results collapse across all orgs on a given day (data-collection incident, e.g. a scraper format change), exclude that platform from detection or annotate the warning — never mass-email "your visibility dropped" for our own pipeline issue.
  4. CTA — link to the brand's Insights page.

Delivery:

  • Email via Resend, cloud only — new small server email module using the Resend HTTP API (RESEND_API_KEY), sending gated behind isCloud().
  • One digest per brand per day (never one email per event). Include a List-Unsubscribe header and a settings link.
  • Webhook — add a daily_pulse.created event to the existing per-brand webhook_configs system (HMAC envelope already in place), so Slack/Notion delivery works from day one through webhooks — including on self-host; native Slack is v2.

Settings (Settings → Notifications, per brand):

  • Frequency: Daily / Weekly / Only when something notable / Off (default Daily). Weekly runs the same engine over a 7-day window.
  • Recipient list (default: organization members).

Eligibility — hard requirement:

  • Send only to organizations whose subscription is active or trialing (the existing isSubscriptionActive check: active / trialing).
  • Users whose trial has expired, or whose subscription has lapsed/canceled, must never receive these emails — no re-engagement mail from this feature.
  • Skip brands with no fresh tracking results for the day.

Data model:

  • pulse_settings (per brand: frequency, recipients) and sent_pulses (send log — dedup, "already sent today" check, and warning cooldown: don't repeat the same warning for the same subject within 7 days).

Out of scope (v2)

  • AI-generated narrative summary (reuse the Reports AI-summary mechanism; LLM cost is per brand × day, so plan-gate it).
  • Native Slack incoming-webhook field with Block Kit formatting.
  • In-app notification center; per-detector threshold tuning UI.

Acceptance criteria

  • After the daily cron, each eligible brand's recipients get at most one email; content matches the dashboard numbers for the same window.
  • No email is ever sent to an org that is not active/trialing, and none are sent from self-hosted deployments (isCloud() gate).
  • The daily_pulse.created webhook event fires on both cloud and self-host when a pulse is generated.
  • The README TODO item "Anomaly alerts" is updated to reference this feature in the implementing PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions