@chore-o-matic/eve-whatsapp
v0.1.0
Published
WhatsApp Cloud API (Meta Graph) channel for eve agents.
Maintainers
Readme
@chore-o-matic/eve-whatsapp
A reusable eve channel that connects an agent to WhatsApp via Meta's WhatsApp Cloud API (Facebook Graph API). It receives webhooks, starts/resumes an eve session per sender, and delivers replies — text and native, tappable contact cards — back over the Graph API.
It mirrors how eve ships its own channels (telegramChannel, twilioChannel): a one-line
factory you re-export from agent/channels/<name>.ts.
Install
npm install @chore-o-matic/eve-whatsappeve is a peer dependency (^0.16) — the channel binds to your app's eve instance, so
there is exactly one copy of eve. No other runtime dependencies.
Use
// agent/channels/whatsapp.ts
import { whatsappChannel } from "@chore-o-matic/eve-whatsapp";
export default whatsappChannel(); // reads WHATSAPP_* from the environmentThe file stem (whatsapp.ts) becomes the channel id. The webhook mounts at
/eve/v1/whatsapp by default — that URL is both your route and the Meta webhook callback
URL. Override it with the route option if you need a different path.
Configuration
Every credential falls back to an environment variable, so whatsappChannel() with no args
is the common case. Override via credentials, or change behavior with the other options:
whatsappChannel({
credentials: { phoneNumberId: "...", accessToken: "..." }, // else from env
route: "/eve/v1/whatsapp", // default; the GET+POST mount and Meta callback URL
handoffFormat: "contacts", // "contacts" (default) | "vcard"
onMessage: (msg) => ({ message: msg.text, auth: {/* ... */}, continuationToken: "..." }),
});| Env var | Purpose |
| -------------------------- | ------------------------------------------------------------ |
| WHATSAPP_ACCESS_TOKEN | Permanent system-user token (outbound sends) |
| WHATSAPP_PHONE_NUMBER_ID | Business phone number id (outbound endpoint) |
| WHATSAPP_APP_SECRET | Meta app secret — verifies X-Hub-Signature-256 on inbound |
| WHATSAPP_VERIFY_TOKEN | String you choose for the webhook subscription handshake |
| WHATSAPP_API_VERSION | Graph API version (optional; defaults to v21.0) |
handoffFormat: "contacts"(default) promotes aBEGIN:VCARD…END:VCARDblock in the agent's reply into a native, tappable WhatsApp contact card; surrounding text is sent separately."vcard"leaves the card inline as text. No agent changes needed either way.onMessageoverrides inbound dispatch (auth, continuation token, filtering). The default uses the verified sender phone as the principal id and one session per sender↔business-number pair.
How it works
GET /— Meta's webhook subscription handshake; echoeshub.challengewhenhub.verify_tokenmatchesWHATSAPP_VERIFY_TOKEN.POST /— verifiesX-Hub-Signature-256(HMAC-SHA256 of the raw body, constant-time) before parsing, extracts messages from the Cloud API envelope, and starts/resumes a session viasend(). Returns200immediately; replies are delivered from themessage.completedevent so Meta's ~5s webhook budget isn't blocked.- Identity is taken from the verified webhook (
from), never message content. The phone is normalized to E.164 (+-prefixed) and exposed to your tools atctx.session.auth.initiator.principalId.
Meta setup (one-time)
- Create a Meta app, add the WhatsApp product, and get a business phone number id and a permanent system-user access token.
- Deploy your agent so
/eve/v1/whatsappis publicly reachable. - In the Meta App dashboard → WhatsApp → Configuration:
- Callback URL:
https://<your-deployment>/eve/v1/whatsapp - Verify token: the value of
WHATSAPP_VERIFY_TOKEN - Subscribe to the
messagesfield.
- Callback URL:
eve does not register the webhook for you (same as the built-in Telegram channel).
Development
npm run build # tsc -> dist/
npm test # hermetic unit tests (test/, offline; runs against dist/)
npm run typecheckThe package ships compiled JS + .d.ts (eve's CLI compiles the host app, not
node_modules, so the library is pre-built).
End-to-end test (opt-in, real API)
npm run test:e2e hits the real WhatsApp Cloud API with a token. It is kept out of
npm test so the default suite stays offline.
- Copy
.env.exampleto.env.local(gitignored) in this package directory and set at leastWHATSAPP_ACCESS_TOKENandWHATSAPP_PHONE_NUMBER_ID. - Run
npm run test:e2e.
cp .env.example .env.local # then fill in the token
npm run test:e2e- Auth check (always): a read-only Graph call that proves the token + phone number id are valid. Sends nothing, charges nothing.
- Real send (opt-in): set
WHATSAPP_E2E_RECIPIENTto an E.164 number to actually send a message via the shippedWhatsAppClient.sendText. The recipient must be within the 24-hour customer-service window or Meta returns a re-engagement error.
Without credentials the suite skips with a note pointing at .env.local.
