# Whatsapp

> WhatsApp automation (Green API primary, WAHA fallback). Use for: send/receive messages, send voice/images/files, retrieve incoming messages (text, voice, images), transcribe voice messages. WAME = 'WhatsApp ME' = send me by WhatsApp.

- Skill: `aviz85/whatsapp` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds@latest add aviz85/whatsapp`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aviz85/whatsapp/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: aviz85 (https://skillmd.com/u/aviz85)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/aviz85/whatsapp

---


# WhatsApp Automation

Send messages via unified script with Green API (primary) + WAHA (fallback if configured).

## Quick Start - Unified Script

```bash
cd ~/.claude/skills/whatsapp/scripts

# Text message
npx ts-node send-message.ts "Hello!" --to 972501234567

# Multiline text from shell: use $'...'
npx ts-node send-message.ts $'Line 1\n\nLine 2\n- Bullet A\n- Bullet B' --to 972501234567

# Image with caption
npx ts-node send-message.ts --to 972501234567 --image /path/to/image.jpg --caption "Check this out!"

# Video with caption
npx ts-node send-message.ts --to 972501234567 --video /path/to/video.mp4 --caption "Watch this!"

# Any media file (auto-detect)
npx ts-node send-message.ts --to 972501234567 --media /path/to/any-file --caption "Here!"

# Voice message
npx ts-node send-message.ts --to 972501234567 --voice /path/to/audio.ogg

# File/document
npx ts-node send-message.ts --to 972501234567 --file /path/to/doc.pdf --caption "Here's the doc"

# Send to group
npx ts-node send-message.ts "Group message" --group 120363xxx@g.us

# Reply to a specific message (quoted reply)
npx ts-node send-message.ts "Reply!" --group 120363xxx@g.us --quote BAE5367237E13A1B

# Voice as quoted reply
npx ts-node send-message.ts --group 120363xxx@g.us --voice /tmp/voice.mp3 --quote BAE5367237E13A1B

# Dry run (preview)
npx ts-node send-message.ts "Test" --to 972501234567 --dry-run
```

## Default Numbers

| Alias | Number | Use Case |
|-------|--------|----------|
| **myself / me / test** | `$MY_PHONE` (from `config.sh`) | Default recipient |

## Sending Video & Audio — encode to AAC

WhatsApp (and Facebook) play a video **silently** if its audio track is MP3 muxed into an MP4 — the file is valid and has sound in desktop players, but the mobile app drops it. Always ship video with **AAC** audio:

```bash
# Re-encode ONLY the audio to AAC, leave the video untouched; +faststart for streaming
ffmpeg -y -i in.mp4 -c:v copy -c:a aac -b:a 192k -movflags +faststart out.mp4

# Verify it isn't silent before sending
ffmpeg -i out.mp4 -af volumedetect -f null /dev/null 2>&1 | grep mean_volume
```

Voice notes: convert the source to OGG/Opus first — raw MP3 in a container is unreliable in the app.

## Script Options

| Option | Description |
|--------|-------------|
| `"message"` | Text message (first positional argument) |
| `--to, --phone <NUMBER>` | Phone number (Israeli format accepted) |
| `--group <ID>` | Group ID (format: 120363xxx@g.us) |
| `--image <PATH>` | Attach image file |
| `--video <PATH>` | Attach video file (MP4, etc.) |
| `--media <PATH>` | Attach any media file (auto-detect) |
| `--voice <PATH>` | Send voice message (OGG/MP3) |
| `--file <PATH>` | Attach document/file |
| `--caption <TEXT>` | Caption for media files |
| `--quote <MSG_ID>` | Reply to a specific message (quotedMessageId) |
| `--dry-run` | Preview without sending |

## Multiline Formatting

When sending a WhatsApp message with line breaks from the shell, do **not** pass literal `\n` inside a normal quoted string if you want actual newlines in the final message.

Use shell ANSI-C quoting instead:

```bash
npx ts-node send-message.ts $'Hi\n\nLine 2\n- Item 1\n- Item 2' --group 120363xxx@g.us
```

Why:
- `"text with \n"` sends the characters `\` + `n` — but the script auto-converts these to real newlines
- `$'text with \n'` sends real line breaks natively

Either form works — the script normalizes literal `\n` to real newlines before sending.

**Apostrophes inside `$'...'`:** Use `'` directly — do NOT use the `'''` trick. The `'''` pattern breaks out of ANSI-C quoting, and everything after it becomes regular single-quoted (where `\n` is literal).

```bash
# CORRECT — apostrophe inside $'...'
$'I've added a check.\n\nNext line.'

# WRONG — breaks ANSI-C quoting, \n becomes literal after the apostrophe
$'I'''ve added a check.\n\nNext line.'
```

For structured WhatsApp messages, prefer:
- short opening line
- blank line
- short bullet list
- blank line
- closing line

## Formatting Rule — Match Language

**English messages:** Use regular bullets/dashes. Never use Hebrew letters (א. ב. ג.) in English text.

```
- First item
- Second item
- Third item
```

**Hebrew messages:** Every line must start with a Hebrew character for correct RTL rendering. Use Hebrew letters for list items.

```
א. פריט ראשון
ב. פריט שני
ג. פריט שלישי
```

**Don't mix:** No Hebrew letters in English messages, no dashes/numbers in Hebrew messages.

**Don't:**
```
- פריט ראשון
1. פריט שני
* פריט שלישי
```

Lines starting with non-Hebrew characters (dashes, numbers, asterisks, English) break RTL alignment in WhatsApp. Always ensure the first visible character is Hebrew.

## Provider Logic

| Content Type | Primary | Fallback |
|--------------|---------|----------|
| **Text** | Green API | WAHA (if configured) |
| **Image** | Green API | - |
| **Video** | Green API | - |
| **Voice** | Green API | - |
| **File** | Green API | - |

**Note:** WAHA is not currently installed. Green API handles all message types.

## Configuration

Credentials in `scripts/.env`:

```env
# Green API (primary provider)
GREEN_API_URL=https://7103.api.greenapi.com
GREEN_API_INSTANCE=your_instance
GREEN_API_TOKEN=your_token

# WAHA (optional fallback — uncomment if installed)
# WAHA_URL=http://localhost:3001
# WAHA_API_KEY=your_key
# WAHA_SESSION=default
```

## Phone Number Format

| Input | Normalized |
|-------|------------|
| `972501234567` | `972501234567@c.us` |
| `0501234567` | `972501234567@c.us` |
| `501234567` | `972501234567@c.us` |

## Direct API (WAHA)

For text messages only:

```bash
source ~/.claude/skills/whatsapp/config.sh

curl -s -X POST "$WAHA_URL/api/sendText" \
  -H "X-Api-Key: $WAHA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "session": "default",
    "chatId": "972501234567@c.us",
    "text": "Hello!"
  }'
```

## Check Session Status

```bash
source ~/.claude/skills/whatsapp/config.sh
curl -s -H "X-Api-Key: $WAHA_API_KEY" "$WAHA_URL/api/sessions/default"
```

## Finding Contact by Name

When user says "send WhatsApp to [name]" without phone number:

1. Use `get-contact` skill to search CRM
2. If not found → ask user for phone
3. If multiple → ask user to choose
4. If one → confirm before sending

**Always confirm contact before sending.**

## Finding a Group by Name or Project

When user says "send to [project] group" — search Green API directly (WAHA may be down):

```bash
TOKEN=$(grep "GREEN_API_TOKEN" ~/.claude/skills/whatsapp/scripts/.env | cut -d'=' -f2)
INSTANCE=$(grep "GREEN_API_INSTANCE" ~/.claude/skills/whatsapp/scripts/.env | cut -d'=' -f2)
curl -s "https://7103.api.greenapi.com/waInstance$INSTANCE/getChats/$TOKEN" | python3 -c "
import json,sys
data=json.load(sys.stdin)
for c in (data if isinstance(data,list) else []):
    if '@g.us' in c.get('id',''):
        print(c.get('id'), '|', c.get('name',''))
"
```

Then send using `send-message.ts --group <ID>`.

**Never check WAHA session before sending** — the script handles fallback automatically.

## Provider Notes

Green API is the primary provider. WAHA is not installed and only used as fallback if explicitly configured.
Just run `send-message.ts` — it uses Green API directly.

## Retrieving Messages (get-messages.ts)

Fetch incoming messages, voice recordings, and media from any contact:

```bash
# Get recent messages from a contact
npx ts-node get-messages.ts --chat 972526062921 --count 5

# Get messages from last 2 hours
npx ts-node get-messages.ts --chat 972526062921 --minutes 120

# Get all messages from a time range
npx ts-node get-messages.ts --start "2h ago" --end "now"

# Output as JSON (includes download URLs for media)
npx ts-node get-messages.ts --chat 972526062921 --json
```

**What you get:**
- Text messages with full content
- **Voice messages** with download URL (OGA/OGG format) — can be transcribed separately
- **Images** with download URL and metadata
- Timestamps, sender info, file names
- All message types (extended text, reactions, media)

**Quoted/reply messages are shown inline** — `get-messages.ts` now displays quoted context automatically with `↳ Reply to` prefix. Both `extendedTextMessage` and `quotedMessage` types are handled.

Message types: `textMessage`, `extendedTextMessage` (with `.text`), `quotedMessage` (reply with `.quotedMessage.textMessage`), `reactionMessage`, `imageMessage`

**Voice Message Workflow:**
1. Get messages with `get-messages.ts --json`
2. Extract `downloadUrl` from `audioMessage` type
3. Download the OGA file
4. Convert to WAV: `ffmpeg -i file.oga -ar 16000 file.wav`
5. Transcribe: `/transcribe --language he /path/to/file.wav`

## Waiting for Replies — Monitor Integration

Sometimes after sending a message you need the contact's reply **before you can continue** — an approval, a decision, feedback, a "how much do I owe you". Instead of stopping and asking the user to watch the chat, hand the waiting to the `Monitor` tool and keep working; each new reply arrives as a notification.

Use `scripts/watch-replies.sh` — it polls for genuinely *new* incoming messages (dedupes by id, handles text / reactions / media) and prints one line per reply, which is exactly what `Monitor` consumes. Exits only when killed (Monitor timeout / `TaskStop`).

```bash
# usage
scripts/watch-replies.sh --chat <number|groupId> [--since <epoch>] [--interval 30] [--label REPLY]
```

Launch it through the **Monitor** tool (not Bash), so replies notify you while you keep working:
- `command`: `~/.claude/skills/whatsapp/scripts/watch-replies.sh --chat 972501234567 --since "$(date +%s)" --label CLIENT`
- `timeout_ms`: how long to wait (e.g. `3600000` for an hour).
- **Always set `--since` to the moment you sent** (`$(date +%s)`), or old history re-fires as if new.

> ⚠️ **Do NOT hand-roll an inline poller in the Monitor `command`.** Passing a group id or JSON body directly in the shell breaks under zsh — `@g.us` triggers `bad math expression: illegal character: @`, and the monitor exits 1. `watch-replies.sh` takes the chat as an **argument** (`--chat <id>`), so group ids are safe. Always reach for this script; never inline the group id + curl/python in the Monitor command.

### Decide per situation whether to use it
Reach for it when the reply changes what you do next:
- you sent something that needs **approval before the next step** (a draft, a preview, a quote);
- you asked a **question** and will act on the answer;
- you're in an **iterative loop** (send fix → they review → send next);
- you started long work and want to hear back **without blocking**.

Skip it for fire-and-forget (a confirmation, an FYI, a "done") — no reply is expected, so there's nothing to watch.

### Staying in the conversation
Often the right move is to *remain engaged*: when a reply lands, respond, then **re-arm a fresh watcher** (new `--since`) for the next turn. That's a real back-and-forth — send sample → they approve → send fix → wait again — until the thread naturally closes (final approval, thanks, payment settled, or the user says wrap up). Each round: **send → watch → reply → re-watch.** Use your judgement on when the exchange is genuinely done and it's time to stop watching.

### Discipline
- Each watcher has its own dedupe file, so two watchers on the same chat double-fire — `TaskStop` the old one before arming a new one.
- Reactions surface as `(reaction 👍)` and media as `[imageMessage]`; when you need the image URL / caption / voice file, pull the full record with `get-messages.ts --json`.

## When to Use

- Send notifications or alerts
- Share images/posters
- Voice messages
- Document sharing
- Automated reminders
- Workshop follow-ups
- **Retrieve and transcribe voice messages** from customers
- **Check media attachments** from contacts

