Notification & Alert System (Stub)

Status: Stub – v0.1 Audience: Backend engineers Purpose: Outline the minimal notification pipeline required for early MVP, focused on scan events for lost / stolen objects. Will be expanded later to cover push, in-app, and SMS.


1. Trigger Matrix

Event Source Conditions Recipients Channel Notes
ScanEvent Object has status lost, stolen, missing OR ACL indicates owner cannot currently see it Current owner (users.email), org admins Email (Resend) Include timestamp & geolocation from scan payload
StatusUpdate Status changes to lost or stolen Owner, org admins Email First alert when item is flagged
(future) OfferAccepted Marketplace offer accepted Buyer & seller Email + push

2. Email Transport – Resend

2.1 API Key Storage

  • Use Supabase encrypted secrets feature or env var RESEND_API_KEY in serverless function.

2.2 Helper Function (pseudo-code)

import { Resend } from 'resend'

export async function sendEmail(to: string, subject: string, html: string) {
  const resend = new Resend(process.env.RESEND_API_KEY)
  await resend.emails.send({
    from: 'Plings <alerts@plings.io>',
    to,
    subject,
    html,
  })
}

2.3 Template – Lost/Stolen Scan

<h1>Heads-up: Your item was scanned</h1>
<p>Item: <strong></strong></p>
<p>When: </p>
<p>Where: </p>
<p>If you've recovered the item, you can update its status in the app.</p>

3. Backend Flow Stub

  1. GraphQL resolver resolveIdentifier → once it classifies the tag as KNOWN_OBJECT:
  2. Internal call recordScanEvent(objectId, userId, geo) (done today).
  3. Add step:
    if (object.statuses.includes('lost') || object.statuses.includes('stolen')) {
        queueEmailNotification('lost_stolen_scan', { objectId, ownerId, geo, ts })
    }
    
  4. queueEmailNotification writes to notifications_outbox table. A background worker (cron or Supabase function) polls this table and sends via Resend.

4. Database Tables (initial)

-- audit_log_system already stores scans; add minimal outbox
create table if not exists notifications_outbox (
  id uuid primary key default gen_random_uuid(),
  type text not null,          -- e.g. 'lost_stolen_scan'
  payload jsonb not null,
  created_at timestamptz default now(),
  sent_at timestamptz
);

5. GraphQL Types (to add later)

type Notification {
  id: ID!
  type: String!
  payload: JSON!
  createdAt: DateTime!
  sentAt: DateTime
}

6. Open Items

  • Decide on push vs email prioritisation.
  • Rate-limit notifications if a lost item is scanned repeatedly.
  • Owner contact workflow: secure chat vs masked email relay.
  • GDPR / data-retention policy for outbox table.