Skip to main content
Status: In Development · Playground demo coming soon. The recipe below is complete and runnable today; only the hosted playground showcase is pending.
A WhatsApp Business agent that handles inbound messages on one WABA number, knows when to hand off to a human, stays out of the way during the human conversation, and picks back up cleanly when the human releases. Memory carries across the whole arc: AI turns, human turns, and the resumption.

What you’ll build

A single-WABA inbound agent that:
  • Takes inbound WhatsApp messages and replies in-thread
  • Detects handoff signals: sentiment, keywords, explicit asks, repeat failure
  • Hands off to a human by surfacing the conversation in your agent console / Slack with full memory context
  • Goes quiet while the human owns the thread
  • Resumes when the human releases: full context, no recap
Est. build time: 60-75 minutes (WhatsApp Cloud API setup is most of it).

When to use this recipe

Build this if:
  • You have one WABA number for one product or business line
  • You have human agents available some of the time but want AI coverage the rest
  • The handoff in and out has to feel seamless to the customer
  • You’re okay with conversation continuity tied to phone number (which it is on WhatsApp)

Architecture at a glance

The “handoff state” is a single key per customer (AI_HANDOFF[phone]). When set, the AI agent stops responding. Human turns are still ingested into Synap so the AI has them when it resumes.

Stack

Prerequisites

  • A Synap API key. See Authentication
  • A WABA number, Meta Business app, verified, with a webhook configured to point at your backend
  • A System User access token with whatsapp_business_messaging scope
  • A human console / Slack channel for handoffs
  • Python: Python 3.11+
  • TypeScript: Node 18+ and Python 3.11+ on the host
TypeScript recipe runs on Node only. Pin Next.js route handlers to export const runtime = "nodejs". WhatsApp webhooks expect synchronous 200 responses within 20s: return fast and process async. See Installation → JavaScript / TypeScript SDK.

Install

Configure

Build it

1. Identity & scoping

WhatsApp gives you a stable identifier: the customer’s phone number. Use it as user_id.
  • customer_id = "<your-business>": your business, single tenant
  • user_id = <E.164 phone, hashed if you prefer>
  • conversation_id = <one per phone>: WhatsApp doesn’t have explicit sessions; treat the whole relationship as one rolling conversation
conversation_id, user_id, and customer_id must be valid UUIDs. Since these key off the phone, derive a deterministic UUID from it with uuid.uuid5(...) (Python) rather than passing the raw phone string.
If your privacy posture requires it, hash phone numbers (e.g., sha256("e164:" + phone)) before using them as user_id. Synap will treat the hash as the stable identifier; you keep raw phones in your own DB.

2. Handoff state

3. The handoff tool

The agent decides when to hand off. The tool fans out: persist a structured memory record + notify the human console.

4. The inbound webhook

WhatsApp webhooks deliver inbound messages. Return 200 fast, process async.

5. The AI agent

6. The human side

Two affordances in your console / Slack:
  1. Reply to the customer → posts a message via WABA, ingests it into memory tagged tier: "human".
  2. Release the thread → calls end_handoff(phone). Next customer message routes to the AI again, with full handoff history loaded from memory.

Run & verify

Next day, new message
The next-day reply comes from the AI again, with the human’s resolution and the reissue tracked in memory.

Customize / extend

  • Multiple WABA numbers → see Multi-WABA Shared Memory for the routing pattern.
  • Outbound campaigns + inbound → see Single-WABA Campaign + Inbound.
  • Slack as the human console → post handoff alerts into a #wa-support channel; replies posted in-thread relay back via WABA. Same pattern as Patterns → Slack Bot.
  • Auto-release on inactivity → after N minutes of no human reply, clear AI_HANDOFF automatically (Redis TTL).
  • Tier-escalation flavor → for AI→AI handoff instead of AI→human, see Tier-1 → Tier-2 Escalation.

Troubleshooting

AI replies during a human handoff
  • Race condition: webhook fired before AI_HANDOFF was set. Check is_handoff inside the async task, not in the webhook handler.
AI loses context after the human releases
  • Confirm human turns are being ingested into memory with tier: "human" metadata. If they’re missing, the AI sees a gap and may re-ask basic questions.
Webhook timeouts on Vercel
  • WhatsApp expects a 200 within 20s. Return immediately from the route handler and run handleInbound in the background. Don’t await it.
Customer gets dupe messages
  • WhatsApp redelivers webhooks that don’t 200 fast enough. Idempotency: track message_id in Redis with a short TTL and skip duplicates.
Template messages required for outbound > 24h
  • WhatsApp’s 24-hour rule: outside the customer-initiated session window, you can only send approved template messages. Track session start in Redis; refuse to send free-form replies outside the window.