Bir WhatsApp ajanı inşa et
OpenClaw + TimelinesAI ile

WhatsApp'ı OpenClaw ajanının çalıştırdığı bir müşteri kanalına dönüştür: gelen mesajlara otomatik yanıt ver, transactional ve olay tetikli bildirimler gönder, CRM'inle senkronize et, inbox'ı ekiple paylaş. TimelinesAI WhatsApp geçidini yürütür. Ajanın muhakemeyle ilgilenir.

Otomatik yanıtTransactional gönderimlerCRM senkronuPaylaşılan inbox

Ne inşa edebilirsin

Ajanın WhatsApp ile neler yapabilir

Bir TimelinesAI skill'i kuruldu mu, OpenClaw ajanın WhatsApp'ı gerçek bir müşteri kanalı olarak çalıştırabilir. Bu listenin uzun versiyonu aşağıdaki bölümlerde — her yetenek ihtiyaç duyduğu spesifik API çağrılarına sahip.

Gelen müşteri mesajlarını otomatik yanıtla — 7/24 autoresponder, mesai dışı yanıtlayıcı veya takılınca insana devreden tam bir AI chatbot.

Transactional ve olay tetikli mesajlar gönder — sipariş onayları, kargo güncellemeleri, randevu hatırlatmaları, ödeme makbuzları ve diğer araçlardaki olaylar (HubSpot, Stripe, Calendly) tarafından tetiklenen bildirimler. Sadece mesajı bekleyen müşterilere.

Çok turlu konuşmalarla lead nitelendir — bir dizi soru sor, cevapları sakla, sohbeti qualified ya da değil olarak etiketle.

WhatsApp aktivitesini CRM'inle senkronize et — gelen numaraları HubSpot/Pipedrive'da ara, deal aşamalarını güncelle, notları geri yaz.

Konuşmaları özetle ve puanla — “ACME geçen hafta ne sordu”, “bu sohbeti niyet için 1-10 puanla”.

Inbox'ı ekiple paylaş — ajanın yanıtları özel not olarak hazırlasın, insanlar göndersin; ya da ajan göndersin ve insanlar izlesin.

Medyayı işle — makbuz fotoğrafları, PDF'ler, sesli notlar — OpenClaw tarafından işlenir, otomatik yanıtlanır.


Buradan başla

Bu rehber nasıl kullanılır

Bu rehber dual-mode. Kendin okuyup Setup'ı manuel olarak yapabilirsin — aşağıdaki bölümlerin serdiği yol — ya da tüm rehberi OpenClaw agent'ına (veya URL okuyabilen herhangi bir agent'a) verebilir ve setup'ı senin yerine yapmasına izin verebilirsin. Her iki yol da aynı yere varıyor: kurulmuş dört WhatsApp skill'i, bağlanmış token ve webhook, müşteri mesajlarına cevap veren agent'ın.

Eğer bunu kendin okuyorsan

Aşağıdaki capability gruplarını — Incoming, Outbound, CRM & analytics, Operations — gözden geçir ve hangisinin kullanım durumun için önemli olduğuna karar ver. Sonra Setup'ı manuel olarak takip et; dört adım, yaklaşık on dakika. Her capability yaptığı tam API çağrılarını listeler ve bu rehberdeki her code block yayınlanmadan önce gerçek TimelinesAI API'ına karşı çalıştırıldı. Tıkanırsan Bilmen gerekenler, önceden bilmezsen seni bir saate mal olacak her gotcha'yı toplar.

Setup'ı agent'ın yapmasını istiyorsan

Bu prompt'u OpenClaw'a (ya da Claude Code, Cursor, Claude desktop, URL okuyabilen herhangi bir agent'a) yapıştır. Bu rehbere URL ile işaret eder ki agent live sürümü okusun, sonra seni install, token, webhook, smoke-test ve ilk skill'ini etkinleştirme boyunca gezdirir — sadece gerçekten senden bir şey gereken yerlerde input ister.

Read https://timelines.ai/guide/openclaw-whatsapp-skills end to end.

Then help me set up an OpenClaw + TimelinesAI WhatsApp agent:

1. Clone InitechSoftware/openclaw-whatsapp-skills into ~/.openclaw/workspace/
   and symlink the four skills into ~/.openclaw/skills/.

2. Ask me for my TimelinesAI API token (Integrations → Public API → Copy
   on app.timelines.ai) and write it to ~/.openclaw/workspace/.env.timelinesai
   as TIMELINES_AI_API_KEY.

3. Run the smoke-test curl from Setup. If it fails, walk me through the
   "If you see something else" failure block in the guide.

4. Help me deploy examples/vercel-webhook-receiver from the companion repo
   and register the webhook with TimelinesAI.

5. Ask which of the four skills I want to enable first — whatsapp-autoresponder,
   whatsapp-lead-qualifier, whatsapp-send, or whatsapp-delivery-check — and
   walk me through its capability section plus any relevant gotchas from
   Things-to-know.

6. Before enabling anything that sends outbound messages, show me Channel
   choice so I pick the right WhatsApp channel (personal number vs Business
   API) for my use case.

Prompt bilinçli olarak herhangi bir outbound göndermeyi açmadan önce durur — gerçekten yapmaya çalıştığın şey için doğru WhatsApp kanalını (personal numara vs Business API) seçmenin için seni önce Kanal seçimi'nden geçirir. Ban riski gerçek ve upstream'de yaşar, gateway'in seni koruyabileceği bir şey değil.


Başlarken

Kurulum

Bağlanacak dört şey. Sonrasında skill'in sadece API çağrıları yapar.

  1. 1

    WhatsApp numaranı bağla

    app.timelines.ai'a giriş yap, iş numaranı taşıyan telefonla QR kodu tara. TimelinesAI bundan sonra geçidi yürütür. Standart personal-number kurulumuyla.

  2. 2

    Bir API token'ı al

    Integrations → Public API → Copy. TIMELINES_AI_API_KEY olarak kaydet. Tek token tüm workspace'i kapsar.

  3. 3

    Companion repodan bir skill kur

    InitechSoftware/openclaw-whatsapp-skills'i ~/.openclaw/workspace/ altına klonla, sonra her skill dizinini ~/.openclaw/skills/ altına symlinkle. Dört satırlık quickstart companion README’de.

  4. 4

    Bir webhook kayıt et

    message:received:new'i bir public HTTPS URL'sine yönlendir. TimelinesAI gelen mesajları oraya pushlar. Receiver'ın OpenClaw'ı çağırır.

Token smoke testi

Herhangi bir şey inşa etmeden önce auth'un çalıştığını onayla:

curl -sS -w "\nHTTP: %{http_code}\n" \
  -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
  https://app.timelines.ai/integrations/api/whatsapp_accounts

Şunu görmelisin

{
  "status": "ok",
  "data": {
    "whatsapp_accounts": [
      {
        "id": "<phone>@s.whatsapp.net",
        "phone": "+<phone>",
        "status": "connected",
        "account_name": "<your label>"
      }
    ]
  }
}

Başka bir şey görüyorsan

  • HTTP 401 ve çıplak Unauthorized metni — token geçmemiş. Adım 2'yi tekrar kontrol et, Bearer önekini eklediğinden ve tokenin yapıştırırken satır atlamadığından emin ol. Cevap düz metin, JSON değil — jq'ya pipe edersen hata verir.
  • HTTP 404 ile bootstrap stilinde HTML “Page not found” sayfası — sonda slash ya da path'de typo var. API hatalı path'ler için HTML döner (JSON error değil), yani çıktın <!DOCTYPE html> ile başlıyorsa sondaki slash'ı kaldır ve path'i tekrar kontrol et.

Webhook'u bir kez kayıt et

# Generate a random secret once. Add it as a query param so only
# TimelinesAI's registered URL can reach your receiver.
WEBHOOK_SECRET=$(openssl rand -hex 16)
WEBHOOK_URL="https://your-app.example.com/api/webhook?secret=${WEBHOOK_SECRET}"

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

Minimal webhook receiver (Node / Vercel)

~40 satır. ?secret= query param'ını doğruluyor, giden echo'ları düşürüyor, 5 saniyelik retry penceresi içinde 2xx döndürüyor, sonra mesajı OpenClaw host'una iletir.

// api/webhook.js
export default async function handler(req, res) {
  // Path-segment auth — TimelinesAI doesn't sign webhooks yet, so the
  // ?secret=<token> you registered is the only thing stopping strangers
  // from invoking this endpoint.
  if (req.query.secret !== process.env.WEBHOOK_SECRET) {
    return res.status(404).end();
  }
  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 });
  }

  // Drop your agent's own sends echoing back as "received".
  // TimelinesAI also fires message:received:new for outbound messages
  // synced from another WhatsApp client on the same number.
  if (data.from_me === true) {
    return res.status(200).json({ ignored: "from_me" });
  }

  // Ack FAST. TimelinesAI retries 3x with a 5-second timeout per attempt —
  // if you take longer than 5s the same event hits you up to 3 times.
  res.status(200).json({ ok: true });

  // TL webhook payloads sometimes arrive flat (data.chat_id) and sometimes
  // nested (data.chat.id) depending on event flavor and version. Destructure
  // with fallback so both shapes work — see Things to know below.
  const chatId = data.chat_id ?? data.chat?.id;
  const whatsappAccountId =
    data.whatsapp_account_id ?? data.chat?.whatsapp_account_id;

  // Fire-and-forget handoff to wherever your OpenClaw host accepts
  // inbound messages. For production durability replace this with a
  // push to a queue (QStash, Inngest, SQS) your agent drains separately.
  try {
    await fetch(process.env.OPENCLAW_HOOK_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        chat_id: chatId,
        whatsapp_account_id: whatsappAccountId,
        text: data.text,
        sender_phone: data.sender_phone,
        message_uid: data.message_uid,
      }),
    });
  } catch (err) {
    console.error("[timelinesai-webhook] handoff failed:", err);
  }
}

OPENCLAW_HOOK_URL, OpenClaw dağıtımının gelen mesajlar için açtığı endpoint — agent'ı nasıl host ettiğine bağlı. Durability notları olan production-ready bir referans için companion repodaki examples/vercel-webhook-receiver'a bak.


Kurulumun

OpenClaw workspace'inin içinde ne var

Bir TimelinesAI token'ı bağladın ve companion repodan skill'leri symlink ettin. İşte gerçekte ne kurduğun ve agent'ının nasıl düşündüğünü, neyi bildiğini ve nasıl davrandığını değiştirmek istediğinde hangi dosyaları düzenleyeceğin. Burada sihir yok — iki dizinde basit markdown ve env dosyaları hepsi bu.

Skills — ~/.openclaw/skills/<skill>/

Symlink'lediğin her skill, bir zorunlu dosya (SKILL.md) ve o dosyanın referans verdiği kaynakları olan bir dizindir. FAQ handler'ının tonunu değiştirmek veya lead qualifier'ın sorularını yeniden sıralamak istiyorsan, düzenleme o skill'in SKILL.md'sinde yaşar. Derlenecek kod yok, build step yok.

DosyaNeyi kontrol ediyor
SKILL.mdSkill'in tümü. Üstünde YAML frontmatter (name, description, user-invocable ve dispatch flag'leri) artı altında instructions/prompt body. Burada skill'in ne yaptığını, OpenClaw'un onu ne zaman seçtiğini ve nasıl davrandığını düzenlersin. Companion repodaki dört WhatsApp skill'inin her biri tek bir SKILL.md — derlenecek kod yok, sadece düzenleyip yeniden başlatırsın.
README.mdSkill için insan tarafına dokümantasyon. OpenClaw bu dosyayı OKUMAZ — bu sana, takımına ve GitHub'da skill'e bakan herkes için. Opsiyonel; companion repodaki skill'ler hepsi bir tane taşır.
Scripts, resources, schemasSKILL.md içinden {baseDir}/... yoluyla referans verilen her şey — küçük helper script'ler, prompt template'leri, JSON schema'lar, test fixture'ları. SKILL.md'in onlara açıkça işaret etmediği sürece OpenClaw tarafından göz ardı edilir. Skill'in neye ihtiyacı varsa SKILL.md'nin yanına koy ve path ile referans ver.

Dört WhatsApp skill'i için — whatsapp-autoresponder, whatsapp-lead-qualifier, whatsapp-send, whatsapp-delivery-check — her SKILL.md'nin üst kısmında role ve izin verilen tool'lar var; body'de agent'ının çalıştığı gerçek prompt var. Düzenlemeden önce birini baştan sona oku; pattern'ler tekrar ediyor.

Workspace — ~/.openclaw/workspace/

Workspace dizini OpenClaw'un skill olmayan her şey için evidir: agent'ının kim olduğu, hakkında neyi bildiği, hangi tool'lara sahip olduğu, nasıl boot ettiği. OpenClaw'un kendi dokümanları “bu klasör evdir. Öyle davran” der. Bu dosyalar install ile gelir — zamanla doldurursun:

DosyaNeyi kontrol ediyor
IDENTITY.mdAgent'ın kim olduğu — isim, creature (kendisini nasıl kavramsallaştırdığı), vibe, imza emojisi, avatar. Erken doldur; diğer dosyaların çoğu buna referans verir.
SOUL.mdTemel çalışma prensipleri ve kişilik. Kimsenin bakmadığı zaman agent'ın nasıl davranması gerektiği — ne zaman samimi vs performatif, nasıl güven kazanacağı, gizlilik sınırlarının nerede olduğu. OpenClaw dokümanları buna agent'ın vicdanı der.
USER.mdAgent'ın yardım ettiği kişinin kim olduğu (sen). İsim, zamirler, timezone, ilgi alanları, proje bağlamı. Agent'lar genel bir “user” yerine belirli birine yardım etmekte daha iyidir — bu dosya onların nasıl hatırladığıdır.
TOOLS.mdHiçbir skill'in içinde istemediğin ortama özgü config — cihaz adları, host adresleri, yerel tercihler. Workspace'te yaşar ki paylaşabileceğin hiçbir şeye dokunmadan düzenleyebilesin.
AGENTS.mdAgent'lar için workspace README'si. OpenClaw'un bu workspace'in nasıl çalıştırılmasını beklediğini tanımlar. Genelde install ile gelir ve nadiren düzenlenir.
BOOT.mdOpenClaw'un başlangıçta ne yapması gerektiğine dair kısa, açık talimatlar. Varsayılan olarak boş; yeniden başlatmalar arasında deterministik boot davranışı istiyorsan doldur.
BOOTSTRAP.mdİlk boot'ta onboarding konuşması. Taze bir workspace'i kimliğini oluşturmaya yönlendirir, sonra sonuçları IDENTITY.md, SOUL.md, USER.md'ye kaydetmen için yönlendirir. Diğerlerini doldurduktan sonra bu dosyayı sil.
HEARTBEAT.mdPeriyodik task tanımları. Boş dosya = heartbeat yok; agent'ın bir şeyi aralıkla kontrol etmesini istediğinde (örn. beş dakikada bir queue poll'u) buraya task'lar ekle.

Entegrasyon başına env dosyaları

Yukarıdaki canonical dosyaların yanında, bağladığın her entegrasyon için bir env dosyası eklersin. Bunlar OpenClaw install'ın PARÇASI DEĞİL — bunlar senin, ve public bir repoya asla commit edilmemesi gereken secret'ları tutar:

DosyaNeyi kontrol ediyor
.env.<integration>Bir entegrasyon için secret'lar ve config, entegrasyon başına bir dosya. WhatsApp işi için Setup adım 2'de TIMELINES_AI_API_KEY ve ALLOWED_SENDER_JID ile .env.timelinesai oluşturdun. HubSpot, Stripe, Pipedrive veya başka bir tool eklerken bu pattern'i tekrarla — bir .env.<name> ve ihtiyacı olan skill'de source eder.
Tek satırlık kural: agent'ın DAVRANIŞINI bir skill'in SKILL.md'sini düzenleyerek değiştirirsin; agent'ın KIM olduğunu workspace'teki SOUL.md veya IDENTITY.md'yi düzenleyerek değiştirirsin; hangi SECRET'ları kullandığını doğru .env.<entegrasyon> dosyasını düzenleyerek değiştirirsin. Workspace'teki geri kalan her şey nadiren dokunacağın makineden ibaret.

Kanal seçimi

Personal numaralar vs WhatsApp Business API

Giden akışları inşa etmeden önce kullanım durumunun hangi WhatsApp kanalına ait olduğunu anla. Yanlış seçmek numaraları banlattırır.

Bu rehber personal WhatsApp numaraları hakkında — QR kodu tarayarak TimelinesAI'a bağladıkların. Gelen konuşmalar ve mesajı bekleyen müşterilere transactional gönderimler için. Cold outreach, marketing broadcast'leri veya promosyon kampanyaları için onun yerine WhatsApp Business API kullan — TimelinesAI bugün dashboard üzerinden destekliyor, Business API akışları için public API otomasyonu Q2 2026'da geliyor.

Bu rehberdeki skill'lerle güvenli (personal numara)

  • Gelen konuşmalar. Müşteri sana önce yazar, sen yanıtlarsın. Risk yok.
  • Transactional gönderimler. Sipariş onayları, kargo güncellemeleri, teslimat bildirimleri, ödeme makbuzları, randevu hatırlatmaları — müşterinin işinle az önce bir şey yaptığı için beklediği her mesaj.
  • Diğer araçlardaki olaylardan tetiklenen bildirimler. HubSpot deal → demo onayı, Stripe başarısız ödeme → recovery notu, Calendly rezervasyonu → toplantı öncesi hatırlatma. Müşteri upstream aracı kullandığında opt-in vermiş olur.
  • WhatsApp'ın 24 saatlik müşteri hizmetleri penceresinde yanıtlamak. Bir müşteri sana yazdığında, serbestçe yanıtlamak için 24 saatin var. Autoresponder, FAQ yöneticisi ve lead qualifier skill'leri bu pencere içinde çalışır.

Personal numaralarda GÜVENLİ DEĞİL

  • Satın aldığın veya scrap'ledığin listelere cold outreach — WhatsApp bunun için numaraları saatler içinde banlar.
  • Açıkça opt-in vermemiş müşterilere marketing broadcast'leri.
  • Promosyon kampanyaları, satış teklifleri, mevsimsel pushlar, ürün lansmanları.
  • Bir marketing blast'a benzeyen her şey. “Ne kadar throughput alabilirim” diye sorduysan, yanlış kanaldasın.

Hangi kanala ihtiyacın olduğunu nasıl anlarsın

  1. 1.Müşteri sana önce yazdı mı, ya da aktif bir 24 saatlik oturum içinde mi yanıtlıyorsun? → Personal numara, bu rehber.
  2. 2.Müşteri açıkça beklediği bir şey almak üzere mi (sipariş, randevu, ödeme, teslimat)? → Personal numara, transactional gönderim.
  3. 3.CRM'in veya billing aracındaki bir müşteri-opt-in olayı tarafından mı tetiklendi? → Personal numara, olay tetikli gönderim.
  4. 4.Broadcast, cold outreach veya promosyon kampanyası mı? → Business API, TimelinesAI dashboard'u kullan.
  5. 5.Emin değil misin? → Gönderme. Promosyon olarak kabul et ve Business API yoluna yönlendir.

Aşağıdaki her giden yeteneği bu testi geçtiğini varsayar. Emin değilsen, shipping yapmadan önce bu bölümü tekrar oku. TimelinesAI seni WhatsApp katmanındaki ban'dan koruyamaz — ban upstream'de, WhatsApp'ın altyapısında uygulanır, geçitte değil.


Referans

API referansı

Aşağıdaki her yeteneği inşa etmek için ihtiyaç duyacağın her endpoint. Base URL https://app.timelines.ai/integrations/api. Auth Authorization: Bearer $TIMELINES_AI_API_KEY.

Okuma

MetotYolNe dönüyor
GET/whatsapp_accountsBağlı WhatsApp numaralarınız, her biri JID, telefon, status ve account name ile.
GET/chatsSohbet listesi. ?phone=... ve ?label=... filtrelerini kabul eder. ?page=N ile sayfala (sayfa başına 50, sabit). Her sohbette sahibi numaranın JID'ini tutan whatsapp_account_id ve chatgpt_autoresponse_enabled var — kendi agent'ını açmadan önce Bilmen gerekenler'e bak.
GET/chats/{id}Bir sohbetin tam detayı.
GET/chats/{id}/messagesMesaj geçmişi, sayfa başına 50. ?page=N ile sayfala ve data.has_more_pages'e bak. Her mesaj from_me, sender_phone, text, timestamp, message_type, status (Sent/Delivered/Read) ve origin (Public API vs WhatsApp app) taşır.
GET/chats/{id}/labelsSohbetin etiketleri.
GET/messages/{uid}/status_historyGiden bir mesajın Sent / Delivered / Read zaman çizgisi.
GET/messages/{uid}/reactionsBir mesaja verilen reaksiyonlar. {data: {users: [{name, phone, reaction, current}], reactions: {<emoji>: count}, total: N}} döner — düz array değil, obje. users kimin reaksiyon verdiğini listeler (her biri seçtiği emojiyi ve kendi workspace'ini işaret eden current boolean'ını taşır); reactions emoji'ye göre indekslenmiş bir histogramdır. Boş durum: {users: [], reactions: {}, total: 0}.
GET/filesAPI üzerinden yüklediğiniz dosyalar.
GET/webhooksKayıtlı webhook abonelikleriniz.

Yazma

MetotYolNe yapıyor
POST/messagesBir telefon numarasına gönderir. Body: {"phone":"+...","text":"..."}. {"message_uid":"..."} döner.
POST/chats/{id}/messagesVar olan bir sohbete gönderir. Body: {"text":"..."}. Gönderici, sohbete sahip olan WhatsApp numarasıdır.
POST/chats/{id}/notesBir sohbete özel not ekler. WhatsApp'a gönderilmez, sadece TimelinesAI içinde görünür. Ajan durumu ve incelemeli iş akışları için kullanılır.
POST/chats/{id}/labelsSohbete bir etiket ekler. Aşama izleme, yönlendirme, stop-reply bayrakları için.
PATCH/chats/{id}Sohbet metadata'ını günceller — sorumlu (responsible_email), okunma durumu ve chatgpt_autoresponse_enabled. Agent'ın yanıt vermeye başlamadan önce sonuncuyu kapat, yoksa TL'nin yerleşik responder'ı seninkiyle yarışır.
PATCH/messages/{uid}/reactionsBir mesaja reaksiyon emojisi koyar. Body gerçek emoji karakterini taşır, shortcode değil.
POST/files_uploadBir dosyayı multipart/form-data ile yükler (alan: file). data.uid döndürür, bunu chat/messages'a file_uid olarak geçirirsin. URL ile upload varyantı yok.
POST/webhooksBir webhook aboneliği kaydeder.
PUT/webhooks/{id}Bir aboneliği günceller veya etkinleştirir/devre dışı bırakır.
DELETE/webhooks/{id}Bir aboneliği kaldırır.

Başlamadan önce

Bilmen gerekenler

Referansta gözden kaçırması kolay olan ve bilmediğin her biri sana bir saat mal olacak birkaç detay.

!
Sondaki slash yok. GET /chats çalışır. GET /chats/ TimelinesAI 404 sayfasını döner — ağ sorunu gibi görünür ama değil. Bu rehberdeki her URL bilinçli olarak sondaki slash'siz yazılmıştır.
!
JSON body'leri geçerli UTF-8 olmalı — ve tuzak heredoc'lardan öteye gider. Parser başka her şeyi reddeder. Em-dash ve smart quote'lar en yaygın tetikleyicidir, ama AYRICA UTF-8 olmayan bir locale'de çalışan herhangi bir shell (Windows'ta Git Bash, bazı Docker base image'ları, eski SSH oturumları) o karakterleri hem heredoc'larda HEM DE inline curl -d "..." argümanlarında bozar — em-dash byte 0x97'ye dönüşer, sunucu tarafında UTF-8 decode'da başarısız olur. Payload'ı açıkça UTF-8 olarak kaydedilmiş bir dosyaya yaz ve curl --data-binary @file.json kullan; tipografik noktalama içeren hiçbir şey için inline -d'ye güvenme.
!
Base URL https://app.timelines.ai/integrations/api'dir. Bazı eski blog yazıları farklı bir subdomain ve X-API-KEY header'ı referans veriyor — o eskidir. app.timelines.ai/integrations/api'da Bearer auth kullan.
!
Webhook payload'larındaki ek URL'leri hızlı süresi dolar. Bir müşteri fotoğraf veya PDF gönderdiğinde, asenkron bir worker'dan değil, webhook handler'ı içinde inline indir.
!
Personal numaralar cold outreach için banlanır. Bu rehberdeki gönderim endpoint'leri herkese göndermene izin verir, ama WhatsApp istenmeyen outbound için numaranı hızlı banlar. Outbound'u sadece sana önce yazmış veya açıkça bir transactional mesaj bekleyen kişilere gönder. Broadcast'ler için WhatsApp Business API kullan — Kanal seçimine bak.
!
Gönderim asenkrondur. POST /messages bir message_uid döner — bu bir makbuzdur, teslimat onayı değildir. Gerçek teslimatı kontrol etmek için GET /messages/{uid}/status_history kullan.
!
Yanıt şekilleri değişir — liste endpoint'leri bir tipli anahtar altında iç içe girer. Çoğu liste endpoint'i {"data":{"<tipli-anahtar>":[...]}} döner, düz {"data":[...]} değil. GET /whatsapp_accounts data.whatsapp_accounts altında iç içe girer, GET /chats data.chats altında (data.has_more_pages sayfalama flag'iyle), GET /chats/{id}/messages data.messages altında, GET /chats/{id}/labels data.labels altında. İstisnalar: GET /messages/{uid}/status_history ve GET /files data'yı düz dizi olarak döner. Tek tip şekil varsayan kod ilk uyumsuzlukta kırılır — res?.data?.<tipli> ?? res?.data ?? [] olarak oku.
!
Geçmiş okumalarında yön için from_me kullan, sender_phone değil. GET /chats/{id}/messages giden mesajları (Public API üzerinden kendi gönderdiklerin) sender_phone ekip numarasına set edilmiş olarak döner, boş string değil. sender_phone != "" ile yön tespiti her gideni gelen olarak etiketler ve konuşmayı ters çevirir. from_me boolean'ına güven — true giden (ajanın konuşması), false gelen (müşteri).
!
Geçmişte alan uid, webhook'ta message_uid. Aynı değer, farklı anahtar. TL bir message:received:new webhook'u gönderdiğinde, payload message_uid kullanır. Aynı sohbetin geçmişini GET /chats/{id}/messages ile okuduğunda, alan adı uid'dir. Aynı tanımlayıcı. Webhook'lardan UID saklayan ve geçmişte bunları arayan kod iki adı da normalize etmeli. Geçmiş mesajları ayrıca created_at değil, timestamp kullanır.
!
Etiketler REPLACE, ADD değil. POST /chats/{id}/labels {"labels":["needs-human","intent/sales"]} alır — dizi, çoğul anahtar. Çağrı sohbetin tüm etiket setini DEĞİŞTİRİR; bir etiket eklemek için önce GET ile mevcut seti alırsın, kendininkini eklersin ve birleşik listeyi POST edersin. Tümünü silmek için: POST {"labels":[]}. Tek bir etiketi kaldırmak için çalışan bir DELETE /chats/{id}/labels/{ad} endpoint'i yok — istemediğin etiket olmadan yeni seti POST etmek zorundasın.
!
Yeni kayıtlı webhook geçmiş olayları oynatabilir. POST /webhooks sonrası ilk olaylarda, TL geçmiş message:received:new olaylarının bir sağanak'ını gönderebilir — veya kayıt handshake'i hala cold-start'taki bir container'a karşı yeniden deneyebilir. Her iki durumda da receiver'ın 1. olaydan itibaren idempotent olmalı. Başlangıçtan itibaren message_uid üzerinden dedupe et, abone olunurken “temiz sayfa” varsayma.
!
Webhook payload'ları düz YA DA iç içe gelebilir. TL bazı zamanlar data.chat_id / data.whatsapp_account_id'i üst seviyede, bazı zamanlar da data.chat.id / data.chat.whatsapp_account_id olarak iç içe gönderir — değişim olay türüne ve versiyona bağlı. Savunmacı destructure: const chatId = data.chat_id ?? data.chat?.id. Eklentiler daha kötü: data.attachment_url, data.attachment?.url ve data.file_url hepsi mümkün. Sadece düz formu okuyan receiver, iç içe teslimatlarda sessizce başarısız olur.
!
Liste endpoint'leri sayfa başına sabit 50 öğe döner. ?limit=N, ?per_page=N ve ?page_size=N sessizce göz ardı edilir — API kendi boyutunu seçer. ?page=N (1'den başlayan) ile sayfala ve data.has_more_pages false olduğunda dur. /chats ve /chats/{id}/messages'ta doğrulandı; pattern diğer liste okumalarına da uygulanır. “X'ten beri tüm mesajlar” lazımsa, sayfaları gezmeyi bekle, tek büyük bir limit=1000 çağrısı değil.
!
Agent'ın devralmadan önce TimelinesAI'nin yerleşik ChatGPT autoresponder'ını devre dışı bırak. Her sohbet varsayılan olarak chatgpt_autoresponse_enabled: true ve TL'nin yerleşik responder'ı gelen mesajlara skill'inle birlikte cevap verir, müşterinin gördüğü çift cevap üretir. PATCH /chats/{id} ile {"chatgpt_autoresponse_enabled": false} sohbet başına kapatır. Public API'da toplu kapatma yok, o yüzden bunu receiver'ın devralmak üzere olduğu her sohbetteki ilk aksiyon yap (ve agent'ın zaten yönettiği her sohbette bir kez çalıştır).
!
Notlar /chats/{id}/messages'ta message_type=note ve from_me=false olarak yaşar, her zaman. POST /chats/{id}/notes gerçek WhatsApp mesajlarıyla aynı timeline'a yazar — geçmişi okuduğunda notlar kim yazmış olursa olsun from_me: false olarak aralara karışmış halde döner. Konuşma akışı üzerinde akıl yürüten her kod .message_type == "whatsapp" ile filtrelemeli: sadece from_me üzerinden gelen→giden eşleştiren bir naive response-time hesabı her notu müşteriden gelen bir mesaj olarak kabul eder ve latency'yi bozar, ve tüm geçmişi bir LLM'e besleyen bir summary veya scoring skill'i kendi önceki notlarını müşteri sözü olarak görür. Notları stream'de sadece yapılandırılmış durum istediğinde tut (qualifier pattern'i); diğer her yerde filtrele.

API çağrıları başarısız olduğunda

Beş hata modu sonunda skill'ine çarpacak. Her biri yanıtta farklı görünür ve her birinin farklı bir doğru yanıtı vardır.

!
400 — geçersiz request body. En yaygın neden JSON'undaki UTF-8 olmayan karakterlerdir (bir shell heredoc'undan gelen em-dash'ler, smart quote'lar). Payload'ı açık UTF-8 encoding ile bir dosyaya yaz ve curl --data-binary @file.json kullan. Yukarıdaki UTF-8 ipucuyla aynı kök.
!
401 — token süresi dolmuş veya iptal edilmiş. app.timelines.ai → Integrations → Public API → Regenerate'den döndür. Yeni token anında etkili olur; eski olan aynı anda çalışmayı bırakır. Redeploy etmeden döndürebilmek için environment'tan oku.
!
429 — rate limited. Yanıt saniye cinsinden bir Retry-After header'ı taşır. Ona saygı göster ve bir kez tekrar dene. Tekrar tekrar 429 alıyorsan, skill'in personal numara'nın ~30 mesaj/dakika throttle'ından hızlı gönderiyor demektir — client tarafında yavaşlat veya outbound yükünü birden fazla bağlı numara arasında böl.
!
5xx — geçit upstream sorunu. Üstel backoff ile tekrar dene: 2s, 4s, 8s. Üçüncü başarısızlıktan sonra bir insana eskalasyon yap ve göndermeyi bırak. Sonsuz döngü yapma — kalıcı 5xx genelde TimelinesAI'ın status sayfası olayıdır, retry'ların düzelteceği bir şey değil.
!
Webhook idempotency. TimelinesAI her teslimatı 5 saniye timeout ile en fazla 3 kere tekrar dener. Receiver'ın dedupe yapmıyorsa, müşteri aynı yanıtı 3 kere görür. İşlemeden önce gelen message_uid'i sohbet notu olarak sakla; sonraki webhook aynı uid ile gelirse atla. Notlar ucuzdur ve debug için insanlara görünür.

Her status koduna göre dallanan küçük bir shell pattern'ı:

$ # Capture the HTTP status into a variable, then branch
$ http_code=$(curl -sS -o /tmp/resp.json -w "%{http_code}" \
    -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)

$ case "$http_code" in
    200|201) ;;                                # ok, carry on
    400)     echo "bad body: $(cat /tmp/resp.json)"; exit 1 ;;
    401)     echo "token expired - rotate"; exit 2 ;;
    429)     sleep 30; retry_once ;;           # respect Retry-After
    5*)      sleep $((2 ** attempt)); retry_with_backoff ;;
  esac

Üretimde güvenli çalıştırma

Hata olmayan, ama daha sonra kötü bir gününden seni kurtaracak iki şey daha.

!
Sessizce düşen bir webhook'u debug etme. Skill'in gerçek bir konuşmaya yanıt vermeyi bıraktığında şu sırayla kontrol et: (1) TimelinesAI'ın webhook teslim log'u — olay URL'ine bile ulaşmıyorsa, sorun kayıt veya DNS'tir, skill'in değil; (2) hosting platformunun fonksiyon log'ları (Vercel logs vb.) işlenmeyen bir exception için; (3) olayı işlemeden önce sohbete POST /chats/{id}/notes ile bir breadcrumb notu bırak ki receiver'ın gerçekten tetiklendiğini onaylayabilesin; (4) GET /webhooks ile aboneliğin hala etkin olduğunu doğrula (token döndürme bazen abonelikleri sessizce düşürür).
!
Tek bir token tüm workspace'i kapsar. API token'ın workspace'in her sohbeti, mesajı, etiketi, notu, dosyası ve webhook'unu okur ve yazar. Onu bir parola gibi kullan: .env'de sakla, asla commit etme, ve bir ekip arkadaşı ayrıldığında veya bir deploy environment'ı değiştiğinde Integrations → Public API → Regenerate'den döndür. Yeni token anında etkili olur; eski olan aynı anda çalışmayı bırakır. Bugün skill başına veya sadece okuma scope'u yok.

Bağlam ve bellek

Konuşma bağlamı

Webhook skill'ine tek bir mesaj verir — yeni gelen mesajı. Önceki turlar yok, sohbet geçmişi yok, akışta bir transkript yok. Durumsuz bir SSS veya mesai dışı yanıtlayıcı için bu yeterli. Müşterinin üç tur önce ne dediğini hatırlaması gereken bir konuşma ajanı içinse bağlamı skill'in kendisi çekmek zorunda. Üç desen tüm yelpazeyi kapsar — mümkün olan en ucuz döngüden gerçekten hatırlayanına kadar.

1

Durumsuz

Sadece son mesaja yanıt ver. Tur başına bir POST, GET yok, geçmiş yok. Bu en basit döngüdür ve hazır skill'lerdeki varsayılandır. SSS botları, mesai dışı yanıtlayıcılar ve sınıflandırma veya yönlendirme için kullan — yanıtın yalnızca mevcut mesaja bağlı olduğu her yerde. Ajan turlar arasında her şeyi unutur: müşteri daha önce söylenen bir şeye atıf yapan bir takip yazarsa anlamaz.

2

Tam bağlam penceresi

Her yanıttan önce son 20 mesajı çek ve modele önceki konuşma olarak ver. Tur başına bir yerine iki API çağrısı, artı her çağrıda ek prompt token'ları. Konuşma ajanları, çok turlu nitelendirme ve ajanın konunun ip ucunu hatırlaması gereken her yer için kullan. Yirmi son tur genellikle yeter — daha genişe gitmek nadiren yardımcı olur ve prompt'u pahalılaştırır; daha dara gitmek ajanın müşterinin bir dakika önce söylediğini unutmasına yol açar.

3

Uyumlu bağlam

Sadece gelen mesaj bir takip gibi görünüyorsa bağlamı çek. Metin üzerinde ucuz bir sezgi — zamirle başlıyor, “bu” veya “o”'ya atıf yapıyor, önceki yanıtından sonra 30 saniye içinde geliyor, tek kelimelik onaylar — geçmişi mi çekeceğine yoksa durumsuz mu yanıt vereceğine karar verir. Turların çoğu ucuz kalır; takipler bellek kazanır. Tam pencereyle başla; maliyet bütçeni bilip sezgi ayarlamak için gerçek prod trafiğin olunca uyumluya geç.

Pencereyi çekmek tek bir GET. Yanıt sohbetin düz bir mesaj dizisidir, en yenisi sonda, müşterinin gelen turları, kendi giden yanıtların ve skill'inin yazdığı her not karışık gelir:

$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    "https://app.timelines.ai/integrations/api/chats/12345678/messages?limit=20"
{"status":"ok","data":[
  {"message_uid":"INBOUND-UID-PLACEHOLDER",
   "text":"hi, is the blue one still available?",
   "sender_phone":"+15550200",
   "message_type":"text","origin":"WhatsApp",
   "created_at":"2026-04-14T10:22:03Z"},
  {"message_uid":"OUTBOUND-UID-PLACEHOLDER",
   "text":"Yes, one left. Want me to hold it for you?",
   "sender_phone":"",
   "message_type":"text","origin":"Public API",
   "created_at":"2026-04-14T10:22:41Z"},
  {"message_uid":"INBOUND-UID-PLACEHOLDER-2",
   "text":"yes please, until tomorrow",
   "sender_phone":"+15550200",
   "message_type":"text","origin":"WhatsApp",
   "created_at":"2026-04-14T10:23:08Z"}
]}

Prod'da ısıran tuzaklar

!
Notları ve kendi giden mesajlarını geçmişten filtrele. GET /chats/{id}/messages sohbetteki her şeyi geri verir: müşterinin gelen turları, kendi giden yanıtların ve skill'inin durum veya debug için yazdığı her not. Notlar message_type == "note" ve origin == "Public API" taşır — geçmişi modele vermeden önce filtrele, yoksa ajanın kendi iç kayıtlarını gerçek konuşma gibi görür ve kendi notlarına yanıt vermeye başlar.
!
Konuşmanın ortasında başlamak. Skill'in bir sohbeti ilk kez gördüğünde, o sohbette webhook'a abone olmadan önceki uzun bir geçmiş olabilir. Daha önce hiç görülmemiş bir sohbette her zaman yakın bağlamı çek — aksi halde müşteri aslında ekibinle var olan bir thread'de otuz tur derinken yeni bir “merhaba”'ya taze bir açılış gibi yanıt vereceksin.
!
Webhook patlamalarını tekilleriştir. Müşteri dört saniyede üç mesaj yazdığında, üç webhook alırsın — muhtemelen paralel. Per-sohbet kilit veya kısa debounce olmadan müşteri üst üste üç yanıt alır. Desen: ilk webhook'u 1–2 saniye tut, sonra geçmişi bir kez çek ve birleşik duruma yanıt ver. Webhook retry'ları (her biri 5 saniye timeout ile 3 denemeye kadar) aynı yoldan gelir — işlemeden önce message_uid üzerinden tekilleştir.
!
created_at'e göre sırala, message_uid'e göre değil. message_uid'ler workspace kapsamlıdır ve global olarak sıralanamaz. Geçmişi prompt'a monte ederken turları payload'daki created_at timestamp'ine göre sırala, UID'ye göre değil. Workspace'ler arası UID'ler de paylaşılmaz — iki farklı TimelinesAI workspace'ine teslim edilen aynı fiziksel WhatsApp mesajı her birinde farklı bir UID'ye sahiptir.

Durum kalıcılığı

OpenClaw skill'leri invocation'lar arası hafızada durum tutmaz. WhatsApp konuşmaları çok turludur. Çözüm, durumu sohbete kaydetmektir:

  • Etiketler ayrık aşamayı tutar — discovery/q1, qualified, escalate. POST /chats/{id}/labels ile ekle, GET /chats/{id}/labels ile oku.
  • Notlar yapılandırılmış veriyi tutar — team_size=8, yanıt taslakları, lead skorları. POST /chats/{id}/notes ile ekle. GET /chats/{id}/messages üzerinde iterate ederek ve message_type == "note" ile filtreleyerek oku.

Avantaj: crash güvenliği, insan ekip arkadaşlarına görünürlük, temiz handoff — bir insan akışı geri sarmak için bir etiketi temizleyebilir veya devralmak için escalate ekleyebilir. Trade-off: her durum geçişi bir HTTP çağrısıdır. Müşteri yüzüne dönük akışlar için bu sorun değil.


Doğru numaradan gönder

Workspace'ine birden fazla WhatsApp numarası bağlıysa, skill'inin amaca uygun olandan gönderdiğinden emin olması gerekir. Her sohbette sahibi olan numaranın tam JID'ini (TELEFON@s.whatsapp.net gibi) tutan whatsapp_account_id alanı vardır. POST /chats/{id}/messages yaptığında, gönderici her zaman o JID'dir — sen seçmezsin, sohbet seçer.

Pattern:

  1. 1Her skill'in environment'ında izin verilen gönderici JID'i hardcode et (örn. ALLOWED_SENDER_JID).
  2. 2Göndermeden önce GET /chats/{id} ile whatsapp_account_id'i izin verilen JID'inle karşılaştır.
  3. 3Eşleşmiyorlarsa, gönderimi atla — bir insana eskalasyon yap veya olayı bırak.

Tur başına iki ek HTTP çağrısı, yanlış persona'dan gönderme şansı sıfır. Tek numaralı workspace'ler için geçerli değil.


Gelen

Gelen mesajlar — ajanın neyi yönetebilir

Bir müşteri WhatsApp numarana her mesaj attığında, TimelinesAI webhook'una sohbet id'si, metin, gönderici telefonu ve eklerle bir message:received:new olayı tetikler. Skill'in olayı okur, ne yapacağına karar verir ve POST /chats/{chat_id}/messages ile yanıtlar. Aşağıdaki her şey bu döngünün bir varyasyonudur.

1

Her gelen mesaja otomatik yanıt ver

Kargo, iade ve açılış saatleriyle ilgili sorulara yanıt ver. Başka her şey için bir yanıt taslakla ve sohbeti incelenmek üzere etiketle.
22:00 ile 08:00 arası otomatik yanıtla. Mesai içinde sadece gelen sohbetleri bana işaretle.
Bu toplantıdayken WhatsApp yanıtlarımı sen hallet.
How it works

skill webhook payload'ını alır, bir yanıt oluşturur ve POST /chats/{id}/messages çağırır {"text":"..."}. Varsayılan olarak durumsuz — tur başına bir API çağrısı. Ajanın önceki mesajları hatırlaması gereken çok turlu konuşmalar için aşağıdaki Konuşma bağlamına bak.

2

İnsana eskalasyonlu FAQ yöneticisi

Kargo, iade ve açılış saatleriyle ilgili sorulara yanıt ver. Başka her şey için sohbete needs-human etiketi koy ve ben kaldırana kadar yanıt verme.
How it works

yanıtlamadan önce GET /chats/{id}/labels'ı kontrol et. Sohbette needs-human varsa gönderme yapmadan çık. Gelen metin bir FAQ konusuyla eşleşiyorsa yanıtla. Aksi halde POST /chats/{id}/labels ile eskalasyon etiketini koy ve sessizce çık. Ekibinin inbox'ı etikete göre filtreler.

3

Konuşmaları doğru kişiye yönlendir

Her gelen sohbet için satış mı, destek mi yoksa faturalama mı olduğunu çıkar ve etiketle. Satış sohbetlerini alex@ours'a, faturalamayı jamie@ours'a ata.
How it works

metinden niyeti sınıflandır, POST /chats/{id}/labels ile intent/sales gibi bir etiket koy, sonra PATCH /chats/{id} ile {"responsible_email":"..."} göndererek sohbeti TimelinesAI inbox'ında devret.

4

Lead'i bir soru dizisiyle nitelendir

Facebook kampanyamızdan gelen her yeni sohbet için kullanım durumunu, ekip büyüklüğünü ve zaman çizelgesini sor. Yanıtları sohbete not olarak kaydet. Ekip 5 ya da daha büyükse qualified diye etiketle.
How it works

etiketler şu an hangi soruda olduğunu izler (discovery/q1, q2, q3); notlar yanıtları tutar. Her tur: şu anki aşama etiketini oku, gelen metni yanıt olarak parse et, POST /chats/{id}/notes ile yaz, etiketi ilerlet, sonraki soruyu sor. Dış veritabanı yok — aşağıdaki Durum kalıcılığına bak.

$ # 1. Read current stage from the chat's labels. Labels nest under
$ # data.labels as a string array — see Response shapes in Things to know.
$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    https://app.timelines.ai/integrations/api/chats/12345678/labels
{"status":"ok","data":{"labels":["discovery/q1"]}}

$ # 2. Save the customer's answer as a structured note. Notes are stored
$ # as messages with message_type=note and aren't pushed to WhatsApp.
$ cat > /tmp/note.json <<'JSON'
{"text":"team_size=12"}
JSON
$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/note.json \
    https://app.timelines.ai/integrations/api/chats/12345678/notes
{"status":"ok","data":{"message_uid":"NOTE-UID-PLACEHOLDER"}}

$ # 3. Advance the stage label. POST /labels REPLACES the full set, so
$ # read existing labels, swap q1 for q2, POST the combined list. The
$ # body key is "labels" (plural, array) — not "label" singular.
$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    https://app.timelines.ai/integrations/api/chats/12345678/labels \
  | jq -c '.data.labels | map(if . == "discovery/q1" then "discovery/q2" else . end) | {labels: .}' \
  > /tmp/labels.json
$ cat /tmp/labels.json
{"labels":["discovery/q2"]}
$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/labels.json \
    https://app.timelines.ai/integrations/api/chats/12345678/labels
{"status":"ok","data":{"labels":["discovery/q2"]}}
5

Fotoğrafları, PDF'leri ve makbuzları anla

Bir müşteri makbuz fotoğrafı gönderdiğinde, tutarı ve satıcıyı çıkar ve not olarak ekle.
Biri PDF gönderirse, fatura / sözleşme / kimlik olarak sınıflandır ve sohbeti ona göre etiketle.
How it works

webhook payload'ları bir ek URL'si içerir. Handler içinde indir (hızlı süresi dolar), OpenClaw'ın vision veya document araçlarıyla işle, çıkarılan veriyi POST /chats/{id}/notes ile geri yaz.

$ # The attachment URL in the webhook payload expires in ~15 minutes.
$ # Download it inline in the receiver, before queuing async work.
$ ATTACH_URL="https://files.timelines.ai/abc123/receipt.jpg?expires=..."
$ curl -sS "$ATTACH_URL" -o /tmp/receipt.jpg

$ # Process /tmp/receipt.jpg with OpenClaw vision tools, then write
$ # the extracted fields back as a structured note on the chat:
$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary '{"text":"receipt: amount=$42.50 vendor=Cafe Madrid"}' \
    https://app.timelines.ai/integrations/api/chats/12345678/notes
6

Sesli notları transcribe et ve yanıtla

Gelen sesli notları transcribe et. Metin olarak yanıtla — gerçekten sesli yanıt lazımsa, TimelinesAI dashboard'undan gönder.
How it works

webhook ses dosyasına bir URL teslim eder. İndir, transcribe et, POST /chats/{id}/messages ile bir metin yanıtı oluştur. Sesli yanıtlar bugün TimelinesAI dashboard özelliğidir ve güncel public API referansında yer almaz — eğer skill'inin programatik olarak sesli yanıt göndermesi gerekiyorsa, legacy voice_message endpoint'inin workspace'inde hala mevcut olup olmadığını doğrulamak için TimelinesAI destek ekibiyle iletişime geç.

7

Müşterinin diliyle eşleş

Müşteri İspanyolca yazıyorsa İspanyolca yanıtla. Konuşma ortasında dil değiştirirse onunla birlikte değiş.
How it works

gelen metin üzerine tamamen OpenClaw tarafında muhakeme. TimelinesAI sadece yanıtı taşır.

8

Tam yanıt göndermeden mesajlara reaksiyon ver

Her gelen mesaja 👀 ile reaksiyon ver ki müşteriler gördüğümü bilsin, sonra gerçek yanıtı sakın yettişmeden oluştururum.
How it works

PATCH /messages/{uid}/reactions ile {"reaction":"👀"}. reaction alanı gerçek emoji karakterini taşımalı — "eyes" veya ":eyes:" gibi kısa kodlar HTTP 400 "Reaction has invalid format" ile reddedilir. Mesaj kredisi tüketmez — reaksiyonlar hafiftir.

$ # Send the raw emoji character in the body. Save to a UTF-8 file and
$ # use --data-binary so shells can't downgrade it.
$ printf '{"reaction":"👀"}' > /tmp/react.json

$ curl -sS -X PATCH \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/react.json \
    https://app.timelines.ai/integrations/api/messages/INBOUND-UID-PLACEHOLDER/reactions
{"status":"ok"}

Giden

Giden — ajanın başlattığı mesajlar

Ajanın başlattığı mesajlar, yanıt değil. Diğer araçlarındaki bir olay (yeni bir sipariş, başarısız bir ödeme, planlanmış bir toplantı) veya belirli bir kişi hakkında doğrudan bir insan talimatı tarafından tetiklenir.

Bu bölümdeki her yetenekten önce: müşteri ya thread'i yakın zamanda açtı (WhatsApp'ın 24 saatlik oturum penceresi içinde) ya da bu mesajı açıkça bekliyor. Hiçbiri doğru değilse, personal numaradan gönderme — o Business API bölgesidir.

9

İsme göre veya yeni bir alıcıya transactional mesaj gönder

John'a faturasının hazır olduğunu söyle.
Tesisatçıya yeni ofis adresimizi mesaj olarak gönder ki parçaları teslim edebilsin.
İmzalı sözleşmeyi az önce kapora yatıran müşteriye gönder.
How it works

var olan bir sohbet için GET /chats?name=John ile (veya CRM'inden) ara ve POST /chats/{id}/messages çağır. Yeni bir alıcı için POST /messages ile {"phone":"+...","text":"..."}. Companion repodaki whatsapp-send skill'i her iki modu da UTF-8 güvenli serializasyon ile yönetir.

$ 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

Aktif bir konuşmada zamanlanmış follow-up'lar

Her pazartesi 9'da, son 24 saatte müşteri aktivitesi olan ama bizden yanıt gelmeyen to-follow-up etiketli sohbetleri kontrol et ve nazik bir hatırlatma gönder.
How it works

bir OpenClaw cron job'u veya standing order ile zamanla (OpenClaw'ın automation dokümanlarına bak). Job GET /chats?label=to-follow-up&read=false ile kitleyi çeker, her sohbetin last_message_timestamp'ini okur ve sadece son müşteri turu 24 saatten az önceyse gönderir. Bu pencerenin dışında follow-up bir re-engagement haline gelir ve Business API bölgesine düşersün — Channel choice'a bak.

11

Diğer araçlarındaki olaylardan mesaj tetikle

Bir HubSpot deal'ı 'demo scheduled' aşamasına gelirse, toplantı linkiyle birlikte WhatsApp onayı gönder.
Stripe başarısız ödeme bildirirse, kartı güncellemek için bir linkle nazik bir recovery mesajı gönder.
Bir Calendly rezervasyonu oluşturulduğunda, aynı sabah toplantı öncesi hatırlatma gönder.
How it works

var olan aracın (HubSpot, Stripe, Calendly, Pipedrive) kendi webhook'unu Setup'ta kurduğun aynı receiver'a atar — event source veya path'e göre yönlendirilen bir handler ekle, TimelinesAI handler'ından branch al, ve POST /messages (yeni alıcı) veya POST /chats/{id}/messages (var olan bir sohbete) çağır. WhatsApp zaten sahip olduğun her iş akışı için bir teslimat kanalı olur. Gönderiler upstream araçtaki bir müşteri eyleminin arkasından geldiği için doğası gereği transactional'dır — personal number kurallarının tam içinde.

12

İstek üzerine dosya ve döküman gönder

Teklif PDF'ini oluştur ve az önce fiyat soran müşteriye gönder.
Sözleşmeyi PDF olarak emaille ve müşterinin WhatsApp sohbetine de bırak.
How it works

iki adım. Dosyanın baytlarını POST /files_upload ile multipart/form-data olarak yükle (URL ile upload endpoint'i yok — ajanın dosyayı önce kendi indirmeli sonra POST etmeli). Yanıt bir uid döndürür, bunu POST /chats/{id}/messages içindeki file_uid alanına geçirirsin. Müşteri dökümanı istedi — bu onun talebine yanıttır, cold outreach değil.

$ # Step 1 - upload the file bytes as multipart/form-data.
$ # The form field name is "file". The response gives you a uid.
$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -F "file=@/path/to/quote.pdf" \
    https://app.timelines.ai/integrations/api/files_upload
{"status":"ok","data":{
  "uid":"FILE-UID-PLACEHOLDER",
  "filename":"quote.pdf",
  "size":128453,
  "mimetype":"application/pdf",
  "uploaded_by_email":"you@yourcompany.com",
  "uploaded_at":"2026-04-14 10:22:03 +0000",
  "temporary_download_url":"https://tl-prod-data.s3.amazonaws.com/..."
}}

$ # Step 2 - attach the uploaded file to a chat message using file_uid.
$ cat > /tmp/send.json <<'JSON'
{"text":"Quote attached for your review.",
 "file_uid":"FILE-UID-PLACEHOLDER"}
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/chats/12345678/messages
{"status":"ok","data":{"message_uid":"OUTBOUND-UID-PLACEHOLDER"}}
13

Bir mesajın gerçekten teslim edilip edilmediğini kontrol et

John bu sabah gönderdiğim fatura mesajını gerçekten aldı mı?
How it works

her gönderim bir message_uid döner. Sonra GET /messages/{uid}/status_history Sent / Delivered / Read zaman çizgisini döner. Aktif bir numarada teslimat genelde bir iki saniye içinde olur. Companion repodaki whatsapp-delivery-check skill'i bunu sarmalıyor.

$ 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"}
]}

CRM

CRM ve analiz

Okuma endpoint'leri ajanına analitik sorulara yanıt verecek ve doğal dilde CRM'inle durumu senkronize edecek kadar veri verir.

14

Yanıt süresi raporlaması

Bu hafta WhatsApp'ta ortalama ilk yanıt süremiz neydi?
Ekibimde yanıtlamada en yavaş kim?
How it works

yakın sohbetleri çek, sonra her sohbet için mesaj zaman çizgisini çek, her gelenden sonraki ilk gideni bul ve delta'ları client tarafında toplullaştır. İki endpoint, özel analitik çağrısı yok.

$ # Pull the last page of a chat's messages (50 per page, fixed).
$ # Messages nest under data.messages — not a flat .data[] array.
$ # FILTER message_type=="whatsapp" to exclude notes (see Things to know
$ # below — notes live in the same timeline with from_me=false and would
$ # otherwise corrupt the response-time calculation).
$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    "https://app.timelines.ai/integrations/api/chats/12345678/messages?page=1" \
  | jq -r '.data.messages[] | select(.message_type=="whatsapp") | "\(.timestamp)\tfrom_me=\(.from_me)\t\(.text[0:40])"'

# 2026-04-12 12:28:40 +0300  from_me=false  Hi - is the order shipped?
# 2026-04-12 12:30:15 +0300  from_me=true   Yes - tracking ABC123, ETA 2-3
# 2026-04-12 14:02:11 +0300  from_me=false  Got it, thanks!
# ...

$ # Compute first-reply latency from each consecutive (false -> true)
$ # pair, then average across all chats. Pure client-side aggregation.
$ # For full history walk ?page=2, ?page=3, ... until data.has_more_pages is false.
15

Yanıtlanmamış mesaj tespiti

Dün kaç mesaj aldık? Kaçı hala yanıtsız?
Son 24 saatte gelen mesajı olan ve yanıtsız her sohbeti göster.
How it works

Okunmayan sohbetler için GET /chats?read=false, sonra .data.messages[] içinde message_type=="whatsapp" VE from_me==false olanları filtrele — notlar da from_me=false taşır ve yanıtsız sayısını şişirirdi. Bilmen gerekenler'e bak.

16

Talep üzerine konuşmaları özetle

ACME Corp ile tüm konuşmayı özetle. Acı noktaları neler?
Yanıtlamadığım her sohbet hakkında bir paragraflık brief ver.
How it works

GET /chats/{id}/messages çek, .data.messages[] içinde message_type=="whatsapp" olanları filtrele (yoksa notlar, agent'ının önceki kendi karıştırmalarını sanki müşteri sözüymüş gibi özete taşır — Bilmen gerekenler'e bak), sonra OpenClaw'a özetlettir. Uzun thread'ler için ?page=N ile sayfala.

17

CRM'ini WhatsApp aktivitesiyle zenginleştir

Bu haftaki her yeni sohbet için numarayı HubSpot'ta ara. Bir contact'sa, sohbeti deal aşamasıyla etiketle. Değilse, contact'ı oluştur.
How it works

TimelinesAI okumalarını CRM'inin API'siyle birleştir. Sonuçları sohbete POST /chats/{id}/labels ve POST /chats/{id}/notes ile geri yaz.

$ # 1. Pull recent chats with their phone numbers. /chats returns 50 per
$ # page under data.chats — paginate with ?page=N if you need more.
$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    "https://app.timelines.ai/integrations/api/chats?page=1" \
  | jq -r '.data.chats[] | "\(.id)\t\(.phone)"'

# 12345678   +15550100
# 12345679   +15550200
# ...

$ # 2. For each phone, look up your CRM (HubSpot/Pipedrive/Close).
$ #    Pseudo-code:
$ #      contact = hubspot.search_by_phone(phone)
$ #      stage   = contact.deal_stage if contact else "unknown"

$ # 3. Add the deal stage label WITHOUT wiping existing labels.
$ # POST /labels is REPLACE semantics (full set), so GET current labels,
$ # append the new one, and POST the combined list.
$ curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    https://app.timelines.ai/integrations/api/chats/12345678/labels \
  | jq -c '.data.labels + ["hubspot/qualified-lead"] | unique | {labels: .}' \
  > /tmp/labels.json
$ curl -sS -X POST \
    -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @/tmp/labels.json \
    https://app.timelines.ai/integrations/api/chats/12345678/labels
18

Konuşma içeriğinden lead'leri puanla

inbound-lead etiketli her sohbeti uyum ve aciliyet için 1-10 arası puanla. Puanı not olarak yaz.
How it works

GET /chats/{id}/messages üzerine LLM muhakemesi — önce message_type=="whatsapp" ile filtrele, yoksa kendi önceki lead-score notların müşteri sözü olarak tekrar okunur ve skor her pass'te kayar. Sonucu POST /chats/{id}/notes ile öngörülebilir bir prefix'le yaz (örn. "lead_score: fit=8 urgency=6") ki bir sonraki pass'in onu bulup üzerine yazabilsin.


Operasyon

Ölçekleme ve handoff

Human-in-the-loop iş akışları, multi-agent yönlendirme ve konuşma hafızası için pattern'lar. Aşağıdaki dört yeteneğin tamamı, TimelinesAI paylaşılan inbox'ından aynı sohbetler üzerinde çalışan insanlarla bir arada var olacak şekilde tasarlanmıştır.

19

Göndermek yerine insan incelemesi için yanıt taslakla

Her gelen yeni mesaj için bir yanıt taslakla ve not olarak kaydet. Gönderme — ben inceler ve gönderirim.
How it works

POST /chats/{id}/notes'a /messages yerine taslak metnini yaz. Not, ekibinin zaten kullandığı sohbet görünümünde belirir.

20

Ajan takılınca bir insana devret

Konuşma 5 turdan fazla çözüme ulaşmadan giderse, ya da müşteri insan isterse, escalate diye etiketle ve ben temizleyene kadar yanıt verme.
How it works

GET /chats/{id}/messages ile turları say, her gönderim öncesi GET /chats/{id}/labels ile stop-reply etiketlerini kontrol et. Eskalasyon tetiklenirse POST /chats/{id}/labels ile escalate koy ve çık.

$ # 1. Count outbound 'whatsapp' messages (skip notes) in this chat.
$ # Messages nest under data.messages — not a flat .data[] array.
$ TURNS=$(curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
    "https://app.timelines.ai/integrations/api/chats/12345678/messages?page=1" \
  | jq '[.data.messages[] | select(.from_me==true and .message_type=="whatsapp")] | length')
$ echo $TURNS
8

$ # 2. Past the threshold? Append 'escalate' to the chat's labels and stop.
$ # POST /labels REPLACES the full set, so read existing first, combine,
$ # then POST — otherwise you wipe every other label on the chat.
$ if [ "$TURNS" -ge 5 ]; then
    curl -sS -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
      https://app.timelines.ai/integrations/api/chats/12345678/labels \
    | jq -c '.data.labels + ["escalate"] | unique | {labels: .}' \
    > /tmp/labels.json
    curl -sS -X POST \
      -H "Authorization: Bearer $TIMELINES_AI_API_KEY" \
      -H "Content-Type: application/json" \
      --data-binary @/tmp/labels.json \
      https://app.timelines.ai/integrations/api/chats/12345678/labels
  fi

$ # 3. Before every future send, GET /chats/{id}/labels and bail
$ #    if .data.labels contains 'escalate' or 'needs-human'.
21

Tek bir inbox'ta birden fazla özelleşmiş ajan çalıştır

Satış AI fiyat sorularını yönetsin, destek AI ürün sorularını yönetsin. Niyete göre yönlendir; ikisi de emin değilse bana eskalasyon yap.
How it works

bir OpenClaw workspace'inde iki uzman skill (biri satış, biri destek) artı üçüncü bir niyet sınıflandırma skill'i — bu üçüncü gelen her mesajda ilk çalışır, seçilen uzmanla sohbeti etiketler ve çıkar. Her uzman yanıtlamadan önce niyet etiketini okur ve diğerine işaret ediyorsa çıkar — böylece mesaj başına tam olarak bir skill ateşlenir.

22

Aynı müşteriyle geçmiş konuşmaları hatırla

Geçen hafta seyahatte olduğunu söylemiştin — nasıl geçti?
How it works

OpenClaw'ın kendi hafızası artı tüm WhatsApp geçmişi için GET /chats/{id}/messages. Geçmiş, TimelinesAI'da yaşadığı için invocation'lar arası yaşar.


Test

Agent'ını ikinci bir WhatsApp numarasıyla uçtan uca test et

Setup'tan sonra burada listelenen capability'leri görebilirsin, ama gerçek bir müşteri agent'ına yazana kadar onu gerçekten çalışırken göremezsin. Bu, iterasyon için kötü bir loop. İşte daha iyisi: aynı TimelinesAI workspace'ine ikinci bir WhatsApp numarası bağla, onu scripted bir müşteri olarak kullan ve gerçek skill'inin konuşmayı uçtan uca yönettiğini izle. Mock yok, sahte webhook yok — gerçek agent'ın, gerçek API'ın, sadece diğer tarafta insan rolünü oynayan ikinci bir numarayla.

Ön koşul — bağlı iki WhatsApp numarası. Bu pattern TimelinesAI workspace'inde iki numara gerektirir: biri müşteri personası olarak, biri gerçek agent stack'ini çalıştırıyor. Eğer şu an sadece bir bağlı numaran varsa, yerine capability #19'a (İnsan review'ı için Draft yanıtlar) atla — daha yavaş ama hala yararlı bir iterasyon loop'u verir: agent'ın yanıtları not olarak draft eder ve sen dashboard'da onaylarsın.

Pattern

TimelinesAI birden fazla WhatsApp numarasının tek bir workspace'te yaşamasına izin verir ve onların gelen webhook'larını aynı receiver'dan yönlendirir. İşin püf noktası her webhook payload'ındaki whatsapp_account_id alanı — mesajı alan numaranın JID'ini taşır. Onu oku, üzerinde switch yap ve ya persona adımına (agent müşteriye yeni cevap verdiğinde) ya da agent adımına (müşteri agent'a yeni mesaj yazdığında) yönlendir. Her iki taraf da diğer tarafın perspektifinden normal POST /messages veya POST /chats/{id}/messages ile gönderir.

Mimari

  [+1 555 0100]                   [+1 555 0200]
     persona                    agent under test
        |                              |
        |    one TimelinesAI workspace |
        |                              |
        +------->  Public API  <-------+
                        |
                 message:received:new
                        |
                        v
           +--------------------------+
           |  Your test receiver      |
           |  switch (accountJid) {   |
           |    persona_jid -> next   |
           |    agent_jid   -> skill  |
           |  }                       |
           +--------------------------+

Persona sender

Persona tarafı scripted bir müşteri: sıradaki söylenecek cümlelerin listesi. Sıradaki tur zamanı geldiğinde, alıcı olarak agent'ın telefon numarasıyla /messages'a POST et. Her iki numara tek workspace'te olduğu için TimelinesAI routing'i halleder.

// persona-sender.js — send the next scripted customer turn from the
// persona WhatsApp number to the agent-under-test number. TimelinesAI
// routes the send because both numbers share one workspace.

const PERSONA_SCRIPT = [
  "hi I saw your ad",
  "we want to reduce customer churn",
  "we are 25 people",
  "we need to roll out in Q3",
];
let idx = 0;

export async function sendNextPersonaTurn() {
  if (idx >= PERSONA_SCRIPT.length) return;
  const text = PERSONA_SCRIPT[idx++];

  await fetch("https://app.timelines.ai/integrations/api/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.TIMELINES_AI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: Buffer.from(JSON.stringify({
      phone: process.env.AGENT_UNDER_TEST_PHONE,
      text,
    }), "utf-8"),
  });
  console.log(`[persona] sent: ${text}`);
}

JID-switch'li receiver

Bir receiver, taraf başına bir branch. Setup'tan zaten bildiğin geri kalan her şey — ?secret= auth'u, from_me echo filtresi, düz-vs-iç içe payload fallback'i — burada değişmeden uygulanır.

// api/webhook.js — route events by which number received them
export default async function handler(req, res) {
  if (req.query.secret !== process.env.WEBHOOK_SECRET) return res.status(404).end();
  const { event_type, data } = req.body || {};
  if (event_type !== "message:received:new") return res.status(200).end();
  if (data?.from_me === true) return res.status(200).end();
  res.status(200).json({ ok: true });

  const accountJid = data.whatsapp_account_id ?? data.chat?.whatsapp_account_id;
  if (accountJid === process.env.AGENT_UNDER_TEST_JID) {
    // Customer just messaged the agent — run your agent-under-test skill
    await handleAgentTurn(data);
  } else if (accountJid === process.env.PERSONA_JID) {
    // Agent just replied to the persona — advance the scripted scenario
    await advancePersona(data);
  }
}

Bu iki snippet işin özü. Tam harness — persona script'leri, scenario loader, CLI kickoff, renkli split-pane logger, Vercel config — companion repoda examples/test-harness/ altında yaşar, kendi README'sinde deploy, env var'lar, webhook kaydı ve ilk scenarionu uçtan uca çalıştırma rehberi var.

Olduğu gibi izle

Harness otonom çalışır — her yanıtı onaylaman gerekmez. Gözetimin TimelinesAI dashboard'da olur: persona numaran ile agent numaran arasındaki sohbeti başka bir browser sekmesinde aç ve her tur canlı görünür. Agent aptalca bir şey söylerse, konuşmayı yönlendirmek için agent'ın inbox'ından manuel bir yanıt gönder. Bir run'ın ortasında davranışı değiştirmen gerekirse, sohbete label ekle veya not yaz — her ikisi de agent'ın bir sonraki turda skill'ine görünür. Kayıtlı webhook'u silerek veya Vercel fonksiyonunu kapatarak run'ı durdur; scenario anında durur.


Örnek

Örnek: tam bir tur

Pratikte API'nin nasıl göründüğünü gösteren beş adımlık somut bir döngü — ajanın bir outbound gönderir, teslimatı onaylar, müşterinin yanıtını işler ve bir follow-up gönderir. Aşağıdaki telefon numaraları, sohbet ID'leri ve message UID'leri placeholder — kendininkilerle değiştir.

Bu örnek boyunca kullanılan placeholder'lar:

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

Token'ının çalıştığını onayla ve bağlı numaralarını listele

$ 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"}
]}}

Bağlı numaralarının bir listesini görmelisin. Status active değilse, devam etmeden önce TimelinesAI dashboard'undan düzelt.

2

Bir giden mesaj gönder

Payload'ı açık UTF-8 encoding ile bir dosyaya yaz, sonra curl'e ver. Bu, her giden için pattern'dir.

$ 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"}}

Yanıt bir makbuzdur, teslimat onayı değildir. Bir sonraki adım için message_uid'i sakla.

3

Teslimat durumunu kontrol et

$ 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 aktif bir numarada genelde bir saniye içinde olur. Read durumu daha sonra, alıcı sohbeti gerçekten açtığında belirir.

4

Müşteri yanıtlar, webhook'un tetiklenir

Müşteri yanıtladığında, TimelinesAI kaydettiğin webhook URL'sine POST yapar:

{
  "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"
  }
}

Receiver'ın hemen 200 ile ack verir, sonra payload'ı OpenClaw skill'ine devreder.

5

Aynı sohbetten geri yanıtla

$ 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"}}

Var olan bir sohbete /chats/{id}/messages üzerinden gönderdiğin için, gönderici otomatik olarak sohbete sahip olan WhatsApp numarası olur — sen seçmezsin, sohbet kaydı seçer. Çoklu numaralı workspace'lerde yanlışlıkla yanlış numaradan göndermemenin nedeni budur.


Sınırlar

Sınırlar ve uyarılar

Bu rehberin kapsamadığı şeyler ve nedenleri.

!
Personal numaralar cold outreach için banlanır. Personal numaralardan istenmeyen broadcast'ler desteklenen bir kullanım durumu değildir. Ban TimelinesAI geçidinde değil, WhatsApp altyapısında uygulanır, bu yüzden seni koruyamayız.
!
WhatsApp'ın 24 saatlik müşteri hizmetleri penceresi. Bir müşterinin son mesajından sonra 24 saat boyunca serbestçe yanıt verebilirsin. Bu pencerenin dışında mesajlar opt-in ve template'ler gerektirir — Business API bölgesi.
!
Asenkron teslimat. POST /messages bir message_uid döner (bir makbuz), teslimat onayı değil. Gerçek teslimatı onaylamak için /status_history kullan.
!
Ek URL'leri hızlı süresi dolar. Medyayı gecikmeli bir worker'dan değil, webhook handler'ı içinde inline indir.
!
Sadece metin, medya, reaksiyonlar ve metadata — sesli/görüntülü çağrılar yok, broadcast-status yok, WhatsApp Channels veya Stories yok.

Yapı taşları

Companion skill bundle GitHub'da →

4 çalışan skill, bir Vercel webhook receiver, compliance docs ve bu rehberin tam mirror'ı. MIT lisanslı.

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

Yetenek rehberi · 2026 · Kanonik URL timelines.ai/guide/openclaw-whatsapp-skills