Skip to content

feat(channels): WhatsApp concierge channel (shared number + phone routing)#3847

Closed
vibegui wants to merge 1 commit into
vibegui/org-chat-integrationfrom
vibegui/whatsapp-concierge
Closed

feat(channels): WhatsApp concierge channel (shared number + phone routing)#3847
vibegui wants to merge 1 commit into
vibegui/org-chat-integrationfrom
vibegui/whatsapp-concierge

Conversation

@vibegui

@vibegui vibegui commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds WhatsApp as a third channel, stacked on the Teams/Discord channels PR (#3749 — this PR's base is vibegui/org-chat-integration, so the diff is WhatsApp-only). Merge #3749 first.

Unlike the per-org Teams/Discord bots, WhatsApp uses one shared decoCMS concierge number. A user verifies their phone once in their profile; inbound messages route by phone → user → org and the org's agent answers as that real user.

How it works

  • Verification (inbound-only): Studio issues a code shown in the user's profile; the user texts it to the concierge number; that inbound proves ownership and links the number. No phone typing, no Studio→worker send for verification.
  • Routing: global POST /api/whatsapp/ingest (shared-secret auth, async) — verify code → resolve phone→user → pick the target org (saved default + in-chat numbered pick-list / switch) → run the agent as the real user → reply via the worker's /send.
  • Identity: one verified phone → one Studio user, across all their orgs.
  • Enable-only channel: no per-org credentials/bot — CHANNELS_LIST advertises WhatsApp (setupKind: "shared"), CHANNEL_CREATE activates immediately with a chosen agent. Only available when WHATSAPP_WORKER_URL/TOKEN, WHATSAPP_INGEST_SECRET, WHATSAPP_CONCIERGE_NUMBER are set.
  • The deployed Cloudflare worker (mangabeira) stays the WABA owner and becomes a thin relay — integration prompt in .context/whatsapp-worker-integration-prompt.md.

Key pieces

  • migration 110-user-phones; 109-channels.bot_user_id made nullable
  • UserPhoneStorage + phone canonicalization; whatsapp-worker client; whatsapp-ingest route
  • runChannelTurn generalized (botUserIduserId) so the real user can answer
  • profile tools PHONE_LINK_START / PHONE_GET / PHONE_DELETE (basic-usage)
  • profile "Link WhatsApp" UI + "Add WhatsApp" enable dialog

Testing

  • bun run check / lint / fmt:check / knip clean; build:server ok.
  • Unit tests: org-selection resolver + phone canonicalization (+ existing channel adapter tests).
  • Manual: curl the ingest endpoint with a mock worker /send, or wire the real worker per the prompt. Run bun run --cwd=apps/mesh migrate first.

🤖 Generated with Claude Code


Summary by cubic

Adds WhatsApp as a shared-number concierge channel. Members link their phone once; inbound messages route phone → user → org, and the agent runs as the real user.

  • New Features

    • Global ingest endpoint with secret auth; verifies one-time codes, resolves phone → user → org (remembered org, number pick-list, or switch).
    • Runs agent turns as the real user (runChannelTurn now uses userId) and replies via the WhatsApp worker /send.
    • Enable-only channel: listed when configured, created without a bot or credentials, activates immediately with a chosen agent.
    • Profile tools and UI for phone linking: PHONE_LINK_START, PHONE_GET, PHONE_DELETE; “Link WhatsApp” in profile with live polling.
    • Settings UI: “Enable WhatsApp” dialog to pick the answering agent.
    • Storage and utils: UserPhoneStorage, phone canonicalization/masking, and unit tests for org selection and phone utils.
  • Migration

    • DB: new user_phones table; channels.bot_user_id is nullable for WhatsApp.
    • Config: set WHATSAPP_WORKER_URL, WHATSAPP_WORKER_TOKEN, WHATSAPP_INGEST_SECRET, WHATSAPP_CONCIERGE_NUMBER.
    • Deploy/enable the WhatsApp worker, then run migrations and enable WhatsApp per org by selecting an agent. Members link their phone from their profile.

Written for commit 6dfad87. Summary will update on new commits.

Review in cubic

…ting)

Adds WhatsApp on top of the Teams/Discord channels feature, with a different
shape: one shared decoCMS concierge number. A user verifies their phone once in
their profile (Studio issues a code, the user texts it to the number); inbound
messages route by phone → user → org and the org's agent answers AS that real
user.

- migration 110-user-phones (verified phone link, pending code, selected org);
  109-channels bot_user_id made nullable (WhatsApp has no synthetic bot)
- UserPhoneStorage + phone canonicalization
- settings: WHATSAPP_WORKER_URL/TOKEN, WHATSAPP_INGEST_SECRET, CONCIERGE_NUMBER
- whatsapp-worker client (Studio→worker /send) + global ingest route
  /api/whatsapp/ingest (secret auth, async): inbound-only code verification,
  phone→user→org resolution with default-org + in-chat pick-list, runs the agent
  as the real user via the generalized runChannelTurn (botUserId→userId)
- profile MCP tools PHONE_LINK_START / PHONE_GET / PHONE_DELETE (basic-usage)
- WhatsApp as an enable-only channel (CHANNELS_LIST setupKind "shared";
  CHANNEL_CREATE skips the bot and activates immediately)
- frontend: profile "Link WhatsApp" (show code + poll) and a per-platform
  "Add WhatsApp" enable dialog (pick agent)
- unit tests: org-selection resolver + phone canonicalization

Stacked on the Teams/Discord channels PR. Worker integration prompt:
.context/whatsapp-worker-integration-prompt.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vibegui

vibegui commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #3850 — WhatsApp is now based off main (standalone), not stacked on the Teams/Discord channels PR.

@vibegui vibegui closed this Jun 12, 2026
@vibegui vibegui deleted the vibegui/whatsapp-concierge branch June 12, 2026 13:26
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