Event check-in

Members check themselves — and their household — in to events via QR. The QR is the share surface; the actual record is one row per attendee in event_checkins. This is also the first producer of attendance records: a future dashboard Attendance KPI (Phase 6) reads from this table.

The check-in surface is module-gated via module_checkin in Community settings. When the module is off, the admin QR option and the member check-in entry points hide; the link itself still resolves so an existing link an admin already shared keeps working during the toggle window.

Per-event QR (admin)

Open Events in the admin sidebar. On any non-trashed event, the row's overflow menu now carries Check-in QR. Tapping it opens a sheet with:

  • The QR image — sized for a phone scan from across a foyer.
  • The deep-link URL below it (https://psalmly.app/c/<slug>/checkin/<code>). Inside the Psalmly app that URL is also reachable via the psalmly:// scheme.
  • Download PNG + Copy link — print it for a kiosk poster, share it in the volunteer team chat, or paste it into a Sunday-morning email.
  • A live attendance list that updates every time someone scans in.

The checkin_code is opaque and unique — guessing one is not viable. Rotating a code is a future fast-follow; for now if a code leaks the admin should delete + recreate the event (a future "Reset code" button will avoid that).

Self check-in (member)

Two paths:

  • Mobile (in-app): on the church Home, tap the Check in quick-action tile (shown when the check-in module is on) → the in-app camera opens → point at the event QR and the app drops into the Confirm view automatically.
  • Web / OS camera: scanning the printed QR with the phone's camera — or opening the deep link directly (paste into the browser, follow a shared link) — opens the web check-in. The QR encodes an https URL and the app declares only the psalmly:// scheme (no associated domains), so an OS-camera scan resolves on the web, not in the app. The link routes to /c/<slug>/checkin/<code> and resolves into the same Confirm view.

In Confirm, the member sees the event identity (title + start + place) plus one toggle per subject:

  • Me — the signed-in member themselves.
  • One toggle per household member (a parent's kids show up here from the 4.2 household model). The viewer's own row is excluded — that's the Me toggle.

Toggle the people checking in and tap Check in. Idempotent: a duplicate check-in (re-scanning by accident) shows "Already checked in" — no error, no double count.

What's stored

Each event_checkins row stamps:

ColumnMeaning
event_idThe event the check-in is for.
church_idDenormalised; lets RLS gate without a join.
user_id OR household_member_idThe attendee. Exactly one is set.
checked_in_atWhen the row was inserted.
checked_in_byThe signed-in member who performed the check-in (always = auth.uid() on insert).

A (event_id, user_id) / (event_id, household_member_id) partial unique index makes double-checkin a no-op (Postgres 23505).

Who sees what

ItemMemberAdmin
Their own check-in
Their household's check-ins
Another member's check-in
The full per-event attendance list

RLS enforces all of this on the database — the client doesn't filter.

Privacy + safety

  • Minor data stays in-household. A child's check-in is visible to any adult in their household and to church admins; never to other members of the church.
  • Check-in does NOT post to the prayer board or any community feed. It's an attendance record, not a social signal.
  • The QR is not a credential — knowing the code lets you confirm attendance for yourself at a specific event. RLS still demands the caller is a member of the church, and a leader/admin can audit any anomaly via the attendance list.

Future / fast-follows

  • Code rotation — a button on the admin sheet to refresh checkin_code (invalidates the old QR). Useful if a code leaks or for recurring events that re-use a printed poster.
  • Children's check-in with printed labels + guardian claim tags — Phase 6. This slice is the lightweight precursor.
  • Geofenced / auto-checkin — out of scope (privacy + battery).
  • Dashboard Attendance KPI consuming these rows — Phase 6 (the data spine ships now; the metric lands later).