Inbox

The Inbox is DayZero's unified notification feed. Every alert, approval request, anomaly, sync event, ledger change, and announcement that needs your attention lands here as a single row with a status, a topic, and a severity — so nothing gets lost across screens. Open it from the sidebar (it renders the Notifications view at /books/inbox), pick the scope you care about, and work the feed top to bottom.

Key capabilities

  • One feed across every business you belong to, with a Me / Business / Firm scope switch (Firm is gated to firm staff by the backend)
  • A real status workflow — Open / Snoozed / Done tabs backed by a new → seen → resolved | dismissed | snoozed state machine
  • Topic filter chips (anomaly, billing, AP approvals, Plaid, month-end, ledger, notes, email ingest, reconciliation, manual) with live server-side counts
  • Severity chips (Info, Warning, Urgent); warning and urgent rows get a colored left border
  • Pin important rows to the top of the Open tab
  • Snooze with presets — 1 hour, 4 hours, Tomorrow 9am, 1 week — and the row reappears when the timer elapses
  • Resolve or Dismiss (both terminal), plus Mark all seen to clear the unread dots in bulk
  • A deep-link action button that jumps straight to the related record (bill, invoice, transaction, report, etc.)
  • Live virtual rows (low cash, stale Plaid connection, etc.) merged into the feed and tagged "Live"
  • A real-time Server-Sent Events stream that pushes new items instantly, with a 30-second poll as a backstop for the bell badge
  • Multi-channel fan-out per topic: in-app always, Slack when a webhook is configured, email when the topic policy allows
  • A bell badge that counts only Open (new + seen) items for your current scope

How it works

When something happens — a bill enters an approval gate, an anomaly is detected, a bank sync finishes — a publisher hands the event to the notification policy. The policy resolves who should be notified (the business owner, all business users, the per-tier approver, mentioned users, or firm staff) and which channels fire, then writes one row per recipient and fans out.

flowchart TD
  event["Event (bill gate, anomaly, sync, mention)"] --> policy{"Audience + channel policy by topic"}
  policy --> rows["Notification rows (recipient: user / business / firm)"]
  rows --> feed["Inbox feed (/books/inbox)"]
  rows -->|"Slack if enabled"| slack["Slack webhook"]
  rows -->|"email per policy"| email["Email"]
  feed --> stream["Live SSE stream + bell badge"]
  feed --> act{"You act"}
  act -->|"open / mark seen"| seen["Seen"]
  act -->|"snooze"| snoozed["Snoozed (wakes later)"]
  act -->|"resolve / dismiss"| done["Done (terminal)"]

How to use it

  1. Open Inbox from the sidebar — the bell badge shows how many Open items wait on you.
  2. Use the Me / Business / Firm switch in the header to choose the audience slice. Me spans all your businesses; Business scopes to the active business; Firm shows the advisor feed.
  3. Switch between the Open, Snoozed, and Done tabs to focus on what's actionable now versus deferred or finished.
  4. Click severity chips (Info / Warning / Urgent) and topic chips to filter; chips show live counts and hide topics with zero items. Click Clear to reset.
  5. Click any row to open the detail panel on the right — a new row is auto-marked seen when you open it.
  6. Use the row's action button (e.g. "Review bill") to jump to the underlying record.
  7. From the detail header, Pin, Snooze (pick a preset), Resolve, or Dismiss the item.
  8. In the Open tab, click Mark all seen to clear unread dots across the visible feed in one action.

Pro tips

  • Stay in the Me scope for your personal triage; switch to Business or Firm only when you're working on someone else's behalf or doing a portfolio sweep.
  • Snooze instead of dismiss for things you can't action yet — a dismissed row is terminal, but a snoozed row resurfaces in the Open tab when its timer elapses.
  • Filter to Urgent severity first thing each morning — those are the rows the policy also escalates to Slack/email.
  • Pin the one or two items you're actively working so they stay at the top of Open even as new rows arrive.
  • "Live" rows are computed, not stored — dismissing one hides it for you specifically; it can reappear if the underlying condition (e.g. low cash) recurs.
  • Tune per-topic noise in notification preferences rather than dismissing repeatedly — you can mute routine sync pings while keeping urgent reconnect alerts.

In-depth guide

Scopes and audience routing

Every notification is addressed to a recipient — a single user, a whole business, or an entire firm — and the read path unions across the three based on your memberships. The scope switch picks which slice you see:

  • Me — everything addressed to you across all your businesses.
  • Business — filtered to one business.
  • Firm — the firm advisor feed (only firm staff can load it).

Who actually receives a row is decided per topic by the audience policy:

  • Billing → the business owner (and firm owners).
  • AP approvals → the per-tier approver (and the assigned advisor).
  • Operational alerts (anomalies, Plaid health) → the owner plus the full firm staff bench.

Status lifecycle

Status replaces the old read/starred/archived flags with a single state machine. Open = new or seen; Done = resolved or dismissed.

Status Meaning Tab Next states
new Just delivered, unread (blue dot) Open seen, snoozed, resolved, dismissed
seen Opened or marked seen Open snoozed, resolved, dismissed
snoozed Hidden until snoozed_until elapses Snoozed seen (auto, when the timer wakes it)
resolved Handled — terminal Done
dismissed Cleared without action — terminal Done

Severity

Severity drives visual urgency and, for some topics, whether email fires.

Severity Use Visual
info Routine / FYI Muted chip, no row border
warning Worth a look soon Amber chip + amber left border
urgent Needs action now Red chip + red left border

Topics

Topic answers "what domain is this about?" and powers the filter chips and per-topic counts. The chip row hides any topic with a zero count so it stays scannable.

Topic Examples
billing Subscription/billing events
ap_approval A bill entered an approval gate
anomaly Unusual transaction or amount detected
plaid / xero / quickbooks Bank/ledger connection health (reconnect needed)
plaid_sync / xero_sync / quickbooks_sync Routine "+N records synced" pings
subscription Recurring-spend budget threshold crossed
month_end Month-end close milestones
reconciliation Reconciliation prompts and results
ledger Ledger changes (per-user, in-app only)
notes Note/mention activity
email_ingest A forwarded email was ingested
manual A person-to-person or manually sent notification

Channels and delivery

Each topic declares how it fans out. In-app (the Inbox) is the constant; Slack and email are conditional.

Channel Behavior
In-app Always written to the feed
Slack Sent when the firm has a webhook configured (if_enabled)
Email Per topic — always (billing), if_urgent / if_high_severity (AP approvals, anomalies, reconnect prompts, budget overruns), or off (routine sync pings, ledger, email ingest)

Delivery is best-effort: if Slack or email fails, the in-app row is still written, so the Inbox is the source of truth.

Email ingestion

  • Forwarded documents (e.g. vendor invoices sent to your DayZero ingest address) raise the email_ingest topic so the business owner sees that something arrived and was processed.
  • By policy these are in-app only — no Slack or email — to avoid a notification echoing the email you just forwarded.

Linking to entities and action buttons

  • Rows carry an optional polymorphic entity reference (entity_type + entity_id) plus an action_url / action_label.
  • When present, the detail panel renders the action button so one click takes you to the bill, invoice, transaction, or report the notification is about — no hunting required.

Live (virtual) notifications

  • Some signals — low cash, a stale Plaid connection — are computed on every read rather than stored as durable rows.
  • They appear inline tagged Live and merge into the feed for the business in context.
  • Because they aren't persistent, dismissing one is recorded per user in a small dismissals table keyed to the signal — and the item can return if the condition recurs.

Real-time stream and the bell badge

  • The feed subscribes to a Server-Sent Events stream that delivers new rows the instant they're published across any server or background worker, with heartbeats to keep the connection alive through proxies.
  • If the real-time layer is unavailable, the stream stays idle and a 30-second poll keeps the bell badge's Open count correct.
  • The badge counts only Open (new + seen) items for your current scope.

Edge cases

  • Selecting the Business scope with no active business shows a "no business selected" prompt instead of an empty feed.
  • Mark all seen only affects new rows currently in the list; it doesn't resolve or dismiss anything.
  • Terminal rows (resolved, dismissed) won't transition again — reopening isn't supported, so prefer Snooze when you might need the item back.
  • Pinning and snoozing apply to durable rows only; Live rows can't be pinned or snoozed.

Start free and keep everything that needs you in one prioritized feed.