WApi, WhatsApp Business API ile aynı yüzeyi sunan, ancak kendi sunucunuzda çalışan bir mesajlaşma ağ
geçididir. REST üzerinden mesaj gönderir, Webhook üzerinden gelen mesaj ve durum güncellemelerini sizin uygulamanıza
iletir, SignalR üzerinden canlı olay akışı yayınlar. SaaS aboneliği, satır başı ücretlendirme veya kapalı kara kutu
yoktur — kaynak kodu sizdedir, veriler sizin SQL Server'ınızda kalır, kimlik doğrulama anahtarları yalnızca sizde durur.
Standart bir HTTP kütüphanesi konuşan her ortam, WApi ile dakikalar içinde entegre olabilir.
Aşağıdaki yetenekler hazır kurulumda etkindir; ek lisans, ek ücret veya ek modül gerektirmezler.
REST + JSON, Standart HTTP
Tüm uç noktalar /api altında, JSON gövde-yanıt, standart durum kodları. C#, VB.NET, Java, PHP,
Python, Node, Go, hatta curl ile çalışır. Swagger UI ile uçları kod yazmadan keşfedebilir,
Try-it-out üzerinden anlık deneme yapabilirsiniz.
HMAC İmzalı Webhook
Gelen mesajları, mesaj status değişikliklerini (sent/delivered/read) ve session olaylarını (qr, ready,
disconnected) sizin verdiğiniz URL'e POST eder. Her istek X-WApi-Signature başlığında ham gövdenin
HMAC-SHA256 imzasını taşır. Başarısız teslim girişimleri yapılandırılabilir sayıda yeniden denenir.
SignalR Canlı Olay Akışı
/api/events hub'ı üzerinden, browser veya .NET istemcileriniz oturuma abone olur ve QR
gösterimi, mesaj akışı, status değişikliği gibi olayları gerçek zamanlı izler. Polling gerektirmez.
Toplu Kampanya, Throttling ile
send-bulk uç noktası ile tek istek üzerinden yüzlerce alıcıya gönderim yapın. Her alıcı için
özel metin verilebilir, gönderim aralığı (intervalMs) milisaniye cinsinden ayarlanabilir.
Süreç boyunca sent, failed ve status sayaçlarını batch dökümünden izleyin.
Rol Tabanlı API Anahtarları
Üç düzey rol — Admin (her şey), Operator (mesaj + webhook yönetimi),
Viewer (yalnızca okuma). Her anahtar için isteğe bağlı CIDR allowlist ve oturum-bazlı kısıtlama.
Anahtarlar veritabanında SHA-256 hash olarak saklanır; ham değer yalnızca yaratma anında bir kez gösterilir.
SQL Server Yerli, EF Core'suz
Veri katmanı Microsoft.Data.SqlClient üzerine kurulu ince bir SqlHelper ile
çalışır — ORM yoktur. Tablolar boot anında idempotent CREATE TABLE ile kurulur. Stored procedure,
table-valued parameter ve hand-tuned query'lerle istediğiniz noktayı optimize edebilirsiniz.
Çoklu Oturum, Çoklu Numara
Her WhatsApp hesabı bir session'dır. Birden fazla numarayı tek bir WApi kurulumu içinden yönetin.
Her oturumun kendi mesaj geçmişi, webhook yapılandırması, durum bilgisi ve auth-state'i vardır.
Audit Log, İz Sürülebilir
Her API çağrısı — kim, hangi rolle, hangi IP'den, ne yaptı, hangi sonuç — dbo.audit_logs
tablosuna yazılır. GET /api/audit ucu üzerinden filtrelenebilir.
Mimari ve Çalışma Yöntemi
WApi katmanlı bir tasarıma sahiptir. Her katmanın bir tek sorumluluğu vardır ve katmanlar
yalın arayüzler üzerinden konuşur. Aşağıdaki şema istemci isteğinin uçtan uca yolunu gösterir.
SqlHelper (ADO.NET wrapper), per-entity repository'ler, ApiKeyAuthHandler,
WebhookDispatcher, ve aktif IWhatsAppEngine uygulamasının kaydı.
WApi.Api
ASP.NET Core Controllers, Swagger pipeline, SignalR hub, UseStaticFiles ile bu sayfa,
Forwarded Headers, Windows Service hosting.
Engine Katmanı
WhatsApp protokolü IWhatsAppEngine arkasında soyutlanmıştır. Konfigürasyon (Engine:Type)
ile aşağıdaki uygulamalar arasında seçim yapılır:
null varsayılan
İçeriden bir stub. Tüm engine çağrılarına sahte yanıt verir, gönderilen mesajlar dbo.messages'a
yazılır ve NULL- önekli senkron sahte mesaj id'leri döner. Gerçek WhatsApp ağına
bağlanmaz. Geliştirme, entegrasyon testi ve demo için idealdir.
node-bridge üretim
WApi'nin C# tarafı, aynı sunucuda 127.0.0.1'de çalışan küçük bir Node.js yan-sürecine HTTP üzerinden
komut iletir. Yan-süreç @whiskeysockets/baileys
kütüphanesini kullanarak gerçek WhatsApp Multi-Device protokolüne bağlanır. Yan-süreç paylaşılan bir
bearer (X-Bridge-Token) ile korunur ve loopback dışına dinlemez.
İstek Yaşam Döngüsü
İstemciX-API-Key başlığıyla HTTPS isteği gönderir.
Caddy TLS'i sonlandırır, X-Forwarded-For ve -Proto başlıklarını ekler,
isteği 127.0.0.1:2785'e iletir.
Kestrel isteği alır; UseForwardedHeaders ile gerçek istemci IP'si geri kazanılır.
ApiKeyAuthHandler başlığı trim eder, SHA-256 hash'ler, dbo.api_keys'te eşleşme arar.
Aktif mi, süresi geçmiş mi, CIDR allowlist'e uygun mu kontrol eder. ClaimsPrincipal oluşturup pipeline'a verir.
Authorization Policy rolün ucu çağırmaya yetkili olup olmadığını doğrular (AdminOnly /
OperatorOrAdmin / AnyRole).
Controller validation çalıştırır, IWhatsAppEngine'i çağırır, sonucu
dbo.messages'a yazar ve istemciye döner.
WebhookDispatcher (varsa) ilgili event'i o oturumun aktif webhook'larına HMAC-SHA256 imzalı
POST ile dağıtır.
SignalR hub aynı event'i o oturuma abone olan tüm canlı istemcilere broadcast eder.
Entegrasyon Kılavuzu
Bir uygulamanın WApi ile uçtan uca konuşmaya başlaması üç adımdır: API anahtarı al, oturum
aç, mesaj gönder. Aşağıdaki örnekler tipik bir entegrasyonun her adımını yapay YOUR_API_KEY değişkeniyle
gösterir.
1 · API anahtarı oluşturun
İlk kurulumda appsettings.json'daki Security:MasterKey değeri ilk Admin
anahtarınızdır; sonraki anahtarları bununla yaratırsınız. Operasyonda günlük kullanım için Master yerine
rolü uygun (Operator/Viewer) iş-anahtarları kullanılması önerilir.
Her WhatsApp numarası ayrı bir oturum (session) olarak tutulur. node-bridge engine ile,
oturum başlatıldığında bir QR kodu üretilir; telefon WhatsApp uygulamasından
Ayarlar → Bağlı Cihazlar → Cihaz Bağla ile bu QR'i tarayarak eşleşir. Eşleşme sonrası auth
state diskte saklanır; servis yeniden başlasa bile rescan gerekmez.
# Oturum oluştur
curl -X POST https://wapi.erp.tr/api/sessions \
-H "X-API-Key: $YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "satis-departmani", "proxyUrl": null, "proxyType": null }'
# Yanıttaki id'yi sakla; bundan sonra her uçta o id geçecek.
SESSION_ID=...
# Oturumu başlat (engine'i ayağa kaldırır; QR üretir)
curl -X POST https://wapi.erp.tr/api/sessions/$SESSION_ID/start \
-H "X-API-Key: $YOUR_API_KEY"
# QR'i çek — base64 PNG olarak döner (img src'ye doğrudan verilebilir)
curl https://wapi.erp.tr/api/sessions/$SESSION_ID/qr \
-H "X-API-Key: $YOUR_API_KEY"
# { "sessionId": "...", "qrCode": "data:image/png;base64,iVBORw...", "status": 0 }
3 · Mesaj gönderin
Oturum connected duruma geçtikten sonra herhangi bir mesaj uç noktası çağrılabilir.
Aşağıda her dilde, gerçek üretim koduna yakın bir minimal entegrasyon vardır.
// .NET 8+ — HttpClient ile minimal istemci
using System.Net.Http.Headers;
using System.Net.Http.Json;
var http = new HttpClient { BaseAddress = new Uri("https://wapi.erp.tr") };
http.DefaultRequestHeaders.Add("X-API-Key", "YOUR_API_KEY");
// Mesaj gönder
var payload = new { chatId = "905321234567@s.whatsapp.net", text = "Merhaba!" };
var res = await http.PostAsJsonAsync($"/api/sessions/{sessionId}/messages/send-text", payload);
res.EnsureSuccessStatusCode();
var msg = await res.Content.ReadFromJsonAsync<MessageDto>();
Console.WriteLine($"WApi message id: {msg!.Id} WhatsApp id: {msg.WaMessageId}");
// Hata kontrolü
if (msg.Status == MessageStatus.Failed)
throw new Exception("Mesaj engine tarafından kabul edilmedi.");
public record MessageDto(Guid Id, Guid SessionId, string? WaMessageId,
string ChatId, string? Body, int Type, int Direction, int Status,
long Timestamp, DateTime CreatedAt);
public enum MessageStatus { Pending = 0, Sent = 1, Delivered = 2, Read = 3, Failed = 4 }
' .NET 8+ — VB.NET sürümü
Imports System.Net.Http
Imports System.Net.Http.Json
Imports System.Threading.Tasks
Module Program
Async Function MainAsync(sessionId As Guid) As Task
Dim http As New HttpClient()
http.BaseAddress = New Uri("https://wapi.erp.tr")
http.DefaultRequestHeaders.Add("X-API-Key", "YOUR_API_KEY")
Dim payload = New With {
.chatId = "905321234567@s.whatsapp.net",
.text = "Merhaba!"
}
Dim res = Await http.PostAsJsonAsync($"/api/sessions/{sessionId}/messages/send-text", payload)
res.EnsureSuccessStatusCode()
Dim msg = Await res.Content.ReadFromJsonAsync(Of MessageDto)()
Console.WriteLine($"WApi message id: {msg.Id} WhatsApp id: {msg.WaMessageId}")
End Function
Public Class MessageDto
Public Property Id As Guid
Public Property SessionId As Guid
Public Property WaMessageId As String
Public Property ChatId As String
Public Property Body As String
Public Property Type As Integer
Public Property Direction As Integer
Public Property Status As Integer
Public Property Timestamp As Long
Public Property CreatedAt As DateTime
End Class
End Module
// Java 17+ — built-in HttpClient
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class WApiClient {
private static final String BASE = "https://wapi.erp.tr";
private static final String KEY = System.getenv("WAPI_KEY"); // YOUR_API_KEY
public static void sendText(String sessionId, String chatId, String text) throws Exception {
HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
String body = String.format(
"{\"chatId\":\"%s\",\"text\":\"%s\"}",
chatId.replace("\"", "\\\""), text.replace("\"", "\\\""));
HttpResponse<String> r = http.send(
HttpRequest.newBuilder()
.uri(URI.create(BASE + "/api/sessions/" + sessionId + "/messages/send-text"))
.header("X-API-Key", KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build(),
HttpResponse.BodyHandlers.ofString());
if (r.statusCode() / 100 != 2) {
throw new RuntimeException("WApi rejected the send: " + r.statusCode() + " " + r.body());
}
System.out.println("Sent: " + r.body());
}
}
WApi tek bir kimlik doğrulama mekanizması kullanır: HTTP başlığındaki anahtar. Anahtar kayıt
ve doğrulamasının her detayı aşağıda açıklanmıştır.
Anahtar formatı ve gönderim
Her API anahtarı wapi_ öneki + 64 karakter hex (32 bayt rastgele) olarak üretilir. Tüm istekler
anahtarı X-API-Key başlığında taşır:
GET /api/sessions HTTP/1.1
Host: wapi.erp.tr
X-API-Key: wapi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json
Saklama
Veritabanında yalnızca SHA-256 hash + ilk 8 karakter (prefix, gösterim için) tutulur. Ham anahtar
asla saklanmaz. Anahtar oluşturulduğu anda POST /api/auth/api-keys'in yanıtında
bir kez döner; kaybedilirse yenisi yaratılmalı, eski iptal edilmelidir (POST /api/auth/api-keys/{id}/revoke).
Roller
Rol
Değer
Erişim
Viewer
0
Tüm okuma uçları: GET sessions, messages, groups, contacts, stats, audit (kendi yetkisindekiler).
Tüm uçlar /api altındadır ve (anonim olarak işaretlenenler dışında)
X-API-Key ister. Bütün şemalar ve örnek istek/yanıt'lar Swagger UI'da yer alır:
/api/docs.
Sağlık & Kimlik
Method
Yol
Rol
Açıklama
GET
/api/health
anonim
Sağlık + DB bağlantısı kontrolü
GET
/api/health/live
anonim
Liveness probe (proseste sağlıklı?)
GET
/api/health/ready
anonim
Readiness probe (DB ulaşılabilir?)
POST
/api/auth/validate
anonim
Verilen anahtarın geçerliliğini, adını, rolünü döner
Bir webhook eklediğinizde, WApi o oturum üzerinde gerçekleşen olayları sizin verdiğiniz
URL'e HTTP POST olarak iletir. Aşağıdaki olay tipleri varsayılan olarak yayımlanır.
// ASP.NET Core (örnek)
[HttpPost("wapi/incoming")]
public async Task<IActionResult> Incoming()
{
using var reader = new StreamReader(Request.Body);
var raw = await reader.ReadToEndAsync();
var sig = Request.Headers["X-WApi-Signature"].ToString();
if (!sig.StartsWith("sha256=")) return Unauthorized();
var expected = Convert.ToHexString(
new HMACSHA256(Encoding.UTF8.GetBytes("shared-hmac-secret"))
.ComputeHash(Encoding.UTF8.GetBytes(raw))
).ToLowerInvariant();
if (!CryptographicOperations.FixedTimeEquals(
Encoding.ASCII.GetBytes(sig[7..]),
Encoding.ASCII.GetBytes(expected)))
return Unauthorized();
var evt = JsonSerializer.Deserialize<WebhookEnvelope>(raw);
// ... işle
return Ok();
}
Yeniden deneme ve idempotency
2xx dışındaki yanıtlar başarısız sayılır. WApi retryCount (varsayılan 3) kadar üstel
geri çekilme (1s, 2s, 4s, 8s…) ile yeniden dener.
Aynı olayın birden fazla kez gelmesine karşı, X-WApi-Timestamp + waMessageId
ikilisi ile idempotent davranan endpoint'ler önerilir.
Webhook tarafındaki maksimum işleme süresi Webhook:TimeoutSeconds ile sınırlıdır
(varsayılan 10 saniye). Uzun süren işleri kuyruğa alın ve hemen 200 dönün.
Hata Yönetimi
WApi standart HTTP durum kodlarını kullanır ve hatalar için
RFC 9457 — Problem Details şeklinde
yapılandırılmış gövde döner.
Kod
Anlamı
Tipik neden
Davranış
200 / 201 / 204
Başarı
—
—
400
Geçersiz istek
Eksik veya tip hatası içeren gövde (örn. chatId boş)
Düzelt ve yeniden gönder; gövdedeki errors alanını incele.
401
Kimlik doğrulanmadı
Eksik/hatalı anahtar, IP allowlist'e dışarıdan istek, süresi geçmiş anahtar
Doğru anahtar başlığını ekle, IP'yi allowlist'e ekle.
403
Yetki yok
Anahtarın rolü ucu çağırmaya yetmiyor (örn. Viewer ile send-text)
Daha yüksek rolde anahtar kullan veya rolü güncelle.
404
Bulunamadı
Session/Message/Webhook id'si yok
Id'yi doğrula.
409
Çakışma
Oturum henüz connected değil; aynı isimde oturum var
Önce start + QR taraması; veya farklı isim seç.
429
Hız limiti
Aşırı gönderim (üstte uygulanan global throttling)
Retry-After başlığı kadar bekleyip yeniden dene.
500 / 502 / 503
Sunucu hatası
Engine bağlantı kopması, DB erişim hatası
Üstel geri çekilme ile yeniden dene; ısrarlıysa /api/health ve /api/infra'ya bak.
Önerilen istemci davranışı
İdempotency anahtarı: aynı mesajın iki kez gönderilmesini önlemek için istemci tarafında
bir tekil id tutun ve mesaj id'lerini DB'de saklayın.
Üstel geri çekilme: 429/5xx için 1s, 2s, 4s, 8s, 16s gibi artan aralıklarla en fazla
5 deneme. Toplu kampanyalarda intervalMs'i artırın.
Devre kesici: 5 dakikada %50'den fazla 5xx ise istemci tarafından gönderimi geçici durdurun;
/api/health 200 dönene kadar bekleyin.
WApi tipik bir Windows veya Linux sunucuda saatler içinde devreye alınır. Aşağıda
Windows + Caddy + SQL Server kurulumunun referans yapılandırması verilmiştir.
Üretim kurulumunda nodebridge/ klasörü ayrı bir Windows Service olarak (örn. WinSW ile) çalışır,
Baileys'i ayağa kaldırır ve 127.0.0.1:2787'de loopback dinler. WApi.Api ona
paylaşılan bir WAPI_BRIDGE_TOKEN bearer ile erişir. appsettings.Production.json:
Yan-süreç Baileys'in useMultiFileAuthState mekanizmasıyla her oturum için ayrı bir auth state
klasörü tutar. Servis yeniden başlatıldığında bu klasörden cred'ler yüklenir; rescan gerekmez.
4 · Docker ile tek-komut tüm-stack
SQL Server + API + Dashboard'u tek bir compose dosyasıyla ayağa kaldırmak için:
docker compose up --build
# API: http://localhost:2785/api/docs
# Dashboard: http://localhost:2886
Sık Sorulanlar
WApi resmi bir WhatsApp Business çözümü müdür?
Hayır. WApi, WhatsApp Multi-Device protokolüne (whatsapp-web.js / Baileys aileti) istemci olarak
bağlanan açık-kaynak bir ağ geçididir. Resmi WhatsApp Business API ile aynı yüzeyde değildir; Meta'nın
yayımladığı resmi REST API'siyle karıştırılmamalıdır. Resmi ToS gereği toplu/spam gönderim hesabın
engellenmesine yol açabilir; kullanım kalıbınızı kişisel iletişim profiline yakın tutun.
SaaS olarak mı yoksa kendi sunucumda mı çalıştırırım?
WApi self-hosted çalıştırılır. Tüm veriler (mesajlar, kişiler, anahtarlar, audit log) sizin SQL
Server'ınızda kalır. Üçüncü taraf bir bulut bağımlılığı yoktur. Bu sayede KVKK/GDPR kapsamındaki
kişisel veri akışını tamamen kontrol edebilirsiniz.
Birden fazla numarayı tek WApi kurulumunda yönetebilir miyim?
Evet. Her numara bir session'dır. Tek bir WApi.Api ve tek bir Node.js yan-süreci ile
sınırsız sayıda oturum tutulabilir. Her oturumun kendi Baileys auth state'i, mesaj geçmişi ve
webhook yapılandırması olur.
Mesajlar uçtan uca şifreli mi?
WhatsApp Multi-Device protokolü Signal Double Ratchet uçtan uca şifrelemeyi kullanır; bu uçtan
uca şifre WApi'nin görmediği bir protokol katmanında uygulanır. WApi'nin gördüğü ham mesaj metni
(sizin yazdığınız ya da Baileys'in açtığı) tabii ki SQL Server'a düz şekilde yazılır — ihtiyaç
halinde SQL Server TDE veya kolon-level encryption kullanın.
WhatsApp'ım banlanır mı?
WhatsApp Business olmayan istemcilere karşı koruyucu davranır. Aşırı/sıralı/identik mesaj
gönderimi otomatik tetikleyicileri çalıştırır. Düşük hacim ve kişiselleştirilmiş içerik genelde
sorun çıkarmaz; toplu kampanyalarda intervalMs'i 1500ms üzerinde tutmak, her alıcı için
en azından farklı bir kelime üretmek ve "opt-out" mekaniği uygulamak risk azaltır. Yine de tüm
risk kullanıcıdadır.
Yedekleme nasıl yapılır?
İki bileşen yedeklenir: SQL Server veritabanı (standart BACKUP DATABASE) ve Node
yan-sürecindeki auth/ klasörü (oturum cred'leri). Bu iki şey beraber yedeklenirse
sistem başka bir makineye birebir taşınabilir — rescan gerektirmez.
Üretim kullanımı için lisans gerekir mi?
Hayır. WApi MIT lisansı altındadır. Ticari ürün içine gömme, kapalı bir dağıtım yapma, fork
etme tamamen serbesttir. Atıf zorunluluğu yoktur.
Hazırsanız Swagger ile başlayın
Tüm uçları ve şemaları kendi anahtarınızla canlı deneyin.