DEV Community

Cover image for How I Connected WhatsApp to AWS & GCP Using a Custom OpenClaw Skill — Full Tutorial
Parul Malhotra
Parul Malhotra

Posted on

How I Connected WhatsApp to AWS & GCP Using a Custom OpenClaw Skill — Full Tutorial

OpenClaw Challenge Submission 🦞

This is a submission for the OpenClaw Writing Challenge


When I first installed OpenClaw, I was blown away by the built-in capabilities — email, calendar, web browsing, shell access. But the real magic is the Skills system: the ability to extend OpenClaw with custom plugins so it can do literally anything your machine can do.

I built CloudClaw — a skill that lets me control AWS EC2 VMs from WhatsApp. In this tutorial, I'll walk you through exactly how to build a custom OpenClaw skill from scratch, using CloudClaw as the working example. By the end, you'll be able to connect OpenClaw to any API or tool you want.

What you'll learn:

  • How OpenClaw Skills work (the SKILL.md system)
  • How to write Python scripts that OpenClaw can call via exec
  • How to connect WhatsApp to OpenClaw in 2 minutes
  • The full pattern for any API integration

Let's build.


Prerequisites

  • macOS (or Linux) with Node.js 22.14+ installed (Node 24 recommended)
  • Python 3.9+ installed
  • An AWS account (free tier is enough to follow along)
  • An API key from a model provider (Anthropic, OpenAI, Google, etc.)

Step 1 — Install OpenClaw

OpenClaw runs as a local background daemon on your machine. Install it with the official one-liner:

# macOS / Linux
curl -fsSL https://openclaw.ai/install.sh | bash
Enter fullscreen mode Exit fullscreen mode

Then run the onboarding wizard — it sets up everything interactively:

openclaw onboard --install-daemon
Enter fullscreen mode Exit fullscreen mode

The wizard guides you through:

  1. Choosing your LLM (Gemini, Claude, GPT-4, local Ollama — your choice)
  2. Entering your API key
  3. Installing the gateway daemon (runs OpenClaw 24/7 in the background)
  4. Naming your assistant

After onboarding, verify the gateway is running:

openclaw gateway status
Enter fullscreen mode Exit fullscreen mode

Step 2 — Understand the Skills System

An OpenClaw Skill is just a folder with a SKILL.md file inside it, placed in your OpenClaw skills directory:

~/.openclaw/skills/
└── your-skill-name/
    ├── SKILL.md          ← Required: defines the skill
    └── scripts/          ← Optional: your supporting code
        └── your_script.py
Enter fullscreen mode Exit fullscreen mode

The SKILL.md file has two parts:

Part 1: YAML Frontmatter (metadata)

---
name: cloud-manager
description: Manages AWS and GCP cloud instances via natural language commands.
user-invocable: true
triggers:
  - start vm
  - stop vm
  - list vms
metadata:
  openclaw:
    requires:
      bins: ["python3"]
---
Enter fullscreen mode Exit fullscreen mode

Key fields:

  • name — identifier for the skill
  • description — used by OpenClaw to decide when to activate this skill
  • user-invocable: true — the user can activate it directly by sending a message
  • triggers — hint phrases for when the skill should fire
  • metadata.openclaw.requires.bins — binaries the skill needs (e.g. python3, aws)

Part 2: Markdown Instructions (the brain)

After the frontmatter, you write plain Markdown telling OpenClaw's LLM:

  • What the skill does
  • When to use which action
  • How to call your scripts
  • How to format the response

This is the real magic. You're writing a structured prompt that becomes the AI's "operating manual" for this skill. The more clearly you write it, the better it performs — no code needed for the intent layer.


Step 3 — Write the SKILL.md

Create the skill directory:

mkdir -p ~/.openclaw/skills/cloud-manager/scripts
Enter fullscreen mode Exit fullscreen mode

Create ~/.openclaw/skills/cloud-manager/SKILL.md with this content:

---
name: cloud-manager
description: >
  Manages AWS EC2 instances and GCP Compute Engine instances via natural language.
  Can start, stop, restart, list, and monitor cloud virtual machines.
user-invocable: true
triggers:
  - start vm
  - stop vm
  - restart vm
  - list vms
  - check cpu
  - check memory
  - cloud status
metadata:
  openclaw:
    requires:
      bins: ["python3"]
---

# Cloud Manager Skill

You are a cloud infrastructure assistant. When the user asks to perform any
cloud operation, use the exec tool to run the appropriate Python script.

## Routing Commands

For AWS operations, run:
exec python3 ~/.openclaw/skills/cloud-manager/scripts/aws_manager.py --action <ACTION> [--name <name>]

For GCP operations, run:
exec python3 ~/.openclaw/skills/cloud-manager/scripts/gcp_manager.py --action <ACTION> [--name <name>]

## Intent Mapping

| User says                       | --action value |
|---------------------------------|----------------|
| start, launch, boot, power on   | start          |
| stop, halt, shut down, kill     | stop           |
| restart, reboot, reset, bounce  | restart        |
| list, show, what servers        | list           |
| cpu, processor usage            | cpu            |
| memory, ram                     | memory         |
| status, health                  | status         |

## Cloud Routing

- User says "AWS" or "Amazon" → only run aws_manager.py
- User says "GCP" or "Google" → only run gcp_manager.py
- No cloud specified → run BOTH and combine results

## Response Format

Format all responses for WhatsApp (plain text + emoji):
- Use 🟢 for running, 🔴 for stopped, 🟡 for pending
- Keep it concise — WhatsApp isn't a dashboard
- Always confirm the action completed (don't just say "sent command")
Enter fullscreen mode Exit fullscreen mode

That's the whole skill definition. OpenClaw reads this file and now understands how to handle cloud messages.


Step 4 — Write the Python Script

This is where your actual API logic lives. OpenClaw's exec tool calls this script and passes the output back to the LLM.

Install the dependency:

pip3 install boto3
aws configure   # enter your AWS Access Key, Secret, region
Enter fullscreen mode Exit fullscreen mode

Create ~/.openclaw/skills/cloud-manager/scripts/aws_manager.py:

#!/usr/bin/env python3
import argparse, boto3, time

def get_client(region="us-east-1"):
    return boto3.client("ec2", region_name=region)

def state_icon(state):
    return {"running": "🟢", "stopped": "🔴", "pending": "🟡",
            "stopping": "🟡", "rebooting": "🔄"}.get(state, "")

def action_list(ec2):
    resp = ec2.describe_instances()
    instances = []
    for r in resp["Reservations"]:
        for inst in r["Instances"]:
            tags = {t["Key"]: t["Value"] for t in inst.get("Tags", [])}
            instances.append({
                "name": tags.get("Name", inst["InstanceId"]),
                "id":   inst["InstanceId"],
                "state": inst["State"]["Name"],
                "type": inst["InstanceType"],
                "ip":   inst.get("PublicIpAddress", ""),
            })
    if not instances:
        print("☁️  No EC2 instances found.")
        return
    print("☁️  AWS EC2 Instances:")
    print("" * 40)
    for i in instances:
        print(f"{state_icon(i['state'])} {i['name']}  |  {i['type']}  |  IP: {i['ip']}")
    print("" * 40)
    running = sum(1 for i in instances if i["state"] == "running")
    print(f"Total: {len(instances)} | 🟢 Running: {running} | 🔴 Stopped: {len(instances)-running}")

def action_start(ec2, name=None):
    filters = [{"Name": "tag:Name", "Values": [f"*{name}*"]}] if name else []
    resp = ec2.describe_instances(Filters=filters)
    for r in resp["Reservations"]:
        for inst in r["Instances"]:
            iid = inst["InstanceId"]
            tags = {t["Key"]: t["Value"] for t in inst.get("Tags", [])}
            iname = tags.get("Name", iid)
            print(f"🚀 Starting {iname} ({iid})...")
            ec2.start_instances(InstanceIds=[iid])
            for _ in range(18):
                time.sleep(5)
                s = ec2.describe_instances(InstanceIds=[iid])
                state = s["Reservations"][0]["Instances"][0]["State"]["Name"]
                ip    = s["Reservations"][0]["Instances"][0].get("PublicIpAddress", "")
                if state == "running":
                    print(f"{iname} is now RUNNING!")
                    print(f"   Public IP: {ip}")
                    return
            print("⏳ Still starting — check AWS Console.")

def main():
    parser = argparse.ArgumentParser()
    # This example shows "list" and "start" — see GitHub repo for stop/restart/cpu
    parser.add_argument("--action", required=True, choices=["list", "start"])
    parser.add_argument("--name", help="Instance name tag (for start)")
    args = parser.parse_args()

    ec2 = get_client()
    if args.action == "list":    action_list(ec2)
    elif args.action == "start": action_start(ec2, args.name)

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Always test your script in isolation first:

python3 ~/.openclaw/skills/cloud-manager/scripts/aws_manager.py --action list
Enter fullscreen mode Exit fullscreen mode

If this works standalone, OpenClaw will work. Half of all skill debugging is just the underlying script misbehaving.


Step 5 — Load the Skill

Restart the gateway to pick up the new skill:

openclaw gateway restart
Enter fullscreen mode Exit fullscreen mode

Verify it's registered:

openclaw skills list
Enter fullscreen mode Exit fullscreen mode

You should see cloud-manager in the output.


Step 6 — Connect WhatsApp (2 minutes, one-time)

This is where it all comes together. OpenClaw supports WhatsApp natively via the Linked Devices protocol — the exact same way WhatsApp Web works in your browser. No Meta developer account, no webhooks, no ngrok, no server.

openclaw channels add
Enter fullscreen mode Exit fullscreen mode

Follow the prompt:

  1. Select WhatsApp (QR link)
  2. Open WhatsApp on your phone → Settings → Linked Devices → Link a Device
  3. Scan the QR code shown in your terminal

Done. The link is permanent — you only do this once. Every time you restart the gateway, the WhatsApp session reconnects automatically.


Step 7 — Test End-to-End

With the gateway running, send yourself these messages on WhatsApp:

list vms
→ ☁️ AWS EC2 Instances (us-east-1):
   🔴 aws-web-vm | t3.micro | us-east-1c | STOPPED

start vm
→ 🚀 Starting aws-web-vm... ✅ RUNNING! Public IP: 54.x.x.x

check cpu
→ 📊 CPU — aws-web-vm: Last 5 min: 4.2% [░░░░░░░░░░] ✅
Enter fullscreen mode Exit fullscreen mode

The whole round trip — WhatsApp → OpenClaw → Python script → AWS API → reply — takes 8–15 seconds.


How It Works Under the Hood

When you send "start vm":

  1. WhatsApp → OpenClaw via local Linked Devices session (no public server needed)
  2. OpenClaw LLM receives your message + all SKILL.md files as context
  3. Intent match — LLM reads the cloud-manager skill and decides to call aws_manager.py with --action start
  4. exec tool runs the Python script on your machine with your local AWS credentials
  5. stdout captured — script output returned to the LLM
  6. LLM formats a clean WhatsApp reply from the script output
  7. Reply sent back to WhatsApp

Your AWS credentials never leave your machine. No middleware, no SaaS layer.


5 Tips for Building Great OpenClaw Skills

After building CloudClaw, here's what I'd tell anyone building their first skill:

1. Be specific in your SKILL.md
The LLM reads your Markdown literally. Write it like documentation for a junior engineer who needs to know exactly what to do. Vague instructions = vague behavior.

2. One script with --action flags beats six separate scripts
Easier to maintain, easier to describe in SKILL.md, easier to test.

3. Design output for the channel
WhatsApp ≠ terminal. No ANSI colors, no wide tables, no JSON blobs. Use emoji, keep lines short, always end with a human-readable status summary.

4. Test your script standalone before connecting OpenClaw
Run python3 your_script.py --action list first. Always. Half of debugging is the script, not OpenClaw.

5. Write actionable error messages
"❌ AWS credentials expired. Run: aws configure" beats a stack trace landing in someone's WhatsApp chat every single time.


Why This Pattern Works for Any API

The CloudClaw pattern — SKILL.md for intent routing, Python script for API logic, exec to wire them together — works for literally any integration:

You want to... Skill triggers Script calls
Control cloud VMs "start vm", "list servers" boto3, GCP SDK
Query a database "show users", "run report" psycopg2, pymongo
Post to social media "tweet this", "post update" tweepy, requests
Control home devices "turn on lights", "set temp" Home Assistant API
Query internal tools "show tickets", "what's open" Jira/Linear API

The pattern is always the same. The only thing that changes is the API you're wrapping.


Full Source Code

Everything is open source — including the complete aws_manager.py, gcp_manager.py (for GCP support), SKILL.md, one-command setup.sh, and a live real-time dashboard that syncs with real AWS status every 10 seconds:

🔗 GitHub: github.com/parulmalhotraiitk/clawcloud


ClawCon Michigan

Did not attend, but the ClawCon community was a huge inspiration — seeing what people were building with OpenClaw in-person was incredible to read about!


Built CloudClaw in 3 days using OpenClaw + boto3 + google-cloud-compute. The lobster runs the cloud now. 🦞☁️

Top comments (0)