Wa deploy
Stage 4. Push the generated bot to GitHub, deploy on Render.com, register the public webhook URL with Wasender, and verify a real round-trip message works.From its SKILL.md
npx -y skills add jackvidal/whatsapp-agent-builder --skill wa-deployAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.7 KB, as published. Nobody here has run it
wa-deploy — Take the bot live
Goal: get the bot onto a public HTTPS URL so Wasender can deliver webhooks to it. Render.com is the default — free tier is enough for a personal bot, and the deploy is a single CLI call.
Speak Hebrew. Ask before doing anything that costs money or makes the bot reachable.
Step 1 — Frame the deploy
הבוט שלך מוכן. עכשיו נעלה אותו לאוויר.
נשתמש ב-Render.com — שירות שמריץ את הבוט בענן. יש להם תוכנית חינמית שמספיקה לרוב המקרים. אם הבוט הופך עמוס נשדרג ל-$7 לחודש.
מוכן?
Step 2 — Push the bot to GitHub
The bot needs to live in a Git repo for Render to deploy it.
Ask:
יש לך חשבון GitHub?
- כן, ואני מחובר ל-
ghCLI כאן- כן, אבל לא מחובר
- אין לי
For 1: skip ahead.
For 2: tell them to run gh auth login and come back.
For 3: walk them through creating one at github.com/signup, then gh auth login.
Then:
cd bot
git init
git add .
git commit -m "initial commit: WhatsApp agent generated by WhatsApp Agent Builder"
gh repo create wa-bot-${BOT_NAME_SLUG} --private --source . --remote origin --push
Capture the repo URL from gh output. Tell the user:
יצרתי לך ריפו פרטי ב-GitHub: {repo_url}. הקוד שלך נשמר שם.
Step 3 — Sign up at Render
Ask:
יש לך חשבון Render?
- כן
- לא — נירשם עכשיו
For 2:
- גש ל https://render.com/register
- הירשם עם GitHub (הכי קל) או אימייל.
- כשסיימת, תגיד לי "מוכן".
Step 4 — Get a Render API key
בשביל לפרוס מכאן, אצטריך מפתח API של Render:
- גש ל https://dashboard.render.com/u/settings#api-keys
- צור מפתח חדש בשם "claude-code".
- הדבק כאן.
Save to project-root .env as RENDER_API_KEY. Don't echo back.
Step 5 — Create the Render service
Use the Render API (POST /v1/services). The bot is a web_service.
Render API schema notes (verified live, 2026-05):
envVarsis top-level in the request body, not insideserviceDetails.buildCommandandstartCommandgo insideserviceDetails.envSpecificDetails(NOT top-level, NOTserviceDetailsdirectly). Putting them at the top level returns"buildCommand is required for non-static, non-docker services"even though they're set there.pythonVersionalso goes insideenvSpecificDetails.- Use
runtime: "python"(not the olderenv: "python"— Render accepts both butruntimeis the current key).
Use Python to build the JSON body so secrets stay in env vars and never appear in shell command output:
set -a; source "$ENV_FILE"; set +a
export OWNER_ID="..." # from GET /v1/owners
python <<'PYEOF' > /tmp/render_create.json
import json, os
body = {
"type": "web_service",
"name": "wa-bot-" + os.environ["BOT_NAME_SLUG"],
"ownerId": os.environ["OWNER_ID"],
"repo": os.environ["GITHUB_REPO_URL"],
"branch": "main",
"autoDeploy": "yes",
"rootDir": "",
"envVars": [
{"key": "WASENDER_API_KEY", "value": os.environ["WASENDER_API_KEY"]},
{"key": "WASENDER_WEBHOOK_SECRET", "value": os.environ["WASENDER_WEBHOOK_SECRET"]},
{"key": "WASENDER_ACCOUNT_PROTECTION", "value": "true"},
{"key": "ANTHROPIC_API_KEY", "value": os.environ["ANTHROPIC_API_KEY"]},
{"key": "LLM_MODEL", "value": "claude-haiku-4-5"},
{"key": "MAX_HISTORY", "value": "20"},
],
"serviceDetails": {
"runtime": "python",
"plan": "free",
"region": "singapore", # frankfurt for Israel, oregon for Americas, singapore for Asia
"envSpecificDetails": {
"pythonVersion": "3.12.7",
"buildCommand": "pip install -r requirements.txt",
"startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT",
},
"healthCheckPath": "/healthz",
},
}
print(json.dumps(body))
PYEOF
curl -s -X POST "https://api.render.com/v1/services" \
-H "Authorization: Bearer $RENDER_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/render_create.json
rm -f /tmp/render_create.json
OWNER_ID comes from GET /v1/owners first:
curl -s "https://api.render.com/v1/owners" \
-H "Authorization: Bearer $RENDER_API_KEY" | jq -r '.[0].owner.id'
Region: pick frankfurt for Israeli users (lowest latency to most Israeli ISPs), oregon for Americas, singapore for Asia/Pacific.
Save the returned service.id and service.serviceDetails.url to .wa-state.json as render_service_id and render_url.
Step 6 — Wait for the build
Poll GET /v1/services/{id}/deploys?limit=1 until status becomes live. Show progress in Hebrew:
בונה... (~2 דקות) מתקין תלויות... מפעיל... 🎉 הבוט באוויר ב: {render_url}
Verify:
curl -s "$RENDER_URL/healthz"
Expect {"status":"ok"}. If it fails, fetch the latest deploy logs (GET /v1/services/{id}/deploys/{deployId}/events) and surface the error in Hebrew.
Step 7 — Register the webhook URL with Wasender
Now the critical bit: tell Wasender where to deliver inbound messages.
Branch on setup_path from .wa-state.json:
Path A (PAT available) — programmatic
curl -sX PUT "https://www.wasenderapi.com/api/whatsapp-sessions/$WASENDER_SESSION_ID" \
-H "Authorization: Bearer $WASENDER_PAT" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "'$RENDER_URL'/webhook/wasender",
"webhook_enabled": true,
"webhook_events": ["messages.received", "session.status"]
}'
Verify:
curl -sX GET "https://www.wasenderapi.com/api/whatsapp-sessions/$WASENDER_SESSION_ID" \
-H "Authorization: Bearer $WASENDER_PAT" | jq '.data.webhook_url'
Should return the Render URL.
Path B (no PAT, dashboard-managed session) — manual
The user has to set the webhook URL via the dashboard. Tell them (in Hebrew):
עכשיו רק תעדכן את ה-Webhook ב-Wasender ידנית:
- גש ל-https://wasenderapi.com/dashboard ופתח את הסשן שלך.
- לחץ על Manage Webhook (כפתור ימני עליון).
- הדבק את הכתובת הזו בשדה Webhook URL:
{RENDER_URL}/webhook/wasender- סמן את האירוע
messages.received(וגםsession.statusאם רוצים התראה על ניתוקים).- חשוב: וודא שהשדה Webhook Secret זהה למה שיש ב-
.envשלך (WASENDER_WEBHOOK_SECRET). אם לא, או החלף את אחד מהם, או צור חדש בשני המקומות.- לחץ Save.
תגיד לי "שמרתי" ואבדוק שהכל עובד.
Wait for confirmation. Then proceed to step 8.
Step 8 — Real round-trip test
Tell the user:
הכל מוכן. שלח עכשיו הודעה אמיתית מהטלפון שלך אל {WASENDER_PHONE}. אני אבדוק שהבוט קיבל וענה.
Wait 30 seconds. Then check Render logs:
curl -s "https://api.render.com/v1/services/$RENDER_SERVICE_ID/logs?limit=20" \
-H "Authorization: Bearer $RENDER_API_KEY"
Look for the inbound webhook line. If found and the user confirms they received a reply on their phone:
🚀 הבוט שלך חי! מספר {WASENDER_PHONE} עונה עכשיו אוטומטית.
If no inbound seen after 60 seconds, run the diagnostic from wa-maintain (don't make the user wait — proactively diagnose).
Step 9 — Update state
Update .wa-state.json:
{
"current_stage": "connect",
"completed_stages": ["setup", "characterize", "build", "deploy"],
"render_service_id": "<id>",
"render_url": "<url>",
"last_updated": "<ISO>"
}
Tell the user:
השלבים הבאים אופציונליים — להוסיף לבוט יכולות נוספות (יומן, מייל, תזכורות מתמשכות) או לבחור איפה ההיסטוריה נשמרת. רוצה להמשיך, או לעצור כאן?
If they want more: route to wa-connect. If stop: route to wa-maintain (idle mode).
Common pitfalls
- Render free tier sleeps after 15 min idle. First message after sleep takes ~30s to wake. Mention this to the user. Suggest the $7 paid tier if always-on matters.
- 404 immediately after a fresh deploy. Even on first deploy, Render's edge can return
404 Not Found(10 bytes, plain text) for ~10–60s while the service is still spinning up internally. The deploy-status API may already saylivewhile curl gets 404s. Don't panic and don't redeploy — wait 30s and retry/healthz. If it's still 404 after 90s, then something is genuinely wrong (checkGET /v1/services/{id}forsuspended, and check deploy events). - Build fails on Python version. If Render reports "no matching Python", check
.python-versionand/or setPYTHON_VERSION=3.12.7env var on the service. - Webhook signature mismatch. Triple-check
WASENDER_WEBHOOK_SECRETenv var on Render matches the one Wasender returned at session-create time. If user regenerated the secret in dashboard, update both Render env andbot/.envlocally. - Wasender returns 422 on PUT session. Some fields may be read-only. If updating webhook fails, try
POST /api/whatsapp-sessions/{id}/webhook(some accounts have a dedicated webhook endpoint — verify in their dashboard). - Account protection rate limits. With
account_protection: true, the bot can only send 1 message per 5 seconds. If a long reply gets split into multiple sends (or there's a burst), they'll queue. Mention this if user complains "the bot sometimes feels slow."