Skip to content

feat(notifications): SMS opt-in/opt-out consent ledger - #1863

Merged
Smartdevs17 merged 2 commits into
Smartdevs17:mainfrom
zarah-s:feat/1747-sms-consent
Oct 26, 2026
Merged

Smartdevs17 merged 2 commits into
Smartdevs17:mainfrom
zarah-s:feat/1747-sms-consent

Conversation

@zarah-s

@zarah-s zarah-s commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a phone-level SMS consent ledger so we can answer, for any number,
whether we may text it, since when, where that decision came from, and why —
the provenance the bare provider optOutStore does not record.

  • SmsConsentService keeps one current SmsConsent record per number plus an
    append-only audit event log. Recording a decision always appends an event,
    even when the status is unchanged, so a replayed inbound STOP is still
    attributable.
  • source is one of web_form, inbound_sms, admin, import, api,
    system; inbound STOP/START records the matched keyword as the reason and the
    Twilio MessageSid for provenance.
  • Opt-out is honoured in two places: the shared provider optOutStore is
    written so the low-level send path refuses opted-out numbers, and
    isOptedOut(phone) lets higher layers check before a send.
  • Express consent latches (explicitOptIn), so a later system-level reset does
    not silently revoke a deliberate opt-in.

Routes

Route Purpose
GET /api/sms-consent/:phone Current consent record
GET /api/sms-consent/:phone/events Consent audit trail
GET /api/sms-consent/user/:userId Records owned by a user
POST /api/sms-consent/:phone/opt-out Record an opt-out
POST /api/sms-consent/:phone/opt-in Record an opt-in

Changes

  • Backend: smsConsentService.ts, smsConsentRouter.ts, wired into
    container.ts and server.ts; smsInboundHandler.ts now records consent on
    STOP/START.
  • DB: migration 016_sms_consent.sql.
  • Frontend: src/store/smsConsentStore.ts, SmsConsentScreen, route +
    Settings entry under Privacy.
  • Docs: new "SMS consent" section in docs/NOTIFICATIONS.md.

Testing

  • smsConsentService.test.ts (15) and smsConsentRouter.test.ts (10) pass
    under jest.backend.config.js.
  • src/store/__tests__/smsConsentStore.test.ts (11) passes.
  • SmsConsentScreen.test.tsx follows the RN screen-test convention (the local
    jest-expo preset is broken per AGENTS.md, so it runs in CI).
  • tsc unchanged at the pre-existing baseline; ESLint clean on touched files.

closes #1747

zarah-s and others added 2 commits October 20, 2026 14:54
Record phone-level SMS consent with provenance (web form, inbound STOP/START,
admin) and an append-only audit trail, honouring opt-outs in the shared
provider store. Adds the /api/sms-consent router, migration 016, an
on-device store and an SMS Consent screen linked from Settings.
@Smartdevs17
Smartdevs17 merged commit c702719 into Smartdevs17:main Oct 26, 2026
11 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add SMS opt-in/opt-out management

2 participants