Ses hizmeti sözleşmesi
Ses hizmetinin arama oluşturmak, transkript turları eklemek ve aramaları kapatmak için çağırdığı imzalı webhook.
Yalnızca sözleşme
Bu bir sözleşmedir. Python ses hizmeti (WebRTC ve SIP üzerinde konuşmadan metne, dil modeli ve metinden konuşmaya) henüz yazılmamıştır. Webhook'un Callwise tarafı yazılmış ve test edilmiştir; üretimde onu çağıran bir şey yoktur.
Arama kayıtlarını yazan tek taraf ses hizmetidir. Her aramayı uygulamaya bir dizi olay olarak bildirir. Yükün kaynağı, apps/web/lib/callwise/voice-events.ts içindeki zod şeması voiceEventSchema değeridir.
Uç nokta
POST /api/voice/events
Content-Type: application/json
x-callwise-timestamp: 1760530212
x-callwise-signature: 5f0c2e…(64 hex characters)İmzalama
| Üstbilgi | Değer |
|---|---|
x-callwise-timestamp | İsteğin imzalandığı andaki Unix zamanı, saniye cinsinden |
x-callwise-signature | `${timestamp}.${rawBody}` ifadesinin, paylaşılan gizli anahtar CALLWISE_VOICE_SECRET ile alınmış onaltılık HMAC-SHA256 değeri |
Gönderdiğiniz baytların aynısını imzalayın. Uygulama, gövdeyi ayrıştırmadan önce imzayı ham gövde üzerinden yeniden hesaplar.
import { createHmac } from 'node:crypto';
const timestamp = Math.floor(Date.now() / 1000).toString();
const rawBody = JSON.stringify(event);
const signature = createHmac('sha256', process.env.CALLWISE_VOICE_SECRET!)
.update(`${timestamp}.${rawBody}`)
.digest('hex');import hmac, hashlib, json, os, time
timestamp = str(int(time.time()))
raw_body = json.dumps(event, separators=(",", ":"))
signature = hmac.new(
os.environ["CALLWISE_VOICE_SECRET"].encode(),
f"{timestamp}.{raw_body}".encode(),
hashlib.sha256,
).hexdigest()Zaman damgası sunucu saatinden 5 dakikadan fazla sapan istekler reddedilir; bu, eski isteklerin yeniden oynatılmasını engeller.
Yanıtlar
| Durum | Ne zaman |
|---|---|
200 { "ok": true, "duplicate": false } | Olay uygulandı |
200 { "ok": true, "duplicate": true } | Bu id ile bir olay zaten uygulanmıştı; hiçbir şey değişmedi |
400 | Gövde geçerli bir JSON değil veya şemayla eşleşmiyor (örneğin bilinmeyen bir olay türü) |
401 | İmza eksik veya yanlış ya da zaman damgası 5 dakikalık pencerenin dışında |
Yineleme güvenliği
Her olayın kendi id değeri vardır. Aynı olayı iki kez göndermek güvenlidir: ikincisi duplicate: true ile onaylanır ve yok sayılır. Ağ hatalarında ve 5xx yanıtlarında aynı id ile yeniden deneyin.
Olayın biçimi
Her olayın zarfı aynıdır:
| Alan | Tür | Notlar |
|---|---|---|
id | string, 1–200 karakter | Olay kimliği; olay başına benzersiz |
type | call.started | call.turn | call.ended | |
accountId | uuid | Çalışma alanı (ekip hesabı) |
call | nesne | call.id (uuid) zorunludur. Onu ses hizmeti seçer ve o aramanın her olayında aynı kimliği kullanır. |
turn | nesne | Yalnızca call.turn üzerinde |
call.started
Canlı bir arama oluşturur. Arayan, telefon numarasıyla eşleştirilir veya yeni oluşturulur. agentId verilmezse ajan, aranan numaradan seçilir.
{
"id": "evt_01JA8S0K6Q2M3",
"type": "call.started",
"accountId": "3c1d6c1e-5c55-4f3e-9d0b-6b1f6f2a7a10",
"call": {
"id": "9b2f7d4e-1a7c-4e0f-8f5e-2d6c4b9a1e33",
"to": "+15552108842",
"callerPhone": "+15551237890",
"callerName": "Emily Johnson",
"direction": "inbound",
"startedAt": "2026-10-15T13:41:02Z",
"isTest": false,
"afterHours": false
}
}İsteğe bağlı call alanları şunlardır: numberId (uuid) veya to (E.164), agentId, locationId, callerPhone, callerName, direction (inbound veya outbound), startedAt (ISO 8601), isTest, afterHours.
call.turn
Transkripte bir satır ekler. call yalnızca kimliği taşır. Turlar (call id, seq) ile saklanır; bu yüzden bir turu yeniden göndermek onun yerini alır.
{
"id": "evt_01JA8S0M1T7Z4",
"type": "call.turn",
"accountId": "3c1d6c1e-5c55-4f3e-9d0b-6b1f6f2a7a10",
"call": { "id": "9b2f7d4e-1a7c-4e0f-8f5e-2d6c4b9a1e33" },
"turn": {
"seq": 1,
"atMs": 4200,
"speaker": "caller",
"text": "Hi, I need a cleaning next week."
}
}turn alanı | Tür |
|---|---|
seq | 0 veya daha büyük tamsayı; arama içindeki sıra |
atMs | 0 veya daha büyük tamsayı; arama başladığından beri geçen milisaniye |
speaker | agent | caller | staff | system |
text | string |
call.ended
Aramayı kapatır. Faturalanan dakikalar ceil(durationS / 60) değeridir ve günün kullanımına eklenir. Uygulama ardından arama için kredi tahsil eder: faturalanan dakikalar çarpı ajanın model yığını oranı, artı bir özet (bkz. Yapay zeka modelleri ve krediler). Tutar, paylaşılan kredi havuzundan alınır. summary verilmezse bir şablon özeti doldurulur.
call.started yanıtı, ses hizmetine aramanın nasıl yönlendirileceğini söyler: route: "agent" veya kredi havuzu, eşzamanlılık sınırı ya da saatlik sınır izin vermediğinde bir reason ile birlikte route: "fallback". Aramalar asla düşürülmez.
{
"id": "evt_01JA8S2B9W0K8",
"type": "call.ended",
"accountId": "3c1d6c1e-5c55-4f3e-9d0b-6b1f6f2a7a10",
"call": {
"id": "9b2f7d4e-1a7c-4e0f-8f5e-2d6c4b9a1e33",
"endedAt": "2026-10-15T13:42:26Z",
"durationS": 84,
"intent": "booking",
"topic": "Dental cleaning",
"outcome": "booked",
"sentiment": "positive",
"confidence": 0.96,
"summary": "New caller booked a dental cleaning for Tuesday at 10:00.",
"recordingPath": "3c1d6c1e-5c55-4f3e-9d0b-6b1f6f2a7a10/9b2f7d4e-1a7c-4e0f-8f5e-2d6c4b9a1e33.mp3"
}
}call alanı | Değerler |
|---|---|
endedAt | ISO 8601 |
durationS | saniye cinsinden tamsayı |
intent | booking reschedule cancel pricing insurance billing hours emergency general spam |
topic | serbest metin |
outcome | booked rescheduled cancelled info transferred voicemail missed spam |
sentiment | positive neutral negative |
confidence | 0–1 |
summary | serbest metin |
handoffReason | serbest metin; sonuç transferred olduğunda |
recordingPath | özel cw-recordings kovasında <accountId>/<callId>.mp3 |
Nasıl uygulanır
Rota, imzayı ve şemayı denetler; ardından hizmet rolüyle Postgres işlevi cw_apply_voice_event(jsonb) çağrısını yapar. Bu işlevi yalnızca hizmet rolü çalıştırabilir. Oturum açmış kullanıcıların aramalarda veya turlarda ekleme izni yoktur; bu yüzden arama kayıtlarının oluşturulmasının tek yolu webhook'tur.
Yayından önce
Gecikme, araya girmenin ele alınması ve gizli anahtarın döndürülmesi konusunda doğrulanması gerekenler için Bağlantılar altındaki ses hizmeti maddesine bakın.