Build a WhatsApp agent
with OpenClaw + TimelinesAI

Turn WhatsApp into a customer channel your OpenClaw agent operates on: auto-reply to incoming messages, send transactional and event-triggered notifications, sync with your CRM, share the inbox with teammates. TimelinesAI runs the WhatsApp gateway. Your agent handles the reasoning.

Auto-reply to customersTransactional sendsCRM syncShared inbox
What you can build

What your agent can do with WhatsApp

With a TimelinesAI skill installed, your OpenClaw agent can operate WhatsApp as a real customer channel. The long version of this list is in the sections below — every capability has the specific API calls it needs.

Answer incoming customer messages automatically — 24/7 autoresponder, after-hours responder, or full AI chatbot that escalates to a human when stuck.

Send transactional and event-triggered messages — order confirmations, shipping updates, appointment reminders, payment receipts, and notifications triggered by events in other tools (HubSpot, Stripe, Calendly). Only to customers who expect the message.

Qualify leads through multi-turn conversations — ask a sequence of questions, store answers, tag the chat as qualified or not.

Sync WhatsApp activity with your CRM — look up incoming numbers in HubSpot/Pipedrive, update deal stages, push notes back.

Summarize and score conversations — "what did ACME ask about last week", "score this chat 1–10 on intent".

Share the inbox with teammates — your agent drafts replies as private notes, humans send them; or your agent sends and humans watch.

Handle media — photos of receipts, PDFs, voice notes — processed by OpenClaw, replied to automatically.


Getting started

Setup

Four things to wire together. After this, your skill just makes API calls.

1

Connect your WhatsApp number

Sign in at app.timelines.ai, scan the QR code with the phone that holds your business number. TimelinesAI runs the gateway from here on.

2

Get an API token

Integrations → Public Api → Copy. Save as TIMELINES_AI_API_KEY. One token covers the whole workspace.

3

Install a skill from the companion repo

4 ready-made skills + webhook receiver + docs at InitechSoftware/openclaw-whatsapp-skills. Clone, symlink into your OpenClaw skills/, done.

4

Register a webhook

Point message:received:new at a public HTTPS URL. TimelinesAI will push incoming messages to it. Your receiver invokes OpenClaw.

Smoke-test the token

Before you build anything, confirm auth works:

curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
  https://app.timelines.ai/integrations/api/whatsapp_accounts

You should see a JSON list of your connected numbers, each with an id (JID), phone, status, and account_name. If not, see Tips below.

Register the webhook once

curl -sS -X POST \
  -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event_type":"message:received:new","url":"https://your-app.example.com/webhook","enabled":true}' \
  https://app.timelines.ai/integrations/api/webhooks

Minimal webhook receiver (Node / Vercel)

~30 lines. Accepts the webhook POST, hands incoming messages off to OpenClaw. Return 2xx within 5 seconds or TimelinesAI retries.

// api/webhook.js
export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).end();

  const { event_type, data } = req.body || {};
  if (event_type !== "message:received:new") {
    return res.status(200).json({ ignored: event_type });
  }

  // Ack fast, then hand off to the agent.
  res.status(200).json({ ok: true });

  await fetch(process.env.OPENCLAW_HOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      chat_id: data.chat_id,
      text: data.text,
      sender_phone: data.sender_phone,
    }),
  });
}

Channel choice

Personal numbers vs WhatsApp Business API

Before you build outbound flows, understand which WhatsApp channel your use case belongs to. Picking wrong gets numbers banned.

This guide is about personal WhatsApp numbers — the ones you connect to TimelinesAI by scanning a QR code. They are for inbound conversations and transactional sends to customers who expect the message. For cold outreach, marketing broadcasts, or promotional campaigns, use WhatsApp Business API instead — TimelinesAI supports it today through the dashboard, and public API automation for Business API workflows is coming in Q2 2026.

Safe with the skills in this guide (personal number)

  • Inbound conversations. Customer messages you first, you reply. No risk.
  • Transactional sends. Order confirmations, shipping updates, delivery notifications, payment receipts, appointment reminders — any message a customer expects because they just did something with your business.
  • Event-triggered notifications from other tools. HubSpot deal → demo confirmation, Stripe failed payment → recovery note, Calendly booking → pre-meeting reminder. The customer opted in when they used the upstream tool.
  • Replying inside WhatsApp's 24-hour customer service window. Once a customer messages you, you have 24 hours to reply freely. The autoresponder, FAQ handler, and lead qualifier skills operate inside this window.

NOT safe on personal numbers

  • Cold outreach to lists you bought or scraped — WhatsApp bans numbers for this within hours.
  • Marketing broadcasts to customers who didn't specifically opt in.
  • Promotional campaigns, sales offers, seasonal pushes, product launches.
  • Anything resembling a marketing blast. If you're asking “what throughput can I get”, you're on the wrong channel.

How to tell which channel you need

  1. 1.Did the customer message you first, or are you replying inside an active 24-hour session? → Personal number, this guide.
  2. 2.Is the customer about to receive something they explicitly expect (order, appointment, payment, delivery)? → Personal number, transactional send.
  3. 3.Triggered by a customer-opted-in event in your CRM or billing tool? → Personal number, event-triggered send.
  4. 4.Broadcast, cold outreach, or promotional campaign? → Business API, use the TimelinesAI dashboard.
  5. 5.Not sure? → Don't send. Treat it as promotional and route it to the Business API path.

Every outbound capability below assumes you've passed this test. If you're not sure, re-read this section before shipping. TimelinesAI cannot protect you from the WhatsApp-layer ban — the ban is enforced upstream, at WhatsApp's infrastructure, not at the gateway.


Before you start

Things to know

A few details that are easy to miss from the reference and will each cost you an hour if you don't know them.

!
No trailing slashes. GET /chats works. GET /chats/ returns the TimelinesAI 404 page — which looks like a network problem and isn't. Every URL in this guide is written without a trailing slash on purpose.
!
JSON bodies must be valid UTF-8. The parser rejects anything else. Em-dashes and smart quotes in shell heredocs are the usual trigger. Write payloads to a file and use curl --data-binary @file.json instead of inline -d "...".
!
Base URL is https://app.timelines.ai/integrations/api. Some older blog posts reference a different subdomain with an X-API-KEY header — that's outdated. Use Bearer auth on app.timelines.ai/integrations/api.
!
Attachment URLs in webhook payloads expire quickly. When a customer sends a photo or PDF, download it inline in the webhook handler, not from an async worker.
!
Personal numbers get banned for cold outreach. The send endpoints in this guide will let you send to anyone, but WhatsApp will ban your number quickly for unsolicited outbound. Only send outbound to people who messaged you first or who explicitly expect a transactional message. For broadcasts, use WhatsApp Business API — see Channel choice.
!
Sending is asynchronous. POST /messages returns a message_uid — that's a receipt, not a delivery confirmation. Use GET /messages/{uid}/status_history to check actual delivery.

Reference

API reference

Every endpoint you need to build every capability below. Base URL https://app.timelines.ai/integrations/api. Auth Authorization: Bearer $TIMELINES_AI_API_KEY.

Reading

MethodPathWhat it returns
GET/whatsapp_accountsYour connected WhatsApp numbers, each with JID, phone, status, account name.
GET/chatsChat list. Supports ?phone=... and ?label=... filters. Each chat has a whatsapp_account_id field holding the JID of the number that owns it.
GET/chats/{id}One chat's full detail.
GET/chats/{id}/messagesMessage history with ?limit=N. Includes from_me, sender_phone, text, timestamp, message_type (whatsapp vs note).
GET/chats/{id}/labelsLabels on the chat.
GET/messages/{uid}/status_historySent / Delivered / Read timeline for an outbound message.
GET/messages/{uid}/reactionsReactions on a message.
GET/filesFiles you uploaded via the API.
GET/webhooksYour registered webhook subscriptions.

Writing

MethodPathWhat it does
POST/messagesSend to a phone number. Body: {"phone":"+...","text":"..."}. Returns {"message_uid":"..."}.
POST/chats/{id}/messagesSend into an existing chat. Body: {"text":"..."}. Sender is whichever WhatsApp number owns the chat.
POST/chats/{id}/notesAttach a private note to a chat. Not sent to WhatsApp, visible only inside TimelinesAI. Used for agent state and draft-review workflows.
POST/chats/{id}/labelsAdd a label to a chat. Use for stage tracking, routing, stop-reply flags.
PATCH/chats/{id}Update chat metadata — assignee, read state.
PUT/messages/{uid}/reactionsSet a reaction emoji on a message.
POST/filesUpload a file by URL. TimelinesAI fetches and hosts it.
POST/files_uploadMultipart file upload.
POST/webhooksRegister a webhook subscription.
PUT/webhooks/{id}Update or enable/disable a subscription.
DELETE/webhooks/{id}Remove a subscription.

Incoming

Incoming messages — what your agent can handle

Every time a customer messages your WhatsApp number, TimelinesAI fires a message:received:new event to your webhook with the chat id, text, sender phone, and attachments. Your skill reads the event, decides what to do, and replies with POST /chats/{chat_id}/messages. Everything below is a variation on that loop.

1

Auto-reply to every incoming message

Answer questions about shipping, returns, and opening hours. For anything else, draft a reply and tag the chat for review.
Between 22:00 and 08:00 reply automatically. During business hours just flag incoming chats for me.
Handle my WhatsApp replies while I'm in this meeting.
How it works

skill receives the webhook payload, composes a reply, calls POST /chats/{id}/messages with {"text":"..."}. One API call per turn, no state.

2

FAQ handler with escalation to a human

Answer questions about shipping, returns, and opening hours. For anything else, tag the chat needs-human and stop replying until I clear the tag.
How it works

before replying, check GET /chats/{id}/labels. If the chat has needs-human, exit without sending. If the incoming text matches an FAQ topic, reply. Otherwise POST /chats/{id}/labels with the escalation tag and exit silently. Your team's inbox filters on the label.

3

Route conversations to the right person

For each incoming chat, figure out if it's sales, support, or billing and tag it. Assign sales chats to alex@ours and billing to jamie@ours.
How it works

classify intent from the text, POST /chats/{id}/labels with intent/sales or similar, then PATCH /chats/{id} with {"responsible_email":"..."} to hand the chat off in the TimelinesAI inbox.

4

Qualify leads through a question sequence

For any new chat from our Facebook ad campaign, ask about their use case, team size, and timeline. Store answers as notes on the chat. Tag it qualified if team size is 5 or more.
How it works

labels track which question you're currently on (discovery/q1, q2, q3), notes hold the answers. Each turn: read the current stage label, parse incoming text as the answer, write it via POST /chats/{id}/notes, advance the label, ask the next question. No external database — see State persistence below.

5

Understand photos, PDFs, and receipts

When a customer sends a photo of a receipt, extract the amount and vendor and add them as a note.
If someone sends a PDF, classify it as invoice / contract / ID and tag the chat accordingly.
How it works

webhook payloads include an attachment URL. Download it in the handler (it expires quickly), process with OpenClaw's vision or document tools, write extracted data back via POST /chats/{id}/notes.

6

Transcribe voice notes and reply

Transcribe inbound voice notes. Reply in text if it's short, or back as a voice note if the answer is long.
How it works

webhook delivers a URL to the voice file. Download, transcribe, compose a reply. Text replies via POST /chats/{id}/messages; voice replies via POST /chats/{id}/voice_message (multipart .ogg or .mp3).

7

Match the customer's language

If the customer writes in Spanish, reply in Spanish. Switch languages mid-conversation if they do.
How it works

pure OpenClaw-side reasoning on the incoming text. TimelinesAI just carries the reply.

8

React to messages without sending a full reply

React with 👀 to every incoming message so customers know I've seen it, then take my time composing the real reply.
How it works

PUT /messages/{message_uid}/reactions with the emoji. No message credit consumed — reactions are lightweight.


Outbound

Outbound — messages your agent sends

These are messages your agent starts, not replies. Triggered by a customer action your other tools detect (new order, failed payment, demo scheduled), or by a direct human instruction about a specific person. All of these assume the customer either messaged you first or explicitly expects a transactional message — if neither is true, it belongs on WhatsApp Business API, not here.

9

Send a transactional message by name or to a new recipient

Message John that his invoice is ready.
Text the plumber our new office address so he can deliver the parts.
Send the signed contract to the client who just wired the deposit.
How it works

for an existing chat, look it up via GET /chats?name=John (or your CRM) and call POST /chats/{id}/messages. For a new recipient, POST /messages with {"phone":"+...","text":"..."}. The whatsapp-send skill in the companion repo handles both modes with UTF-8-safe serialization.

$ cat > /tmp/send.json <<'JSON'
{"phone":"+15550200",
 "text":"Hi - your order shipped. Tracking: ABC123."}
JSON

$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/send.json \
    https://app.timelines.ai/integrations/api/messages
{"status":"ok","data":{"message_uid":"OUTBOUND-UID-PLACEHOLDER"}}
10

Scheduled follow-ups inside an active conversation

Every Monday at 9am, check chats tagged to-follow-up that had customer activity in the last 24 hours but no reply from us, and send a gentle check-in.
How it works

OpenClaw's scheduling skill fires on the cron, pulls the audience with GET /chats?label=to-follow-up, filters to chats where the last customer message was less than 24 hours ago, and sends. The 24-hour filter is critical — outside that window, a follow-up becomes a re-engagement touch and you're back to Business API territory.

11

Trigger messages from events in your other tools

When a HubSpot deal hits 'demo scheduled', send a WhatsApp confirmation with the meeting link.
When Stripe reports a failed payment, send a polite recovery message with a link to update the card.
When a Calendly booking is created, send a pre-meeting reminder the morning of.
How it works

your existing tool (HubSpot, Stripe, Calendly, Pipedrive) fires its own webhook into your agent. The agent classifies the event and calls POST /messages or POST /chats/{id}/messages. This is the highest-value pattern in the guide — WhatsApp becomes a delivery channel for any workflow you already have, and the sends are transactional by nature because they're downstream of a customer action in the upstream tool.

12

Send files and documents on request

Generate the quote PDF and send it to the customer who just asked for pricing.
Email the contract as a PDF and also drop it in the WhatsApp chat for the client.
How it works

two steps. Upload the file via POST /files (by URL) or POST /files_upload (multipart). Then reference it in POST /chats/{id}/messages. The customer asked for the document — this is a reply to their request, not cold outreach.

13

Check whether a message was actually delivered

Did John actually receive the invoice message I sent this morning?
How it works

every send returns a message_uid. Later, GET /messages/{uid}/status_history returns the Sent / Delivered / Read timeline. Delivery is usually within a second or two on an active number. The whatsapp-delivery-check skill in the companion repo wraps this.

$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    https://app.timelines.ai/integrations/api/messages/OUTBOUND-UID-PLACEHOLDER/status_history
{"status":"ok","data":[
  {"status":"Sent",     "timestamp":"2026-04-12 12:28:40 +0000"},
  {"status":"Delivered","timestamp":"2026-04-12 12:28:41 +0000"}
]}
Every capability in this section assumes: the customer opened the thread recently (within WhatsApp's 24-hour session window), OR explicitly expects this message from you. Cold outreach to purchased lists and marketing broadcasts will ban your personal number — use WhatsApp Business API for those.

CRM

CRM and analytics

TimelinesAI's read endpoints give your agent enough data to answer analytical questions and sync state with your CRM in natural language.

14

Response time reporting

What was our average first-reply time on WhatsApp this week?
Who on my team is slowest to reply?
How it works

GET /chats + GET /chats/{id}/messages + GET /messages/{uid}/status_history. Aggregate client-side.

15

Unanswered message detection

How many messages did we receive yesterday? How many are still unanswered?
Show me every chat with an incoming message in the last 24 hours and no reply.
How it works

GET /chats?read=false for unread chats, then filter messages by from_me=false.

16

Summarize conversations on demand

Summarize the entire conversation with ACME Corp. What are their pain points?
Give me a one-paragraph brief on every chat I haven't replied to yet.
How it works

fetch GET /chats/{id}/messages, let OpenClaw summarize. Paginate for long threads.

17

Enrich your CRM with WhatsApp activity

For every new chat this week, look up the number in HubSpot. If it's a contact, tag the chat with their deal stage. If not, create the contact.
How it works

combine TimelinesAI reads with your CRM's API. Write results back to the chat via POST /chats/{id}/labels and POST /chats/{id}/notes.

18

Score leads from conversation content

Score every chat tagged inbound-lead from 1 to 10 on fit and urgency. Write the score as a note.
How it works

LLM reasoning over GET /chats/{id}/messages, result written via POST /chats/{id}/notes.


Operations

Scale and handoff

Patterns for human-in-the-loop workflows, multi-agent routing, and conversation memory.

19

Draft replies for human review instead of sending

For every new incoming message, draft a reply and save it as a note. Don't send — I'll review and send them myself.
How it works

POST /chats/{id}/notes with the draft text instead of /messages. The note appears in the same chat view your teammates already use.

20

Hand off to a human when the agent is stuck

If the conversation goes more than 5 turns without resolution, or the customer asks for a human, tag escalate and stop replying until I clear the tag.
How it works

count turns with GET /chats/{id}/messages, check stop-reply labels with GET /chats/{id}/labels before every send. If escalation triggers, POST /chats/{id}/labels with escalate and exit.

21

Run multiple specialized agents on one inbox

Sales AI handles pricing questions, support AI handles product questions. Route by intent; if both are unsure, escalate to me.
How it works

two OpenClaw agents, two skills, one workspace. An intent-classification skill runs first, tags the chat, and the specialist skills check the label before replying.

22

Remember past conversations with the same customer

Last week you mentioned you were traveling — how did that go?
How it works

OpenClaw's own memory plus GET /chats/{id}/messages for the full WhatsApp history. Chat history survives across invocations because it lives on TimelinesAI.


State persistence

OpenClaw skills don't keep state in memory across invocations. WhatsApp conversations are multi-turn. The fix is to store state on the chat itself:

  • Labels hold discrete stagediscovery/q1, qualified, escalate. Add with POST /chats/{id}/labels, read with GET /chats/{id}/labels.
  • Notes hold structured data — team_size=8, draft replies, lead scores. Add with POST /chats/{id}/notes. Read by iterating GET /chats/{id}/messages and filtering message_type == "note".

The upside: crash safety, visibility to human teammates, clean handoff — a human can clear a label to rewind the flow, or add escalate to take over. The trade-off: every state transition is an HTTP call. For customer-facing flows this is fine.


Sending from the right number

If your workspace has more than one WhatsApp number connected, your skill has to make sure it sends from the intended one. Every chat has a whatsapp_account_id field holding the full JID (like PHONE@s.whatsapp.net) of the number that owns it. When you POST /chats/{id}/messages, the sender is always that JID — you don't pick it, the chat does.

The pattern:

  1. 1Hard-code the allowed sender JID in each skill’s environment (e.g. ALLOWED_SENDER_JID).
  2. 2Before sending, GET /chats/{id} and compare whatsapp_account_id to your allowed JID.
  3. 3If they don’t match, skip the send — surface to a human or drop the event.

Two extra HTTP calls per turn, zero chance of sending from the wrong persona. For single-number workspaces this doesn't apply.


Example

Example: one full round trip

A concrete five-step loop showing what the API looks like in practice — how your agent sends an outbound, confirms delivery, processes the customer's reply, and sends a follow-up. Phone numbers, chat IDs, and message UIDs below are placeholders — substitute your own.

Placeholders used throughout this example:

Your business number   → +1 555 0100 (JID: 15550100@s.whatsapp.net)
Your customer's number → +1 555 0200
API token              → $TIMELINES_AI_API_KEY
1

Confirm your token works and list your connected numbers

$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    https://app.timelines.ai/integrations/api/whatsapp_accounts

{"status":"ok","data":{"whatsapp_accounts":[
  {"id":"15550100@s.whatsapp.net","phone":"+15550100",
   "status":"active","account_name":"Your Business"}
]}}

You should see a list of your connected numbers. If the status is anything other than active, fix that in the TimelinesAI dashboard before continuing.

2

Send an outbound message

Write the payload to a file with UTF-8 encoding, then curl it. This is the pattern for every outbound.

$ cat > /tmp/send.json <<'JSON'
{"phone":"+15550200",
 "text":"Hi - your order shipped. Tracking: ABC123."}
JSON

$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/send.json \
    https://app.timelines.ai/integrations/api/messages

{"status":"ok","data":{"message_uid":"OUTBOUND-UID-PLACEHOLDER"}}

The response is a receipt, not a delivery confirmation. Hold on to the message_uid for the next step.

3

Check delivery status

$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    .../messages/OUTBOUND-UID-PLACEHOLDER/status_history

{"status":"ok","data":[
  {"status":"Sent",     "timestamp":"2026-04-12 12:28:40 +0000"},
  {"status":"Delivered","timestamp":"2026-04-12 12:28:41 +0000"}
]}

Sent → Delivered is typically within a second on an active number. The Read status appears later, when the recipient actually opens the chat.

4

The customer replies, your webhook fires

When the customer responds, TimelinesAI posts to your registered webhook URL:

{
  "event_type": "message:received:new",
  "data": {
    "chat_id": 12345678,
    "message_uid": "INBOUND-UID-PLACEHOLDER",
    "sender_phone": "+15550200",
    "sender_name": "Customer",
    "text": "Thanks! When will it arrive?",
    "timestamp": "2026-04-12 12:29:20 +0000"
  }
}

Your receiver acks with a 200 immediately, then hands the payload to your OpenClaw skill.

5

Reply back from the same chat

$ cat > /tmp/reply.json <<'JSON'
{"text":"Estimated delivery is 2-3 business days. You'll get tracking updates to this chat."}
JSON

$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/reply.json \
    https://app.timelines.ai/integrations/api/chats/12345678/messages

{"status":"ok","data":{"message_uid":"REPLY-UID-PLACEHOLDER"}}

Because you're sending into an existing chat via /chats/{id}/messages, the sender is automatically the WhatsApp number that owns the chat — you don't pick it, the chat record does. This is the whole reason you don't accidentally send from the wrong number in multi-number workspaces.

Full loop: two outbound messages, one inbound webhook, three API calls — everything you need to operate WhatsApp as a customer channel from an OpenClaw skill. Every capability earlier in this guide is a variation on this shape.

Heads up

Limits and caveats

!
One WhatsApp Web session per number — inherited from the transport. For parallelism, connect multiple numbers to the workspace.
!
Personal numbers get banned for cold outreach. See Channel choice. Unsolicited broadcasts from personal numbers are not supported — TimelinesAI cannot protect you from the WhatsApp-layer ban. For broadcasts, use WhatsApp Business API through the TimelinesAI dashboard (public API automation coming in Q2 2026).
!
WhatsApp's 24-hour customer service window. You can reply freely for 24 hours after the customer's last message. Outside that window, messages require opt-in and templates — Business API territory. Don't build re-engagement flows that routinely cross the 24-hour line.
!
Async delivery POST /messages returns a receipt, not a delivery confirmation. Use /status_history.
!
Attachment URLs expire quickly — download media inline in the webhook handler.
!
Webhook retries — 3 attempts with a 5-second timeout each. Return 2xx fast, do slow work async.
!
No public webhook signature verification — use a secret path segment in your webhook URL as minimum defense.
!
Token scope — one token covers the whole workspace: chats, messages, notes, labels, files, webhooks (read + write) and WhatsApp accounts (read only).
!
Skills can't receive HTTP directly — you need a thin receiver between TimelinesAI and OpenClaw (the Vercel example in Setup is the full template).
!
Text, media, reactions, metadata only — no voice/video calls, no broadcast-status, no WhatsApp Channels or Stories.

Building blocks

Companion skill bundle on GitHub →

4 working skills, a Vercel webhook receiver, compliance docs, and a full mirror of this guide. MIT licensed.

TimelinesAI API Docs · OpenClaw Skills Docs · v0.1.0 release

Capability guide · 2026 · Canonical URL timelines.ai/guide/openclaw-whatsapp-skills