| name | wa-maintain |
| description | Forever-stage. Tweak the bot's personality, refresh expired tokens, diagnose "the bot is acting weird", recover from a disconnected WhatsApp, update tools, view recent conversations. The user lives here once the bot is deployed. |
wa-maintain โ Keep the bot healthy
Goal: be the user's on-call engineer. They tell you what's wrong (or what they want to change) and you handle it. Always diagnose before changing code.
Speak Hebrew. Be calm. Most issues have a 30-second fix.
Greet based on what they said
When /wa routes here, ask:
ืืื ืืคืฉืจ ืืขืืืจ?
๐ง ืืืื ืขืื ื ืืฉืื ืืืืจ / ืื ืขืื ื / ืขื ื ืืืจ ืฉืืกืืจ ืื
โ๏ธ ืืฉื ืืช ืืช ืืืืฉืืืช, ืืืื, ืื ืืชืฉืืืืช
๐ ืืืืืืกืืค ืืชื ืชืง / ืงืื QR ืคื
๐ ๏ธ ืืขืืื ืื ืืืืืืฃ ืืืืืช ืงืืืืช
๐ ืืจืืืช ืฉืืืืช ืืืจืื ืืช
๐ซ ืืืกืื ืืกืคืจ / ืืคืชืื ืืืืื
ืืืจ โ ืชืืชืื ืืืืืื ืฉืื
Diagnostic ladder โ when something is broken
When the user says "ืืืื ืื ืขืื ื" or similar, run this 7-step ladder in order. Stop at the first failure and fix it.
Step 1 โ Wasender session status
curl -sX GET "https://www.wasenderapi.com/api/status" \
-H "Authorization: Bearer $WASENDER_API_KEY"
connected โ
โ step 2.
need_scan / disconnected โ re-run wa-setup step 4 (fetch QR, render image, user scans).
expired / logged_out โ user logged the bot out from their phone's "Linked Devices". Tell them, re-scan.
Step 2 โ Render service is live and recently deployed
curl -s "https://api.render.com/v1/services/$RENDER_SERVICE_ID" \
-H "Authorization: Bearer $RENDER_API_KEY"
status: live โ
โ step 3.
status: build_failed / update_failed โ fetch the latest deploy events, surface error in Hebrew, fix.
status: suspended โ free tier hit usage limits. Tell user.
Step 3 โ Health endpoint responds
curl -s "$RENDER_URL/healthz"
{"status":"ok"} โ
โ step 4.
- Empty / 502 โ service is asleep (Render free tier). Wait 30s, retry. If still failing: check Render logs.
Step 4 โ Webhook URL registered correctly with Wasender
Branch on setup_path from .wa-state.json:
Path A (PAT available):
curl -s "https://www.wasenderapi.com/api/whatsapp-sessions/$WASENDER_SESSION_ID" \
-H "Authorization: Bearer $WASENDER_PAT" | jq '{webhook_url, webhook_enabled, webhook_events}'
Expect webhook_url == $RENDER_URL + "/webhook/wasender", webhook_enabled: true, messages.received in events.
If wrong: re-register (PUT /api/whatsapp-sessions/{id} with the right values โ see wa-deploy step 7).
Path B (no PAT):
Ask the user to confirm visually in the dashboard:
ืชืคืชื ืืช ืืกืฉื ื-https://wasenderapi.com/dashboard, ืชืืืฅ Manage Webhook. ืืชืืื ืื:
- ืื ืืชืื ื-Webhook URL?
- ืืกืืื
messages.received?
Compare against RENDER_URL + "/webhook/wasender". If mismatched, walk them through fixing it (same instructions as wa-deploy step 7 Path B).
Step 5 โ Webhook signature matches
The most common silent failure: env var WASENDER_WEBHOOK_SECRET on Render doesn't match what Wasender is sending.
Trigger a controlled test: send "test" from any whitelisted number, then immediately:
curl -s "https://api.render.com/v1/services/$RENDER_SERVICE_ID/logs?limit=20" \
-H "Authorization: Bearer $RENDER_API_KEY"
Look for "bad signature" 401s. If found: fetch the current secret from Wasender:
curl -s "https://www.wasenderapi.com/api/whatsapp-sessions/$WASENDER_SESSION_ID" \
-H "Authorization: Bearer $WASENDER_PAT" | jq -r '.data.webhook_secret'
Update Render env WASENDER_WEBHOOK_SECRET and redeploy.
Step 6 โ Inbound messages are reaching the webhook
Check Render logs for recent POSTs to /webhook/wasender with 200 responses. If nothing arriving despite step 4+5 being correct: the user might be messaging the wrong number, or the session got muted by Wasender. Check session-logs:
curl -s "https://www.wasenderapi.com/api/whatsapp-sessions/$WASENDER_SESSION_ID/session-logs?limit=20" \
-H "Authorization: Bearer $WASENDER_PAT"
Step 6.5 โ Sender arrived as a LID (modern WhatsApp privacy mode)
If logs show inbound msg_id=... remoteJid=...@lid cleanedSenderPn=None, you've hit WhatsApp's LID privacy model. Symptoms:
- Whitelist mode rejects the user even when their phone should be allowed
- The fallback "you're not whitelisted" send 422s with
"The provided JID does not exist on WhatsApp." because the bot is trying to send to +<lid> which isn't a real phone
Why it happens. WhatsApp 2024-2025 anonymizes sender phone numbers behind LIDs (Linked-device IDs) when the sender hasn't saved the recipient's number. Wasender (Baileys-based) only receives what WhatsApp gives it.
Fix in code (should already be in wa-build's generated main.py):
- Use
remote_jid as chat_id when cleanedSenderPn is None โ preserves the @lid suffix.
is_allowed() accepts either the JID or the +phone form for matching.
- Wasender's
/send-message accepts both formats in to โ sending to <lid>@lid works.
Workarounds if the user wants whitelist-only:
- Paste the LID into
spec.audience.allowed_numbers (e.g. "124833811697866@lid") โ works but is opaque
- Have the user save the bot's number in their phone contacts; sometimes WhatsApp then promotes future messages back to phone-mode (not guaranteed)
- Switch to
audience.mode: "open" and rely on the LLM's scope rules to decline non-target users
Step 7 โ LLM call is succeeding
Check Render logs for anthropic / openai errors:
- 401 โ API key invalid / billing issue.
- 429 โ rate-limited.
- 5xx โ provider down; nothing we can do but retry.
- Tool error โ look at the specific tool that failed.
Tweaking personality / scope
User says "ืฉื ื ืืช ืืืื ืืืืืช ืืืชืจ ืจืฉืื" or "ืื ืชืืคืฉืจ ืื ืืืืจ ืขื ืืืืจืื":
- Read
bot/spec.json.
- Modify the relevant field (
tone.formality, scope.out, etc.).
- Save back.
- No code change needed โ
prompt.py reads spec.json at process start.
- To pick up the change live, restart the Render service (or bump a deploy):
curl -sX POST "https://api.render.com/v1/services/$RENDER_SERVICE_ID/deploys" \
-H "Authorization: Bearer $RENDER_API_KEY" \
-H "Content-Type: application/json" -d '{"clearCache": "do_not_clear"}'
Confirm with user:
ืฉืื ืืชื. ืืขืื ืืงื ืืืื ืืขืืื ืขื ืืืื ืืืืฉ. ืฉืื ืื ืืืืขื ืืืืืง.
Refreshing OAuth tokens (Calendar / Gmail)
Symptom: tool returns "credentials expired" or 401 from Google.
- Visit
{RENDER_URL}/auth/google from the user's browser.
- Grant permissions again.
- New refresh token saved automatically.
If the user revoked access in their Google account, the refresh token won't work โ they need to re-auth fresh.
Viewing recent conversations
The user might want to see what the bot said to a specific contact (audit). If persistence is SQLite on disk:
For non-technical users, easier: write a tiny one-off Python script locally that queries the DB and prints the last N turns. Or better โ add a /admin/conversations route to main.py that requires a basic-auth token and returns HTML.
Reconnecting WhatsApp after disconnect
If WhatsApp pulls the linked device (the user logged out from their phone, or hit the 14-day inactivity limit):
- Run
wa-setup step 4 (connect + fetch QR + render image).
- User scans.
- Status returns to
connected.
The WASENDER_API_KEY and WASENDER_WEBHOOK_SECRET don't change on reconnection โ same session, same credentials. No Render redeploy needed.
Updating a tool
User says "Gmail ืืชืืื ืืจืืฅ ืืื ืื ืฉืืื":
- Check Render logs around the failure timestamp.
- Identify the exception.
- Fix the tool file in
bot/tools/<tool>.py.
- Push to GitHub. Render auto-deploys.
- Verify with a test message.
Don't refactor adjacent code while you're in there. Surgical fixes only.
Removing a tool
cd bot
git rm tools/<tool>.py
git commit -m "remove tool: <tool>"
git push
Update .wa-state.connected_tools (remove the entry). The auto-import in tools/__init__.py won't see the missing file; the LLM won't be offered the tool.
Bot is too slow / responses are cut off
- Increase
MAX_HISTORY env var on Render (default 20 โ try 40). Trade-off: more tokens, more cost per message.
- Increase
max_tokens in agent.py (default 1024 โ 2048). Same trade-off.
- If
account_protection: true is making sends slow: in production with a known good number, you can disable it (WASENDER_ACCOUNT_PROTECTION=false env). Warn user about ban risk.
Token / API key rotated
User regenerated their Wasender PAT, Anthropic key, etc.:
- Update project-root
.env (locally) and Render env vars (remote).
- Redeploy if it's an env Render needs.
- For PAT: the PAT only matters for the plugin's own session-management calls (
wa-setup, wa-maintain, wa-deploy). The runtime bot uses WASENDER_API_KEY (per-session), which doesn't rotate unless the user explicitly regenerates it from the Wasender dashboard.
What never to do
- Don't blindly redeploy when something's broken. Diagnose first โ redeploy is rarely the fix and slows iteration.
- Don't paste secrets into the chat as you debug. Refer to env var names.
- Don't edit code on Render's web shell. Always commit + push so changes survive redeploys.
- Don't rebuild from scratch if one tool is broken. Fix the tool.