Skip to content

Practitioner Notifications (Supabase Edge Function)

This function sends practitioner emails for:

  • patient protocol acceptance
  • patient lab sharing
  • adherence alert (<50% for 3+ trailing days)
  • weekly summary

Required secrets

Set these before deploy:

supabase secrets set \
  SENDGRID_API_KEY=SG.xxxxx \
  NOTIFY_FROM_EMAIL="Onset Clinic <notifications@theonset.app>" \
  NOTIFY_FUNCTION_SECRET=your_shared_secret \
  ONSET_IOS_APP_STORE_URL="https://apps.apple.com/us/app/the-onset/id6761314183"

Optional: set NEXT_PUBLIC_ONSET_IOS_APP_STORE_URL in the clinic portal for the same App Store link on patient handout PDFs.

SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are injected by Supabase automatically in hosted Edge Functions.

Deploy

supabase functions deploy practitioner-notifications

Staging (Onset-staging, lzpyfohgfdohrtlamzek)

Staging starts with no edge functions. Run once from the clinic portal repo:

cp supabase/.env.staging.example supabase/.env.staging   # add SENDGRID_API_KEY
./scripts/supabase/deploy-staging-notifications.sh

See docs/ENVIRONMENTS.md for the full staging email checklist.

Invoke payloads

{ "eventType": "protocol_accepted", "assignmentId": "uuid" }
{ "eventType": "lab_shared", "labResultId": "uuid" }
{ "eventType": "adherence_shared", "practitionerId": "uuid", "patientAppUserId": "uuid" }
{ "eventType": "weekly_summary", "practitionerId": "uuid" }
{ "eventType": "clinic_patient_shared", "patientShareId": "uuid" }

Trigger examples (database webhooks via pg_net)

Run this SQL in Supabase SQL editor after enabling pg_net:

create extension if not exists pg_net;

create or replace function public.notify_protocol_accepted()
returns trigger
language plpgsql
security definer
as $$
begin
  if new.status = 'accepted' and (old.status is distinct from new.status) then
    perform net.http_post(
      url := current_setting('app.settings.supabase_url') || '/functions/v1/practitioner-notifications',
      headers := jsonb_build_object(
        'Content-Type', 'application/json',
        'x-notify-secret', current_setting('app.settings.notify_function_secret')
      ),
      body := jsonb_build_object(
        'eventType', 'protocol_accepted',
        'assignmentId', new.id
      )
    );
  end if;
  return new;
end;
$$;

drop trigger if exists trg_notify_protocol_accepted on public.protocol_assignments;
create trigger trg_notify_protocol_accepted
after update on public.protocol_assignments
for each row execute function public.notify_protocol_accepted();

create or replace function public.notify_lab_shared()
returns trigger
language plpgsql
security definer
as $$
begin
  perform net.http_post(
    url := current_setting('app.settings.supabase_url') || '/functions/v1/practitioner-notifications',
    headers := jsonb_build_object(
      'Content-Type', 'application/json',
      'x-notify-secret', current_setting('app.settings.notify_function_secret')
    ),
    body := jsonb_build_object(
      'eventType', 'lab_shared',
      'labResultId', new.id
    )
  );
  return new;
end;
$$;

drop trigger if exists trg_notify_lab_shared on public.shared_lab_results;
create trigger trg_notify_lab_shared
after insert on public.shared_lab_results
for each row execute function public.notify_lab_shared();

create or replace function public.notify_adherence_shared()
returns trigger
language plpgsql
security definer
as $$
begin
  perform net.http_post(
    url := current_setting('app.settings.supabase_url') || '/functions/v1/practitioner-notifications',
    headers := jsonb_build_object(
      'Content-Type', 'application/json',
      'x-notify-secret', current_setting('app.settings.notify_function_secret')
    ),
    body := jsonb_build_object(
      'eventType', 'adherence_shared',
      'practitionerId', new.practitioner_id,
      'patientAppUserId', new.patient_app_user_id
    )
  );
  return new;
end;
$$;

drop trigger if exists trg_notify_adherence_shared on public.shared_adherence;
create trigger trg_notify_adherence_shared
after insert on public.shared_adherence
for each row execute function public.notify_adherence_shared();

Weekly summary scheduling

Create a Supabase cron job:

select cron.schedule(
  'practitioner-weekly-summary',
  '0 15 * * 1',
  $$
  select net.http_post(
    url := current_setting('app.settings.supabase_url') || '/functions/v1/practitioner-notifications',
    headers := jsonb_build_object(
      'Content-Type', 'application/json',
      'x-notify-secret', current_setting('app.settings.notify_function_secret')
    ),
    body := jsonb_build_object('eventType', 'weekly_summary')
  );
  $$
);