AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Whatsapp

skill-omerlapidot-whatsapp-connector-whatsapp · by OmerLapidot

Read, search, and send WhatsApp messages through the local whatsapp-connector daemon. Use when the user asks to check WhatsApp, read or search a chat/group, summarize messages, reply, or send on WhatsApp. Reads any chat freely; sends ONLY to allow-listed chats and ONLY after the user confirms.

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-omerlapidot-whatsapp-connector-whatsapp

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-omerlapidot-whatsapp-connector-whatsapp)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
21d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Whatsapp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

WhatsApp connector

Drive the user's WhatsApp via the wa CLI, which talks to an always-on local daemon.

Always call it by absolute path: {{WA_DIR}}/bin/wa

Reading (allowed freely)

  • wa chats — list chats/groups
  • wa read "" [--limit 30 | --all] — recent messages (each has an id for reactions). --all reads the WHOLE chat (every synced message), bypassing the 200 cap — use sparingly: a busy chat can be thousands of messages (large payload + heavy on your context)
  • wa search "" [--chat ""] — find messages
  • wa members "" — who is in a group
  • wa contacts "" — resolve a name/number
  • wa media "" "" — download an image/file; prints a path you can then open

Chats are addressed by name (fuzzy) or exact id. If wa reports the name is ambiguous, show the candidates and ask which one.

Response shapes (verified against a live account)

Everything prints as JSON (or a plain string). Shapes you can rely on:

  • wa chats → array of { id, name, unread }, ordered most-recently-active first.

The id suffix is the chat type:

  • …@g.us — a group
  • …@lid — an individual person (WhatsApp's current per-contact id). The older

…@c.us form can also appear; treat BOTH as 1:1 people. To filter to real people, exclude @g.us — do NOT match on @c.us alone (modern accounts return @lid and you'll get zero results).

  • wa read{ chat, id, messages: [ … ] }, oldest→newest. Each message is

{ id, sender, ts, text, hasMedia }:

  • sender is the literal string "me" for the user's own messages, otherwise the

counterparty's chat id — so message direction is just sender === "me".

  • ts is a UNIX timestamp in seconds (multiply by 1000 for a JS Date).
  • --limit is capped at 200 (MAX_LIMIT); a busier chat returns only its most

recent 200, so a chat sitting at exactly 200 means "≥200", not exactly 200. Pass --all to bypass the cap and fetch every message — but "every" means every message currently synced into the Web session, NOT the full lifetime history. Depth is volatile: a freshly linked device backfills older messages over time, and a daemon restart flushes the loaded depth — right after a (re)start most chats read only their newest message or two and re-deepen as the session re-syncs. So a low --all count can mean "not synced yet," not "that's the whole chat." A big, warmed chat can return thousands of messages at once.

  • wa status{ state, ready, syncPercent } (plus recovery: {attempt, max}

while self-recovering); statestarting | needs-login | syncing | ready | recovering | relinking | auth-failure | disconnected.

  • Errors come back as { ok:false, error, code } with codes like

NOT_ALLOWED (chat not on the allow-list), NOT_APPROVED (human denied or the dialog timed out), AMBIGUOUS (name matched >1 chat), NOT_FOUND.

Sending (two-step — read this every time)

wa send and wa send-media do NOT transmit. They return a token + a preview. To actually send:

  1. Run wa send "" "" (or wa send-media ...). It returns { pending: true, token, chat, preview } and sends nothing.
  2. Show the user the exact chat and preview, and get an explicit "yes".
  3. Only then run wa send-confirm . The token expires in ~2 minutes.

react is single-step but still restricted to allow-listed chats. mark-read is single-step and works on any chat — it only changes the user's own read state and sends nothing outward, so it is NOT allow-list gated.

Allow-list (gated by a native dialog on the user's screen)

  • wa allow list — chats sends are permitted to. The entry * means EVERYONE.
  • wa allow add "" ["" ...] — request permission for one or more chats
  • wa allow remove "" ["" ...] — request removal (raw ids accepted)
  • wa allow all / wa allow remove all — the EVERYONE wildcard on / off

Every mutation above triggers a NATIVE DIALOG that only the human can click. The command blocks up to 60 seconds while the dialog is on screen — this is normal, wait for it. A denial, timeout, or "not approved" error is FINAL: do not retry, do not edit allowlist.json, report the outcome to the user. If a send fails with "not on the send allow-list", offer wa allow add — run it only after the user agrees, and remind them to expect the dialog.

Scheduled sends

  • wa schedule "" "" --at "HH:MM" (or --at "YYYY-MM-DD HH:MM", --in 30m, --every monday --at 09:00, --every month --on 1 --at 08:00, --media )
  • It returns a pending token like send; confirm with the user, then wa schedule-confirm .
  • wa schedule list / wa schedule cancel manage jobs. Fires re-check the allow-list.

If the daemon is down

If wa prints "daemon is not running", start it yourself:

{{WA_DIR}}/bot.sh start

On macOS this registers a launchd agent (com.whatsapp-connector.daemon) with KeepAlive + RunAtLoad, so the daemon stays up for as long as the Mac is on — it relaunches automatically after a crash and at every login. On Linux bot.sh falls back to a nohup background process with a pidfile. Then poll wa status until ready: true (see "Still syncing").

Do NOT run node index.js yourself as the long-term daemon — that process dies with its terminal. bot.sh start is what makes it persistent.

One exception — a fresh, never-linked machine needs a human for the QR. If after starting, wa status reports state: "needs-login" (no WhatsApp session on disk yet), the login QR renders only to a foreground terminal, which you cannot scan. Ask the user to run node index.js in a terminal from {{WA_DIR}}, scan it via WhatsApp → Settings → Linked Devices, wait for ready, then Ctrl+C and run {{WA_DIR}}/bot.sh start for the persistent daemon.

Still syncing (just after a restart)

wa status returns { state, ready, syncPercent }. Right after a (re)start the daemon reports state: "syncing" while it finishes delivering messages that queued while it was off, and commands fail with "not ready (state: syncing)". This is normal and self-clears within a few seconds (up to ~60s if a lot queued). Poll wa status until ready: true, then proceed — don't tell the user the daemon is down.

Auto-recovery (a stuck daemon heals itself)

If WhatsApp Web authenticates but never signals ready (an intermittent hang, usually just after a restart), the daemon now recovers on its own — and shows the stage in wa status:

  • state: "recovering" with recovery: {attempt, max} — it's recycling the

client, reusing the on-disk session (no QR). Just keep polling wa status; do NOT restart the daemon yourself while it's recovering.

  • state: "relinking" — the soft retries were exhausted, so it moved the session

aside and a new QR scan is required. This becomes needs-login once the QR is ready: tell the user to scan it (see "If the daemon is down" — the QR only shows in a foreground node index.js).

So a temporary recovering/syncing status is the daemon fixing itself, not a failure — report it calmly and wait.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.