Genel bakış
Çağrı kayıtlarını API üzerinden periyodik olarak çekmek (polling) kota tüketir ve veriyi gecikmeli almanıza neden olur.
Webhook'lar olayları uygulamanıza anında iletir. Bir çağrı başladığında, bağlandığında veya bittiğinde Hipcall santrali uygulamanıza bir HTTP POST gönderir. Çağrı bittiği an görüşme süresi ve ses kaydı bağlantısı doğrudan veritabanınıza ulaşır.
Üretim ortamına hazır bir webhook alıcısının dört görevi vardır:
- Zaman aşımını önlemek için 50 milisaniyenin altında HTTP 200 dönmek.
- Uç noktayı gizli bir rota anahtarıyla korumak.
- Aynı çağrının farklı olaylarını ve mutabakat verilerini UUID ile tekilleştirmek.
- Sunucu kesintilerini telafi etmek için gece mutabakatı yapmak.
Bu sayfada Hipcall panelinde webhook ayarlamayı, gerçek istekleri incelemeyi ve ASP.NET Core Minimal API ile bir alıcı yazmayı anlatıyoruz.
Başlamadan önce
Şunlara ihtiyacınız var:
- .NET 8 SDK (
dotnet --version8.0 veya üstü olmalı). - Dışarıdan erişilebilir bir HTTPS adresi. Yerel ortamda port 5080'i dışarı açmak için ngrok kullanın:
ngrok http 5080
- Hipcall panelinde entegrasyon ekleme yetkisi.
- Test için çevrimiçi bir Hipcall uygulaması (web veya masaüstü).
Webhook kurulumu
Webhook'u Hipcall panelinden oluşturun:
- Ayarlar > Entegrasyonlar > Kataloğa Göz At bölümüne gidin.
- Web kancası (Webhook) seçeneğine tıklayın.
- Detayları girin:
- Ad:
Üretim CDR Alıcısıgibi bir isim verin. - URL: İçinde gizli bir yol bulunan HTTPS adresinizi yazın:
https://your-server.example.com/hipcall/events/whsec_live_xxxxxxxxxxxxxxxx. - Olaylar:
call_init,call_bridgedvecall_hangupseçeneklerini işaretleyin.
- Ad:
- Kayıtlar sekmesini açın. Hata Ayıklama Modu'nu etkinleştirdiğinizde sistem iki saat boyunca istek gövdelerini ve HTTP yanıtlarını loglar.
Gövde yapısı ve curl testi
Hipcall, istekleri application/json olarak gönderir. Paket, bir event anahtarı ve çağrı detaylarını barındıran data nesnesi içerir.
Terminalinizde aşağıdaki curl komutunu çalıştırarak yerel alıcınıza sahte bir Hipcall call_init olayı gönderebilirsiniz:
curl -X POST http://localhost:5080/hipcall/events/whsec_live_xxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{
"data": {
"credited": null,
"team_touch_at": null,
"first_touch_duration": null,
"contact_id": null,
"callee_id": null,
"answered_at": null,
"voicemail_url": null,
"caller_number": "+90850XXXXXXX",
"missing_call_reason": null,
"call_duration": null,
"callback_time": null,
"call_flow": [
{
"action": "init",
"detail": {
"id": null,
"type": "contact"
},
"timestamp": 1790691203
}
],
"direction": "outbound",
"callee_number": "+90530XXXXXXX",
"voicemail_id": null,
"callback_user_id": null,
"callee_type": "contact",
"ended_at": null,
"missing_call": null,
"channel_type": "number",
"callback_cdr_uuid": null,
"voicemail_type": null,
"caller_id": null,
"started_at": "2026-09-29T14:13:23Z",
"bridged_at": null,
"channel_id": 942,
"caller_type": null,
"user_id": 4200,
"hangup_by": null,
"uuid": "410c92c5-...masked...",
"record_url": null,
"number_id": 942,
"company_id": 80719
},
"event": "call_init"
}'
İstek başlıklarının incelenmesi
Gelen HTTP başlıkları şu şekildedir:
Host: your-server.example.com
User-Agent: mint/1.9.0
Content-Type: application/json
Accept-Encoding: gzip
X-Forwarded-For: 31.192.211.2
X-Forwarded-Proto: https
Hipcall webhook istekleri X-Signature gibi HMAC imza başlıkları içermez.
Olayların taşıdığı veriler
Tek bir çağrı birden fazla olay tetikler.
| Olay | Tetiklenme Anı | Öne Çıkan Alanlar | Kullanım Amacı |
|---|---|---|---|
call_init |
Santralde çağrı başladığında | uuid, direction, caller_number, started_at |
Oturum başlangıcını tespit etme. |
call_bridged |
Temsilci bağlandığında | uuid, direction, user_id, call_flow |
Temsilcinin katıldığını doğrulama. |
call_hangup |
Çağrı kapandığında | uuid, call_duration, hangup_by, record_url |
Görüşme özeti ve ses arşivi oluşturma. |
Tıklayıp arama (Click-to-Call) akışı
API üzerinden arama başlatıldığında Hipcall telefonu temsilciyi otomatik yanıtlar. Temsilci hemen santrale bağlandığı için, müşteri tarafı çalarken call_init ve call_bridged olayları peş peşe ulaşır.
Ses kaydı bağlantısı
call_hangup olayındaki data.record_url, 7 gün geçerli geçici bir AWS S3 bağlantısıdır (X-Amz-Expires=604800).
Ses dosyasını webhook geldiği anda arka planda indirin ve kurumunuzun kendi depolama alanına kaydedin. Geçici bağlantıyı veritabanına yazmayın.
Güvenilir mimari tasarımı
Üretim ortamı için şu dört ilkeyi uygulayın:
flowchart TD
A["Gelen Webhook İsteği"] --> B{"Gizli URL Şifresini Doğrula"}
B -- "Geçersiz" --> C["401 Unauthorized"]
B -- "Geçerli" --> D["Gövdeyi Ayrıştır ve UUID Kontrolü Yap"]
D --> E["Anında HTTP 200 OK Dön (< 50 ms)"]
subgraph BG ["Arka Plan Asenkron İşleme"]
F["Veritabanına Tekil Kayıt Yaz (Upsert)"]
F --> G{"record_url Var mı?"}
G -- "Evet" --> H["MP3 Dosyasını İndir"]
G -- "Hayır" --> I["Tamamlandı"]
H --> I
end
D -.->|Asenkron Görev| F
subgraph REC ["Gece Mutabakat Görevi"]
J["Zamanlanmış Görev: Gece 02:00"] --> K["Hipcall REST API: GET /api/v3/calls"]
K --> L["UUID Küme Farkını Hesapla"]
L --> M["Kaçan Çağrıları ve Sesleri Arşive Ekle"]
end
1. Hızlı cevap verin, ağır işleri arka plana devredin
Hipcall yanıtı 15 saniye içinde bekler. Alıcı ses kaydı indirmek için beklerse bağlantı zaman aşımına uğrar.
İşlem sırası:
- Gizli anahtarı doğrulayın.
- JSON gövdesini çözün.
- Veriyi kuyruğa alın.
- Anında HTTP 200 OK dönün (50 ms altı).
- Ses indirme ve veritabanı işlemlerini arka planda yapın.
2. Tekilleştirme (Idempotency)
Aynı çağrıya ait call_init, call_bridged ve call_hangup olayları farklı zamanlarda aynı uuid'yi taşıyarak ulaşır. Sisteminiz bunları yeni kayıt olarak değil, mevcut kaydın güncellemeleri olarak işlemelidir. Ayrıca, gece mutabakatı (nightly reconciliation) sırasında API'den günün tüm çağrılarını çektiğinizde, webhook ile zaten kaydedilmiş çağrılar da gelecektir. Veri kirliliğini önlemek için:
- Tekilleştirme anahtarı olarak
data.uuidkullanın. - Zaman damgası veya telefon numarası üzerinden tekilleştirme yapmayın.
- Gelen kayıtlarla mevcut kaydı güncelleyin (upsert).
3. Gece mutabakatı
Yalnızca webhook kullanan bir sistemde sunucu yeniden başlatmaları sırasında veri kaçabilir.
Eksiksiz bir arşiv için:
- Her gece çalışan bir görev oluşturun.
- API'den günün çağrılarını çekin:
GET /api/v3/calls?started_at[gte]=...&started_at[lte]=...&limit=100
- API'deki UUID'ler ile kendi veritabanınızı karşılaştırın.
- Eksik çağrıları ve ses kayıtlarını API üzerinden tamamlayın.
4. İmzasız uç noktaları koruma
HMAC başlığı olmadığı için alıcı adresinizi korumanız gerekir:
- Gizli URL yolu: URL'nizde gizli bir anahtar bulundurun (
/hipcall/events/whsec_live_...). Anahtar yoksa HTTP 401 Unauthorized dönün. - IP beyaz listesi: Güvenlik duvarınızda (Nginx, Cloudflare) sadece Hipcall çıkış IP adresine (
31.192.211.2) izin verin.
Minimal API alıcı örneği
Aşağıdaki ASP.NET Core Minimal API uç noktası, JSON gövdesini ayrıştırır, gizli anahtarı doğrular, işlemi arka plana devredip santrale derhal 200 OK döner.
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options => options.ListenAnyIP(5080));
var app = builder.Build();
var expectedSecret = Environment.GetEnvironmentVariable("HIPCALL_WEBHOOK_SECRET")
?? throw new InvalidOperationException("HIPCALL_WEBHOOK_SECRET ortam değişkeni bulunamadı.");
var jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
PropertyNameCaseInsensitive = true
};
app.MapPost("/hipcall/events/{secret?}", async (string? secret, HttpRequest request) =>
{
if (string.IsNullOrEmpty(secret) || !string.Equals(secret, expectedSecret, StringComparison.Ordinal))
{
return Results.Unauthorized();
}
HipcallWebhookPayload? payload;
try
{
payload = await JsonSerializer.DeserializeAsync<HipcallWebhookPayload>(request.Body, jsonOptions);
}
catch
{
return Results.Ok();
}
if (payload?.Data?.Uuid != null)
{
// Veritabanı ve ses indirme işlemlerini arka plana devret
_ = Task.Run(() => ProcessWebhookAsync(payload.Event, payload.Data.Uuid));
}
// Bekletmeden anında HTTP 200 OK dön
return Results.Ok();
});
app.Run();
async Task ProcessWebhookAsync(string? eventName, string uuid)
{
// Tekilleştirme (Idempotency) ve ses indirme kodları buraya gelir
Console.WriteLine($"Arka planda işleniyor: {eventName} - {uuid}");
await Task.CompletedTask;
}
public class HipcallWebhookPayload
{
public string? Event { get; set; }
public CallDataPayload? Data { get; set; }
}
public class CallDataPayload
{
public string? Uuid { get; set; }
public string? Direction { get; set; }
public string? CallerNumber { get; set; }
public string? CalleeNumber { get; set; }
public int? CallDuration { get; set; }
public string? RecordUrl { get; set; }
public string? HangupBy { get; set; }
public string? VoicemailId { get; set; }
public DateTime? StartedAt { get; set; }
public DateTime? AnsweredAt { get; set; }
public DateTime? BridgedAt { get; set; }
public DateTime? EndedAt { get; set; }
}
Hata aldığınızda
1. HTTP 500 yanıtı
Sunucunuz 500 Internal Server Error döndürdüğünde:
- Telefon görüşmesi etkilenmez. Webhook dağıtımı ile telefon trafiği bağımsızdır.
- Hipcall hatayı loglara yazar.
- Hipcall istekleri tekrar denemez (at-most-once). Bu yüzden 200 dönmek ve kaçan kayıtları gece tamamlamak zorunludur.
2. Zaman aşımı
Alıcınız 15 saniyeden geç yanıt verirse Hipcall bağlantıyı keser ve olayı başarısız sayar.
3. Kırık durumu
Alıcınız bir saat içinde 4 kez başarısız yanıt (200 dışı kod veya 15 saniye zaman aşımı) verirse:
- Hipcall entegrasyonu Kırık (Broken) duruma alır.
- Siz tekrar Aktif konuma getirene kadar yeni webhook göndermez.
- Düzeltmek için paneli açın, durumu Aktif yapıp kaydedin.
Daha fazla bilgi için Hipcall API Referansı sayfasına bakın. Bu kurallar Webkancaları v1 için geçerlidir.
Parametre listesi
Çağrı olaylarında data içindeki temel alanlar:
| Parametre | Tip | Örnek | Açıklama |
|---|---|---|---|
uuid |
string | "9a266251-d2a3-44fc-b422-9486ddf880c7" |
Çağrının tekil kimliği. |
direction |
string | "outbound" |
"inbound" veya "outbound". |
caller_number |
string | "+90850XXXXXXX" |
Arayan numara. |
callee_number |
string | "+90530XXXXXXX" |
Aranan numara. |
call_duration |
integer | 14 |
Toplam konuşma süresi (saniye). |
missing_call |
boolean | false |
Yanıtsız gelen çağrılarda true. |
hangup_by |
string | "contact" |
Kapatan taraf ("user", "contact", "system"). |
record_url |
string/null | "https://storage.hipcall.com.tr/..." |
AWS S3 ses indirme bağlantısı. |
started_at |
string | "2026-09-21T10:37:07Z" |
Çağrının başlama anı (UTC). |
answered_at |
string/null | "2026-09-21T10:37:07Z" |
Çağrının yanıtlanma anı (UTC). |
ended_at |
string/null | "2026-09-21T10:37:21Z" |
Çağrının kapanma anı (UTC). |
Sonraki adımlar
- HTTP alıcısı ile veritabanı arasına RabbitMQ ekleyin.
- PostgreSQL kullanarak
uuidalanını eşsiz (unique) ayarlayın. - Gece mutabakatını
GET /api/v3/callsile otomatikleştirin. - Sorularınızı Hipcall Topluluk platformunda paylaşın.