Skip to content

Add WhatsApp as a second front door onto the handover pipeline - #3

Open
Laolex wants to merge 1 commit into
mainfrom
feat/whatsapp-inbound
Open

Add WhatsApp as a second front door onto the handover pipeline#3
Laolex wants to merge 1 commit into
mainfrom
feat/whatsapp-inbound

Conversation

@Laolex

@Laolex Laolex commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

The friction was never the feature set. A carer already has the voice note in WhatsApp; Memreda asked them to remember to open a website. This accepts messages where the care conversation already happens and runs them through the same source → handover pipeline as the web app and the email channel.

A WhatsApp message becomes a note source. A voice note becomes a voice source. Nothing downstream changes.

Identity is the hard part, not transport

A verified X-Hub-Signature-256 proves Meta sent the request. It proves nothing about who owns the number. So:

  • Signature verification runs against the raw bytes before the body is trusted; failures get 401.
  • With no WHATSAPP_APP_SECRET set, every delivery is rejected — the channel is off by default.
  • A number only becomes a sender after a signed-in member mints a six-digit code in Settings → Link WhatsApp. It is hashed, expires in fifteen minutes, is single-use, and binds exactly one number.
  • An unlinked number gets instructions and writes nothing.

Other decisions worth reviewing

Voice notes are content-sniffed, not trusted. Media is downloaded from Graph in two hops (the lookup returns a URL, and the download still needs the bearer token), then run through the same sniffAudioType check as any upload — the existing defence against Safari mislabelling its own recordings.

Deduplication reuses inbound_message_ids. Same table the email channel uses, keyed on WhatsApp's message id.

Deliberate divergence from the email path: forwarded email becomes an owner-reviewable draft because the caregiver did not write it. WhatsApp messages go straight to a handover, because the member typed or recorded them on purpose — exactly as they would in the app. Removing that step is the point of the channel. Everything stays editable and erasable afterwards. This is the decision most worth pushing back on if you disagree.

New tables

user_phones, phone_link_codes — both additive, created in the existing CREATE TABLE IF NOT EXISTS block.

Verification

npm test — 27/27 pass (17 existing, 10 new). New coverage: the subscription handshake, unsigned and wrongly signed deliveries, unlinked numbers, code binding and single-use replay, text → handover, duplicate message ids, voice → Graph download → transcript → handover, unsupported attachments, and delivery-status callbacks.

Not code — what still blocks going live

A Meta Business account, WhatsApp Business Platform access, a verified business number, and the webhook pointed at HTTPS on memreda.xyz. Replies are always inside the 24-hour service window so they should not need approved templates, but Meta's current terms and conversation pricing have not been verified.

Not merged yet, by request.

The friction was never the feature set: a carer already has the voice note
in WhatsApp, and Memreda asked them to remember to open a website. This
accepts messages where the care conversation already happens and runs them
through the same source-to-handover pipeline as the web app and email.

A verified X-Hub-Signature-256 proves Meta sent the request but says
nothing about who owns the number, so a number only becomes a sender after
a signed-in member mints a single-use, hashed, fifteen-minute code and
sends it in. Unlinked numbers get instructions and write nothing.

Voice notes are downloaded from Graph and content-sniffed like any upload
rather than trusting the declared mime type. Messages are deduplicated on
WhatsApp's message id through the existing inbound_message_ids table.
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.

1 participant