npm install signalbird · composer require pariette/signalbird · pip install signalbirdYönetim (Management) - kendi projenizi kodla yönetin
Panelde tıklayarak yaptığınız her şeyi kodla yapar: Telsiz kanalı açmak, olay akışını okumak, sohbet gelen kutusunu işlemek, uygulama kaydı ve cihaz listesi yönetmek.
Bu bir admin yüzeyi değildir. Anahtar tek bir takıma bağlıdır ve yalnız o takımın kayıtlarına dokunur; başka takımın kaydı 404 döner. Kullanıcı yönetimi, faturalama, abonelik ve plan işlemleri SDK'da yoktur ve olmayacaktır - onlar panelin ve şirket sahibinin işidir.
Ne zaman işinize yarar:
- Ortam kurulumu: her yeni ortam için (staging, prod, müşteri kurulumu) Telsiz kanallarını CI'da açmak.
- Kendi ajan arayüzünüz: sohbeti kendi panelinize gömmek ya da CRM'e köprülemek.
- Bot: gelen kutusuna otomatik ilk yanıt yazmak, konuşmayı sınıflandırmak.
- Mobil sürüm çıkarken uygulama kaydını elle açmak zorunda kalmamak.
Anahtar
Kapsam (scope) sistemi 1 Eyl 2026'da kalktı. Yönetim, gönderimle aynı
anahtarı kullanır: gizli domain anahtarı (sb_secret_live_…). Panelde
Alan adları → [alan adı] → Anahtarlar → Yeni anahtar ile üretilir, yalnız
bir kez gösterilir ve projenizin .env dosyasına SIGNALBIRD_DOMAIN_KEY
olarak konur. Sunucu anahtarı için alan adının DNS doğrulaması gerekmez.
Gizli anahtar yalnız sunucuda çalışır; Origin başlığı taşıyan (tarayıcıdan
gelen) istekte 401 SECRET_KEY_IN_BROWSER döner. Açık anahtar
(sb_public_live_…) yönetim uçlarını hiç açmaz.
Kurulum
npm install signalbird # Node.js / TypeScript
composer require pariette/signalbird # PHP / Laravel
pip install signalbird # Python
go get github.com/Pariette-Inc/signalbird.sdk/v2
dotnet add package Signalbird.SdkMetot adları beş dilde de birebir aynıdır; yalnız yazım geleneği değişir
(createModuleKey / create_module_key / CreateModuleKey /
CreateModuleKeyAsync).
Hızlı başlangıç
import { management } from 'signalbird'
// SIGNALBIRD_DOMAIN_KEY ortam değişkeninden okunur
const created = await management().createModuleKey('logger', { name: 'ödeme-servisi' })
// `secret` YALNIZ burada döner: sunucuda yalnız SHA-256 özeti saklanır.
// Kaybederseniz panelden yenisini üretirsiniz; geri okunamaz.
await vault.write('SIGNALBIRD_DOMAIN_KEY', created.data!.secret)Kendi istemcinizi kurmak isterseniz:
import { SignalbirdManagement } from 'signalbird'
const admin = new SignalbirdManagement({
domainKey: process.env.SIGNALBIRD_DOMAIN_KEY!,
timeout: 15000,
throwOnError: false, // varsayılan; geliştirmede açabilirsiniz
})Açık anahtar verirseniz (sb_public_live_…) ilk istekte değil,
istemciyi kurarken WRONG_KEY_TYPE hatası alırsınız.
Sonuç biçimi
Her metot aynı zarfı döner:
{ ok: true, status: 201, data: { … } }
{ ok: false, status: 403, code: 'API_KEY_SCOPE', message: '…', data: { … } }throwOnError: true iken hata yerine SignalbirdError fırlatılır
(code, status, body taşır).
Telsiz yönetimi
Kanallar
Telsiz'de proje yoktur; kanal, modül anahtarıdır (logger modülü). Kanal
bildirim kuralının durduğu yerdir: kim alacak, push mu e-posta mı, sessiz
saatlerde ne olacak, aynı mesaj kaç kez uyaracak. Kanalın gizli bir sırrı
YOKTUR: adı kodun içinde durur. Gizli olan tek şey domain anahtarıdır ve
panelde üretilir.
await admin.listModuleKeys('logger')
const { data } = await admin.createModuleKey('logger', {
title: 'Ödeme',
key: 'odeme', // kodunuzda radio('odeme') diye çağıracağınız ad
level: 'critical', // kanalın varsayılan seviyesi
notify: ['push', 'email'],
recipient_user_ids: [12, 34], // boş = bütün takım
quiet_from: 0,
quiet_to: 7, // kritik seviye sessiz saatleri yine de deler
dedupe_seconds: 300, // aynı mesajı 5 dk içinde tekrar duyurma
})
await admin.getModuleKey('logger', data!.module_key.id)
await admin.updateModuleKey('logger', data!.module_key.id, { level: 'error' })
await admin.deleteModuleKey('logger', data!.module_key.id)
keydeğiştirilebilir; eski ad 30 gün daha kabul edilir, böylece üretimdeki kod bir sonraki yayına kadar kayıt kaybetmez.
Tanımsız bir ada log gelirse kanal kendiliğinden ve sessiz açılır (bildirim kuralını ekip koyar). AWS SNS / CloudWatch bağlantısı için kanalın gelen bağlantı adresi panelden üretilir (Telsiz → Kanallar → AWS / Webhook).
Olay akışı
await admin.radioSummary()
await admin.radioEvents({
key: 'odeme',
level: ['critical', 'error'],
q: 'ödeme',
from: '2026-08-01',
page: 1,
per_page: 50,
})Sohbet - ajan tarafı
Panelin Gelen Kutusu ekranıyla aynı uçlar. "Ajan" olarak anahtarı üreten kullanıcı kaydedilir; sahipsiz (miras) anahtar yazma yapamaz.
const inbox = await admin.listConversations({ status: 'open', per_page: 20 })
const detail = await admin.getConversation(conversationId)
const messages = await admin.listConversationMessages(conversationId, {
after: lastMessageId, // cm_… imleci; tam listeyi tekrar çekmez
include_internal: true, // iç notlar - ziyaretçi bunları asla görmez
})Yanıtlama
await admin.reply(conversationId, { body: 'Merhaba! Birazdan dönüyoruz.' })
// İç not: gelen kutusunda görünür, ziyaretçiye ASLA gitmez
await admin.reply(conversationId, { body: 'Ödeme ekibine soruldu', is_internal: true })
await admin.editChatMessage(conversationId, messageId, 'Düzeltilmiş metin')
await admin.deleteChatMessage(conversationId, messageId)
await admin.reactToChatMessage(conversationId, messageId, '👍') // ikinci kez → kaldırırAkış yönetimi
await admin.assignConversation(conversationId) // anahtarın sahibine ata
await admin.assignConversation(conversationId, 42) // belirli üyeye ata
await admin.setConversationStatus(conversationId, 'resolved')
await admin.updateConversation(conversationId, { priority: 'high', tags: ['ödeme'] })
await admin.readConversation(conversationId, lastMessageId)
await admin.setTyping(conversationId, true)Ziyaretçi ve hazır yanıtlar
await admin.getVisitor(visitorId)
await admin.updateVisitor(visitorId, { name: 'Ayşe Yılmaz', attributes: { plan: 'pro' } })
await admin.banVisitor(visitorId)
await admin.listCannedReplies()
await admin.createCannedReply({ shortcut: '/kargo', title: 'Kargo', body: 'Kargonuz yolda.' })
await admin.updateCannedReply(id, { body: 'Kargonuz bugün çıktı.' })
await admin.deleteCannedReply(id)Bot deseni
// Her dakika: yeni açılan konuşmalara ilk yanıtı yaz
const inbox = await management().listConversations({ status: 'open' })
for (const conversation of inbox.data?.data ?? []) {
if (conversation.unread_count === 0) continue
await management().reply(conversation.id, {
body: 'Merhaba! Ekibimiz birkaç dakika içinde yanıtlayacak.',
})
await management().setConversationStatus(conversation.id, 'pending')
}Yoklama aralığını makul tutun: okuma sınırı dakikada 240, yazma 60 istektir.
Sohbet - tetikleyiciler
"Şu olduğunda şunu yap." Kural kayıtta durur, kodda değil: müşteri davranışı değiştirmek için sürüm çıkarmak zorunda kalmasın.
await management().createChatTrigger({
name: 'Fiyat sorusu → satış',
event: 'visitor.message', // conversation.created | visitor.message | no_reply
conditions: {
match: 'all',
rules: [{ field: 'body', op: 'contains', value: 'fiyat' }],
},
actions: [
{ type: 'reply', body: 'Merhaba! Fiyat listemizi hemen paylaşıyoruz.' },
{ type: 'tag', value: 'satış' },
{ type: 'priority', value: 'high' },
],
})| Alan | Değerler |
|---|---|
event |
conversation.created · visitor.message · no_reply (delay_seconds sonra, hâlâ yanıt yoksa) |
| koşul alanı | body · page_url · source · within_hours · visitor_is_new · visitor_email · tag |
| işleç | contains · not_contains · eq · neq · exists · not_exists · is_true · is_false |
| eylem | reply · internal_note · tag · priority · assign |
Koşul listesi boş bırakılabilir: tetikleyici o olayın her seferinde çalışır (karşılama mesajı böyle kurulur). Aynı tetikleyici aynı konuşmada bir kez konuşur; yoksa "fiyat" geçen her mesaja aynı yanıt giderdi.
Otomatik yanıt system göndericisiyle yazılır, ajan olarak değil. Ajan gibi
görünseydi ziyaretçi karşısında insan olduğunu sanır, first_response_at
damgası da yalan söyler ve SLA raporunuz bozulurdu.
Atama eylemi yalnız atanmamış konuşmada çalışır: bir insan üstlendiyse otomasyon onu masadan almaz.
Sohbet - rapor
const { data } = await management().chatReport('30d') // 7d | 30d | 90d{
"volume": { "total": 412, "open": 18, "resolved": 380, "closed": 14, "unanswered": 6 },
"first_response": { "median_s": 96, "p90_s": 640, "answered": 406 },
"resolution": { "median_s": 1820, "p90_s": 9400, "resolved": 380 },
"satisfaction": { "average": 4.6, "count": 210, "breakdown": { "5": 160, "4": 35 } },
"agents": [{ "user_id": 7, "name": "Zeynep", "assigned": 190, "replies": 812,
"median_first_response_s": 74, "rating_average": 4.8 }]
}İki şeyi bilerek böyle yapıyoruz:
- Ortalama değil ortanca + p90. Tek bir gece yarısı yanıtı ortalamayı saatlerce yukarı çeker ve kimsenin tanımadığı bir sayı üretir. Ortanca tipik günü, p90 kötü günü anlatır.
- Veri yoksa
null,0değil. "0 saniyede yanıtlıyoruz" rapor ekranındaki en tehlikeli yalandır. Ekranınızda "-" gösterin.
volume.unanswered raporun en önemli sayısıdır: hiç ajan yanıtı görmemiş
konuşmalar.
Uygulamalar
Uygulama, müşterinizin sitesine ya da mobil uygulamasına gömülen açık anahtarın
(sb_public_live_…) sahibidir.
const app = await admin.createModuleKey('chat', {
name: 'Mobil - iOS',
platform: 'ios',
chat_enabled: true,
push_enabled: true,
})
// public_key gizli DEĞİLDİR; zaten istemciye gömülür
console.log(app.data!.public_key) // sb_public_live_…
await admin.updateModuleKey('chat', id, { allowed_origins: ['https://ornek.com'] })
await admin.updateModuleKey('chat', id) // eski anahtar ANINDA geçersiz
await admin.listModuleKeyDevices('push', id, { page: 1 }) // token maskeli döner
await admin.deleteModuleKey('chat', id)Anahtar yenilemek kanal adını DEĞİŞTİRMEZ: domain anahtarını panelden yenilersiniz, sitenizdeki
data-channelaynı kalır. v1'de anahtar yenilemek gömme satırını kırıyordu.
Signalbird ekranını KENDİ panelinizde göstermek
Gelen kutusunu, monitörleri ya da kampanya ekranını kendi yönetim panelinize gömebilirsiniz. Ekran Signalbird'ün kendisidir - kopyası değil; biz güncelledikçe sizinki de güncellenir.
const { data } = await admin.embedToken({ module: 'chat' })
// data.url → 120 saniyelik, TEK KULLANIMLIK adres
// <iframe src={data.url} />$embed = Signalbird::management()->embedToken(['module' => 'chat']);
$url = $embed['data']['url'];Modüller: chat · topics · messages · contacts · campaigns ·
monitoring · radio · members.
Üç şey bilinmeli:
- Adresi sunucunuzda üretin. Takım anahtarı tarayıcıya inmemeli; tarayıcıya yalnız kısa ömürlü adres iner.
- Jeton 120 saniye yaşar ve bir kez kullanılır. URL'de gittiği için
Refererbaşlığına ve loglara düşer; kısa ömür bunun içindir. İframe yüklendikten sonra 60 dakikalık bir oturum kurulur. - Anahtarınızın
embed:issuekapsamı olmalı. Bu kapsam bilerek ayrıdır: gömme jetonu bir panel oturumuna çevrilir, yani dar kapsamlı bir anahtarın üretebilmesi kapsam kısıtını anlamsız kılardı.user_idvermezseniz anahtarın sahibi kullanılır; verdiğinizde o kişi takımın üyesi olmalıdır.
Hata kodları
| Kod | Anlamı |
|---|---|
WRONG_KEY_TYPE |
sb_ dışı anahtar verildi - kurulum anında |
API_KEY_INVALID |
Anahtar yok, süresi dolmuş ya da iptal edilmiş |
API_KEY_SCOPE |
Anahtarda gereken kapsam yok (required_scope alanına bakın) |
API_KEY_IP_BLOCKED |
Anahtar IP kısıtına takıldı |
MODULE_DISABLED |
Paketinizde bu modül yok (logger, chat) |
VALIDATION_ERROR |
422 - gövde doğrulamadan geçmedi |
NETWORK_ERROR / TIMEOUT |
Ağ (status 0) |
HTTP_<durum> |
Sunucu kod vermediyse |
404 iki anlama gelir ve ayırt edilmez: kayıt yok ya da başka bir takımın. Bu kasıtlıdır - aksi hâlde kimlik numarası deneyerek başka takımların kayıt sayısı öğrenilebilirdi.
Diğer diller
// PHP / Laravel
Signalbird::management()->createModuleKey('logger', ['name' => 'ödeme-servisi']);
Signalbird::management()->reply($conversationId, ['body' => 'Merhaba!']);# Python
admin = SignalbirdManagement(domain_key=os.environ["SIGNALBIRD_DOMAIN_KEY"])
admin.create_module_key({"name": "ödeme-servisi"})
admin.reply(conversation_id, {"body": "Merhaba!"})// Go
admin, _ := signalbird.NewManagement(signalbird.KeyConfig{DomainKey: key})
admin.CreateModuleKey(ctx, "logger", map[string]any{"title": "Ödeme hataları"})// .NET
await management.CreateModuleKeyAsync("logger", new Dictionary<string, object?> { ["title"] = "Ödeme hataları" });Tam metot listesi (45 metot) ve davranış kuralları:
signalbird.sdk/docs/CONTRACT.md § 10.