critical priority low complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

Webhook fires on INSERT events to the `activities` table
Webhook fires on UPDATE events to the `activities` table (covers cases where an activity is backdated or corrected)
Webhook payload includes at minimum: `peer_mentor_id`, `org_id`, and `record.id` of the inserted/updated activity
Webhook target URL points to the deployed `badge-criteria-edge-function` Supabase Edge Function
Webhook is configured with a timeout of at least 10 seconds to accommodate edge function processing time
Webhook is configured with at least 3 retry attempts on HTTP 5xx responses from the edge function
Webhook does NOT fire on DELETE events (deleted activities should not retroactively revoke badges — that is handled separately if at all)
Webhook configuration is reproducible via Supabase CLI migration or `supabase/config.toml` (not manually configured only in dashboard)
Webhook is disabled in local development (Supabase CLI `supabase start`) to prevent localhost calls during development
Webhook is active and verified in the staging Supabase project before production deployment

Technical Requirements

frameworks
Supabase CLI
Supabase Dashboard (Webhooks)
Supabase Edge Functions
apis
Supabase Database Webhooks (pg_net extension)
badge-criteria-edge-function HTTP endpoint (POST)
data models
activities (peer_mentor_id, org_id, id — included in webhook payload)
performance requirements
Webhook delivery must not block the original activity INSERT/UPDATE transaction — Supabase webhooks are async by default via pg_net, verify this is not a synchronous hook
Edge function must respond within 10 seconds to avoid webhook timeout and unnecessary retries
security requirements
Webhook must include a shared secret header (e.g., `x-webhook-secret`) that the edge function validates on receipt — prevents unauthenticated calls to the edge function URL
Store the shared secret in Supabase Edge Function environment secrets (not hardcoded in config)
The edge function URL should not be publicly advertised — it is an internal service endpoint
Webhook payload must not include sensitive activity fields (notes, free-text summaries) — only IDs required for evaluation trigger

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Supabase database webhooks are implemented via the `pg_net` extension and the `supabase_functions.http_request()` Postgres function. Define the webhook as a Postgres trigger in a migration file (`supabase/migrations/YYYYMMDD_badge_evaluation_webhook.sql`). The trigger should call `supabase_functions.http_request()` with: method='POST', url=``, headers including the `x-webhook-secret` from a Supabase vault secret, body as `json_build_object('peer_mentor_id', NEW.peer_mentor_id, 'org_id', NEW.org_id, 'activity_id', NEW.id)`. Set `timeout_milliseconds=10000` and `retries=3` in the http_request call.

Use `AFTER INSERT OR UPDATE ON activities FOR EACH ROW` trigger timing. For local dev safety, wrap the trigger creation in an environment check or simply document that `supabase db push` should only be run against non-local projects for this migration.

Testing Requirements

Manual verification steps: (1) Insert a test activity row for a known peer_mentor_id in staging — confirm edge function receives the webhook within 5 seconds by checking edge function logs in Supabase Dashboard, (2) Update an existing activity row — confirm webhook fires again, (3) Delete an activity row — confirm no webhook is fired, (4) Temporarily return HTTP 500 from the edge function and verify retry behaviour in pg_net logs (`select * from net._http_response`), (5) Verify the shared secret header is present in the received request inside the edge function. Document these verification steps in a runbook comment in the migration file.

Component
Badge Criteria Edge Function
infrastructure medium
Epic Risks (3)
medium impact medium prob technical

Supabase Edge Functions may experience cold start latency of 500ms–2s when they have not been invoked recently. If evaluation latency consistently exceeds the 2-second UI expectation, the celebration overlay timing SLA cannot be met without the optimistic UI fallback from the UI epic.

Mitigation & Contingency

Mitigation: Keep the edge function warm by scheduling a lightweight health-check invocation every 5 minutes in production. Optimise the function size to minimise Deno module load time. Implement the optimistic UI path in badge-bloc (from the UI epic) as the primary UX path so cold start only affects server-side reconciliation, not perceived responsiveness.

Contingency: If cold starts remain problematic, migrate badge evaluation to a Supabase database function (pl/pgsql) triggered directly by a database trigger on activity insert, eliminating the Edge Function overhead entirely for the evaluation logic while keeping Edge Function only for FCM notification dispatch.

high impact low prob integration

Supabase database webhooks can fail silently if the edge function returns a non-2xx response or times out. A missed webhook means a peer mentor does not receive a badge they earned, which is both a functional defect and a trust issue for organisations relying on milestone tracking.

Mitigation & Contingency

Mitigation: Implement idempotent webhook processing: the edge function reads the activity ID from the webhook payload and checks whether evaluation for this activity has already run (via an audit log query) before proceeding. Add Supabase webhook retry configuration (3 retries with exponential backoff). Monitor webhook failure rates via Supabase logs alert.

Contingency: Implement a nightly reconciliation job (Supabase scheduled function) that scans all activities from the past 24 hours, re-evaluates badge criteria for any peer mentor with no corresponding evaluation log entry, and awards any missing badges. Alert operations if reconciliation awards more than 5% of badges, indicating systematic webhook failure.

high impact low prob security

The evaluation service loads badge definitions per organisation, but a misconfigured RLS policy or incorrect organisation scoping in the edge function could cause one organisation's badge criteria to be evaluated against another organisation's peer mentor activity data, leading to incorrect or cross-contaminated badge awards.

Mitigation & Contingency

Mitigation: The edge function must extract organisation_id from the webhook payload activity record and pass it explicitly to every database query. Write a security test that seeds two organisations with distinct badge definitions and verifies that evaluating a peer mentor in org A never reads or awards org B definitions. Use Supabase service role key only within the edge function, never the anon key.

Contingency: If cross-org contamination is detected in audit logs, immediately disable the edge function webhook, run a targeted SQL query to identify and revoke incorrectly awarded badges, notify affected organisations, and perform a full security review of all RLS policies on badge-related tables before re-enabling.