İçeriğe atla

Entegrasyon Rehberi

OnlineAsistan, web sitenizdeki ziyaretçilerle önce yapay zekâ asistanı Kont ile konuşur. Ziyaretçi canlı temsilci istediğinde görüşmeyi kendi yazılımınıza aktarabiliriz: size bir webhook göndeririz, temsilciniz kendi ekranından cevap yazar.

1. Akış

  1. Ziyaretçi sitenizdeki chat balonundan yazar; Kont cevaplar.
  2. "Temsilciye bağlan" denince chat.handoff olayı webhook adresinize POST edilir — içinde ziyaretçi bilgisi, tüm görüşme geçmişi ve cevap adresi + anahtarı vardır.
  3. Temsilciniz kendi panelinizden yazar, siz cevabı bizim API'ye POST edersiniz; mesaj ziyaretçinin ekranında anında görünür.
  4. Devirden sonra ziyaretçinin yazdığı her mesaj size chat.message olayıyla iletilir.
  5. Görüşme bitince /close ucunu çağırırsınız (ya da ziyaretçi kapatır, size chat.closed gelir).

2. Size gönderdiğimiz istek

Başlıklar:

X-OA-Event: chat.handoff
X-OA-Event-Id: 9f1c...   (aynı id iki kez gelebilir — bir kez işleyin)
X-OA-Timestamp: 1785000000
X-OA-Signature: sha256=<hmac>

Gövde:

{
  "event": "chat.handoff",
  "event_id": "9f1c...",
  "sent_at": "2026-08-03T10:00:00+03:00",
  "company": { "id": 12, "name": "Örnek Firma" },
  "session": {
    "id": 481,
    "token": "d3f7...",
    "channel": "web",
    "mode": "pending",
    "started_at": "2026-08-03T09:58:12+03:00"
  },
  "reply": {
    "url": "https://onlineasistan.net/api/chat/d3f7.../reply",
    "close_url": "https://onlineasistan.net/api/chat/d3f7.../close",
    "token": "oa_..."
  },
  "data": {
    "reason": "Ziyaretçi canlı asistan istedi.",
    "visitor": { "name": "Ziyaretçi", "verified": false, "subscriber_no": null },
    "history": [
      { "role": "assistant", "content": "Merhaba...", "at": "..." },
      { "role": "user", "content": "Temsilciye bağlan", "at": "..." }
    ]
  }
}

3. İmza doğrulama

İmza, timestamp + "." + ham gövde metninin panelinizdeki imza anahtarı ile HMAC-SHA256'sıdır. 5 dakikadan eski istekleri reddedin.

4. Hazır alıcı kod

Düz PHP (tek dosya)

<?php
$secret = 'whsec_...';                       // panelden: imza anahtarı
$body   = file_get_contents('php://input');
$ts     = $_SERVER['HTTP_X_OA_TIMESTAMP'] ?? '';
$sig    = $_SERVER['HTTP_X_OA_SIGNATURE'] ?? '';

if (abs(time() - (int) $ts) > 300) { http_response_code(408); exit; }
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($expected, $sig)) { http_response_code(401); exit; }

$olay = json_decode($body, true);

// Buraya kendi kaydınızı açın (ticket, sohbet kaydı, bildirim...)
// $olay['reply']['url'] ve $olay['reply']['token'] değerlerini saklayın —
// temsilciniz cevap yazarken bunları kullanacaksınız.

http_response_code(200);
echo json_encode(['ok' => true]);

Laravel

// routes/web.php
Route::post('/onlineasistan/webhook', WebhookController::class)
    ->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\PreventRequestForgery::class]);

// app/Http/Controllers/WebhookController.php
public function __invoke(Request $request)
{
    $ts   = (string) $request->header('X-OA-Timestamp');
    $body = $request->getContent();

    abort_if(abs(time() - (int) $ts) > 300, 408);

    $expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, config('services.onlineasistan.secret'));
    abort_unless(hash_equals($expected, (string) $request->header('X-OA-Signature')), 401);

    $olay = $request->json()->all();

    // Aynı olay iki kez gelebilir — event_id ile tekilleştirin
    if (WebhookOlay::where('event_id', $olay['event_id'])->exists()) {
        return response()->json(['ok' => true]);
    }

    match ($olay['event']) {
        'chat.handoff' => app(SohbetServisi::class)->devralindi($olay),
        'chat.message' => app(SohbetServisi::class)->ziyaretciMesaji($olay),
        'chat.closed'  => app(SohbetServisi::class)->kapandi($olay),
        default        => null,
    };

    return response()->json(['ok' => true]);
}

Node.js (Express)

const crypto = require('crypto');

app.post('/onlineasistan/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const secret = process.env.OA_SECRET;
    const ts  = req.get('X-OA-Timestamp') || '';
    const sig = req.get('X-OA-Signature') || '';
    const body = req.body.toString('utf8');

    if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(408);

    const expected = 'sha256=' + crypto.createHmac('sha256', secret)
      .update(ts + '.' + body).digest('hex');
    if (expected !== sig) return res.sendStatus(401);

    const olay = JSON.parse(body);
    // ... kendi kaydınızı açın
    res.json({ ok: true });
  });

5. Temsilcinizin cevap yazması

curl -X POST https://onlineasistan.net/api/chat/{oturum-token}/reply \
  -H "Authorization: Bearer oa_..." \
  -H "Content-Type: application/json" \
  -d '{"message":"Merhaba, size nasıl yardımcı olabilirim?","agent_name":"Ayşe"}'

Diğer uçlar:

  • GET /api/chat/{token} — görüşmenin tüm geçmişi ve durumu (kendi ekranınızı beslemek için).
  • POST /api/chat/{token}/close — görüşmeyi kapatır; isteğe bağlı message alanıyla veda mesajı.

6. Widget'ı sitenize ekleme

<script src="https://onlineasistan.net/widget.js" data-key="FIRMA_ANAHTARINIZ" async></script>

Anahtarınızı ve izinli alan adlarını panelden yönetirsiniz. Güvenlik için izinli alan adı listesini doldurmanızı öneririz; böylece anahtarınız başka sitede çalışmaz.

7. Teslim garantisi

Sisteminiz geçici olarak cevap veremezse olayı 1 dk, 5 dk, 15 dk ve 1 saat sonra yeniden göndeririz. Tüm gönderimler panelinizdeki Entegrasyon sayfasında listelenir. İsteğinizi 2xx ile yanıtlayın; başka bir kod başarısızlık sayılır.

8. Kont'un kendi sisteminizden bilgi çekmesi (Veri API'si)

Ziyaretçi "borcum ne kadar?", "siparişim nerede?" diye sorduğunda Kont'un tahmin etmemesi, sizin sisteminizden gerçek bilgiyi alması gerekir. Bunun için kod yazmanıza gerek yok: panelden API adresinizi, anahtarınızı ve Kont'un çağırabileceği uçları tanımlarsınız.

Panel → Veri Bağlantıları → Yeni

  • API adresi: https://sizin-sisteminiz.com/api
  • Kimlik doğrulama: Bearer token · özel başlık (X-API-Key) · adres parametresi · HTTP Basic · yok
  • API anahtarı: şifreli saklanır, hiçbir ekranda ham gösterilmez

Sonra her uç için bir "araç" tanımlarsınız:

Araç adı     : siparis_durumu
Ne işe yarar : Sipariş numarasına göre kargo durumunu döndürür
Yöntem       : GET
Yol          : /siparis/{no}
Parametre    : no  (Yol, zorunlu)  — "Sipariş numarası"

Akış şöyle işler:

Ziyaretçi : "1234 numaralı siparişim nerede?"
Kont      : (siparis_durumu aracını no=1234 ile çağırır)
            GET https://sizin-sisteminiz.com/api/siparis/1234
            Authorization: Bearer <sizin anahtarınız>
Siz       : {"durum":"kargoda","kargo_firmasi":"Yurtiçi","takip_no":"YK123456789"}
Kont      : "Siparişiniz kargoda, Yurtiçi ile gönderildi. Takip no: YK123456789."

API'nizden beklenenler

  • JSON dönün; cevap kısa ve anlamlı olsun (dahili id, html, log alanı göndermeyin).
  • Hassas veri döndürmeyin — TC kimlik, kart numarası, şifre. Asistan bunu ziyaretçiye okuyabilir.
  • Yetkisizde 401, bulunamayanda 404 dönün; Kont bunu "bilgi bulunamadı" olarak yorumlar.
  • Yanıt süresi 15 saniyeyi geçmesin.
  • Bize verdiğiniz anahtarı yalnız okuma yetkili ayrı bir anahtar yapın; panelden istediğiniz an değiştirin.

KVKK

Kimliği doğrulanmamış ziyaretçiye borç/fatura gibi kişisel bilgi döndürmeyin. Ya API'niz doğrulama bilgisi istesin (abone no + doğum tarihi gibi), ya da önce bir "kimlik doğrula" aracı tanımlayın; Kont o başarılı olmadan diğer uçları çağırmasın.

9. Hazır örnek dosyalar

Depodaki ornekler/ klasöründe kopyala-yapıştır hazır dosyalar var:

  • 1-web-sitesi/ — siteye eklenecek kod; WordPress, Shopify, Wix, React ve Laravel için ayrı anlatım
  • 2-kendi-yazilimi/ — webhook alıcısı ve temsilci cevap kodu: düz PHP, Laravel (controller + servis), Node.js
  • 3-veri-apisi/ — Kont'un çağıracağı API'nin nasıl görünmesi gerektiği + çalışan örnek

← Ana sayfa