API Dokümantasyonu

SMS ve WhatsApp Business mesajlaşma için tam REST API referansı. Her endpoint'in yanındaki "AI'ya Kopyala" butonu, endpoint metadata'sını yapay zeka asistanlarına yapıştırılınca eksiksiz çalışacak yapılandırılmış prompt'a çevirir.

Base URL
https://sms.gambi.dev
Auth
X-Api-Key
Format
application/json

Hızlı Başlangıç

  1. 1. Hesap oluştur (30 saniye, e-posta + parola).
  2. 2. Panelde /app/api-keys sayfasından bir API anahtarı oluştur. Anahtar formatı: gsms_xxxxxxxxxxxxxxxx (sadece 1 kez gösterilir, kaydet).
  3. 3. Test isteğini at:
İlk istek
curl https://sms.gambi.dev/api/v1/me \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"

Kimlik Doğrulama

Tüm authenticated istekler X-Api-Key header'ı ile yapılır. Anahtarlar sunucuda SHA-256 hash olarak saklanır; plaintext sadece üretim anında bir kez gösterilir. Anahtar sızdığında /app/api-keys sayfasından anında iptal edebilirsin. Per-key audit log (son 30 gün) görüntülenebilir.

Rate Limit

Varsayılan limit her API anahtarı için 30 istek/sn. Premium müşterilerde 100/sn'e yükseltilebilir. Her yanıt aşağıdaki header'ları içerir:

Response headers
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
X-RateLimit-Reset: 1700000123

Limit aşıldığında 429 RATE_LIMIT döner. Retry-After saniye olarak yanıt header'ında gelir. Client tarafında exponential backoff önerilir.

Yetkilendirme

GET/api/v1/meAuth Gerekir

Hesap bilgisi

API anahtarınızın geçerliliğini test etmek ve hesap kimliği + rolü doğrulamak için kullanılır. Bu endpoint çağrısı kredi yakmaz.

Hatalar
HTTPCodeAçıklama
401NO_KEYX-Api-Key header eksik
401INVALID_KEYAPI anahtarı geçersiz
shell
curl https://sms.gambi.dev/api/v1/me \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "ok": true,
  "userId": 42,
  "email": "ops@bahis123.com",
  "role": "USER",
  "balanceCredits": 1250.5,
  "balanceWaCredits": 800
}

SMS Endpoint'leri

POST/api/v1/sms/sendAuth Gerekir

Toplu SMS gönderimi

Bir veya birden fazla alıcıya SMS gönderir. Kampanya olarak kayıt edilir, DLR webhook'larıyla durumu takip edilir. Segment hesaplaması otomatik (GSM-7 için 160 char, Unicode için 70 char). Kredi düşümü gönderim öncesi atılır, başarısız mesajlar için iade yapılır. NOT: Mesajdaki linkler teslimat için otomatik normalize edilir, http(s):// ve www. kaldırılır (ör. https://www.site.com → site.com). Linki noktasıyla yazmanız yeterli; operatör filtresi ve ulaşılabilirlik için bu işlem otomatik yapılır.

Request Body (application/json)
AdTipAçıklama
to*string[] | stringE.164 formatında alıcı(lar): '+905551234567'. Tek string veya array
text*stringMesaj gövdesi. Maks 5 segment (Unicode için 5×70=350 char)
senderstringOnaylı gönderici adı. Boş bırakılırsa varsayılan kullanılır
campaignNamestringRaporlarda görünecek kampanya adı (varsayılan: 'API send')
scheduledAtstring (ISO8601)İleri tarihli gönderim. Boş = anında
Hatalar
HTTPCodeAçıklama
401NO_KEYX-Api-Key header eksik
401INVALID_KEYAPI anahtarı geçersiz
402INSUFFICIENT_CREDITYetersiz bakiye
403SENDER_NOT_APPROVEDSender ID onaysız
422VALIDATIONBody doğrulanamadı (zod)
422TOO_LONGMesaj 5 segmenti aşıyor
422NO_RECIPIENTSGeçerli E.164 alıcı yok
413TOO_MANYAlıcı limiti aşıldı (100k)
429RATE_LIMIT30 istek/sn limit
shell
curl -X POST https://sms.gambi.dev/api/v1/sms/send \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["+905551234567", "+905559876543"],
    "text": "Bonusunuz hazır: 500 TL",
    "sender": "BAHIS123"
  }'
Request body örneği
json
{
  "to": [
    "+905551234567",
    "+905559876543"
  ],
  "text": "Merhaba {{1}}, bonusunuz hazır: {{2}} TL",
  "sender": "BAHIS123",
  "campaignName": "Hafta sonu bonus"
}
Yanıt örneği (başarı)
json
{
  "ok": true,
  "campaignId": 4521,
  "accepted": 2,
  "rejected": 0,
  "creditsReserved": "2.00",
  "balanceAfter": "1248.50"
}
GET/api/v1/sms/balanceAuth Gerekir

SMS bakiyesi

SMS kredi bakiyenizi (kredi cinsinden + TL karşılığı) döner. 1 kredi = 1 SMS segmenti. Tier'ınıza göre per-segment fiyat değişir.

Hatalar
HTTPCodeAçıklama
401NO_KEYX-Api-Key header eksik
401INVALID_KEYAPI anahtarı geçersiz
shell
curl https://sms.gambi.dev/api/v1/sms/balance \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "ok": true,
  "balanceCredits": 1248.5,
  "balanceTL": 374.55,
  "pricePerSegmentTL": 0.3,
  "tier": "Pro"
}
GET/api/v1/campaignsAuth Gerekir

Kampanya listesi

Son 30 günlük SMS kampanyalarınızı sayfalı olarak döner. ?limit=N (max 100) ve ?cursor=N pagination.

Query Parametreleri
AdTipAçıklama
limitnumber1-100, varsayılan 20
cursornumberÖnceki yanıtın nextCursor'u
statusstringDRAFT|QUEUED|SENDING|COMPLETED|FAILED
Hatalar
HTTPCodeAçıklama
401INVALID_KEYAPI anahtarı geçersiz
429RATE_LIMITHız limiti
shell
curl "https://sms.gambi.dev/api/v1/campaigns?limit=20" \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "ok": true,
  "items": [
    {
      "id": 4521,
      "name": "Hafta sonu bonus",
      "status": "COMPLETED",
      "totalRecipients": 2,
      "sentCount": 2,
      "deliveredCount": 2,
      "failedCount": 0,
      "createdAt": "2026-05-19T10:30:00Z"
    }
  ],
  "nextCursor": 4520
}
GET/api/v1/campaigns/:idAuth Gerekir

Kampanya detayı

Kampanyanın anlık durum ve sayaçları. Canlı kampanyalarda 5sn aralıklarla poll'lanabilir.

Path Parametreleri
AdTipAçıklama
id*numberKampanya ID
Hatalar
HTTPCodeAçıklama
401INVALID_KEYAPI anahtarı geçersiz
404NOT_FOUNDKampanya bulunamadı (veya başka müşteriye ait)
shell
curl https://sms.gambi.dev/api/v1/campaigns/4521 \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "ok": true,
  "campaign": {
    "id": 4521,
    "name": "Hafta sonu bonus",
    "status": "SENDING",
    "totalRecipients": 1000,
    "sentCount": 750,
    "deliveredCount": 620,
    "failedCount": 30,
    "pendingCount": 250,
    "creditsReserved": 1000,
    "creditsSpent": 750,
    "createdAt": "2026-05-19T10:30:00Z"
  }
}
GET/api/v1/contactsAuth Gerekir

Kontak listesi

Numara rehberindeki kontakları sayfalı döner.

Query Parametreleri
AdTipAçıklama
limitnumber1-100, varsayılan 50
cursorstringÖnceki yanıtın nextCursor'u (BigInt)
searchstringTelefon/ad arama
shell
curl "https://sms.gambi.dev/api/v1/contacts?limit=50" \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "ok": true,
  "items": [
    {
      "id": "1234567890",
      "phone": "+905551234567",
      "firstName": "Ahmet",
      "lastName": "Yılmaz",
      "country": "TR",
      "optedOut": false,
      "createdAt": "2026-04-01T08:00:00Z"
    }
  ],
  "nextCursor": "1234567889"
}
POST/api/v1/contactsAuth Gerekir

Kontak oluştur

Yeni kontak ekler. Telefon E.164 formatında, duplicate kontroli sunucuda yapılır.

Request Body (application/json)
AdTipAçıklama
phone*stringE.164 (+905551234567)
firstNamestringAd
lastNamestringSoyad
emailstringE-posta
tagsstring[]Etiketler (örn ["vip","futbol"])
customFieldsobjectCustom JSON alanlar
Hatalar
HTTPCodeAçıklama
409DUPLICATEBu telefonla kontak zaten var
422VALIDATIONPhone E.164 değil
shell
curl -X POST https://sms.gambi.dev/api/v1/contacts \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+905551234567",
    "firstName": "Ahmet",
    "tags": ["vip"]
  }'
Request body örneği
json
{
  "phone": "+905551234567",
  "firstName": "Ahmet",
  "lastName": "Yılmaz",
  "tags": [
    "vip",
    "yatirim_5000_plus"
  ],
  "customFields": {
    "signupSource": "site_form"
  }
}
Yanıt örneği (başarı)
json
{
  "ok": true,
  "contact": {
    "id": "1234567890",
    "phone": "+905551234567",
    "createdAt": "2026-05-19T11:00:00Z"
  }
}
GET/api/v1/contacts/:idAuth Gerekir

Kişi detayı

Tek bir kişiyi ID ile döndürür. Yalnızca kendi hesabınıza ait kişilere erişebilirsiniz. Kredi yakmaz.

Path Parametreleri
AdTipAçıklama
id*string (bigint)Kişi kimliği (liste/oluşturma yanıtındaki id)
Hatalar
HTTPCodeAçıklama
404NOT_FOUNDKişi bulunamadı veya size ait değil
shell
curl https://sms.gambi.dev/api/v1/contacts/1234567890 \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "id": "1234567890",
  "phone": "+905551234567",
  "firstName": "Ahmet",
  "lastName": null,
  "email": null,
  "country": "TR",
  "tags": [
    "vip"
  ],
  "customFields": null,
  "optedOut": false,
  "createdAt": "2026-05-20T10:00:00.000Z"
}
PATCH/api/v1/contacts/:idAuth Gerekir

Kişi güncelle

Bir kişinin alanlarını kısmi günceller, yalnızca gönderdiğiniz alanlar değişir. PUT da aynı şekilde çalışır. phone gönderirseniz hesabın varsayılan ülkesine göre E.164'e normalize edilir. tags gönderildiğinde mevcut etiketlerin tamamını değiştirir.

Path Parametreleri
AdTipAçıklama
id*string (bigint)Kişi kimliği
Request Body (application/json)
AdTipAçıklama
phonestringYeni numara, E.164'e normalize edilir
firstNamestring | nullAd
lastNamestring | nullSoyad
emailstring | nullGeçerli e-posta adresi
tagsstring[]Etiket listesi (tümünü değiştirir)
customFieldsobjectSerbest anahtar/değer alanları
optedOutbooleanÇıkış (opt-out) durumu
Hatalar
HTTPCodeAçıklama
404NOT_FOUNDKişi bulunamadı
422VALIDATIONGeçersiz gövde
422INVALID_PHONETelefon normalize edilemedi
shell
curl -X PATCH https://sms.gambi.dev/api/v1/contacts/1234567890 \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Mehmet","tags":["vip","2026"]}'
Request body örneği
json
{
  "firstName": "Mehmet",
  "tags": [
    "vip",
    "2026"
  ]
}
Yanıt örneği (başarı)
json
{
  "id": "1234567890",
  "phone": "+905551234567",
  "firstName": "Mehmet",
  "lastName": null,
  "email": null,
  "country": "TR",
  "tags": [
    "vip",
    "2026"
  ],
  "customFields": null,
  "optedOut": false,
  "createdAt": "2026-05-20T10:00:00.000Z"
}
DELETE/api/v1/contacts/:idAuth Gerekir

Kişi sil

Bir kişiyi kalıcı olarak siler. Başarılı silmede gövde dönmez (HTTP 204 No Content). Geri alınamaz.

Path Parametreleri
AdTipAçıklama
id*string (bigint)Silinecek kişi kimliği
Hatalar
HTTPCodeAçıklama
404NOT_FOUNDKişi bulunamadı veya size ait değil
shell
curl -X DELETE https://sms.gambi.dev/api/v1/contacts/1234567890 \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
204 No Content, yanıt gövdesi yoktur.
GET/api/v1/sms/:messageIdAuth Gerekir

Mesaj durumu sorgula

Tek bir mesajın güncel durumunu döndürür. messageId, gönderim yanıtındaki provider mesaj kimliğidir (results[].messageId). Kredi yakmaz.

Path Parametreleri
AdTipAçıklama
messageId*stringGönderim yanıtındaki provider mesaj kimliği
Hatalar
HTTPCodeAçıklama
400BAD_IDmessageId eksik
404NOT_FOUNDMesaj bulunamadı
shell
curl https://sms.gambi.dev/api/v1/sms/2003b23d-4765-44f7-bdf7-bb14bbb0e36c \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "messageId": "2003b23d-4765-44f7-bdf7-bb14bbb0e36c",
  "campaignId": 4521,
  "phone": "+905551234567",
  "status": "DELIVERED",
  "errorCode": null,
  "segments": 1,
  "sentAt": "2026-05-20T10:00:00.000Z",
  "deliveredAt": "2026-05-20T10:00:05.000Z"
}
POST/api/v1/dlrAuth Gerekir

DLR test (echo)

Entegrasyon test ucu: gönderdiğiniz JSON gövdesini (POST) ya da query parametrelerini (GET) aynen 'received' içinde geri döndürür. Gerçek teslim raporları bu uçtan GELMEZ, onlar sizin webhook URL'inize POST edilir (bkz. Webhooks › DLR webhook). Bu uç yalnızca kendi tarafınızı denemek/log akışını görmek içindir; GET ve POST kabul eder, kredi yakmaz.

Request Body (application/json)
AdTipAçıklama
(serbest)objectHerhangi bir JSON, değişmeden 'received' alanında döner
Hatalar
HTTPCodeAçıklama
401INVALID_KEYAPI anahtarı geçersiz
405METHODGET/POST dışında bir method kullanıldı
shell
curl -X POST https://sms.gambi.dev/api/v1/dlr \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"messageId":"abc-123","status":"DELIVERED"}'
Request body örneği
json
{
  "messageId": "abc-123",
  "status": "DELIVERED"
}
Yanıt örneği (başarı)
json
{
  "ok": true,
  "received": {
    "messageId": "abc-123",
    "status": "DELIVERED"
  }
}

OTP Endpoint'leri

POST/api/v1/otp/sendAuth Gerekir

OTP gönder

Bir telefon numarasına tek kullanımlık doğrulama kodu (OTP) gönderir. Kod sunucuda üretilir ve güvenli saklanır; siz yalnızca bir requestId alırsınız (kod yanıtta DÖNMEZ). Onaylı bir sender ID zorunludur. Şablon {code} placeholder'ı içermelidir; mesaj 5 segmenti aşamaz. Kredi gönderim anında düşülür.

Request Body (application/json)
AdTipAçıklama
phone*stringAlıcı numara (E.164 veya hesabın varsayılan ülkesine göre normalize edilir)
sender*stringOnaylı gönderici adı (maks 15 karakter)
templatestringMesaj şablonu; {code} yerine kod konur. Varsayılan: 'GambiSMS dogrulama kodunuz: {code}'
lengthnumberKod uzunluğu, 4-10
ttlnumberGeçerlilik süresi (saniye), 60-1800
maxAttemptsnumberİzin verilen doğrulama denemesi, 1-10
Hatalar
HTTPCodeAçıklama
400INVALID_PHONEGeçersiz telefon numarası
400INVALID_TEMPLATEŞablon {code} placeholder içermiyor
402INSUFFICIENT_CREDITSYetersiz kredi
403SENDER_NOT_APPROVEDSender ID onaylı değil
422TOO_LONGOTP mesajı 5 segmenti aşıyor
502SEND_FAILEDSMS gönderimi başarısız
shell
curl -X POST https://sms.gambi.dev/api/v1/otp/send \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+905551234567","sender":"GAMBI","template":"Kodunuz: {code}"}'
Request body örneği
json
{
  "phone": "+905551234567",
  "sender": "GAMBI",
  "template": "Kodunuz: {code}",
  "length": 6,
  "ttl": 300
}
Yanıt örneği (başarı)
json
{
  "requestId": "otp_a1b2c3d4",
  "phone": "+905551234567",
  "expiresAt": "2026-05-20T10:05:00.000Z",
  "status": "PENDING",
  "costCredits": "1"
}
POST/api/v1/otp/verifyAuth Gerekir

OTP doğrula

Kullanıcının girdiği kodu, otp/send'den dönen requestId ile doğrular. verified=true ise kod doğru. Yanlış kodda triesRemaining kalan deneme sayısını verir; denemeler bitince veya süre dolunca status REJECTED/EXPIRED olur.

Request Body (application/json)
AdTipAçıklama
requestId*stringotp/send yanıtındaki requestId
code*stringKullanıcının girdiği kod
Hatalar
HTTPCodeAçıklama
404NOT_FOUNDOTP isteği bulunamadı
422VALIDATIONrequestId/code eksik veya geçersiz
shell
curl -X POST https://sms.gambi.dev/api/v1/otp/verify \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"requestId":"otp_a1b2c3d4","code":"123456"}'
Request body örneği
json
{
  "requestId": "otp_a1b2c3d4",
  "code": "123456"
}
Yanıt örneği (başarı)
json
{
  "verified": true,
  "status": "APPROVED",
  "triesRemaining": 0
}
GET/api/v1/otp/statusAuth Gerekir

OTP durumu

Bir OTP isteğinin güncel durumunu döndürür. Süresi geçmiş PENDING istekler bu çağrıda EXPIRED'a çevrilir. Kredi yakmaz.

Query Parametreleri
AdTipAçıklama
requestId*stringotp/send yanıtındaki requestId
Hatalar
HTTPCodeAçıklama
404NOT_FOUNDOTP isteği bulunamadı
422VALIDATIONrequestId gereklidir
shell
curl "https://sms.gambi.dev/api/v1/otp/status?requestId=otp_a1b2c3d4" \
  -H "X-Api-Key: gsms_xxxxxxxxxxxxxxxx"
Yanıt örneği (başarı)
json
{
  "requestId": "otp_a1b2c3d4",
  "status": "PENDING",
  "phone": "+905551234567",
  "expiresAt": "2026-05-20T10:05:00.000Z",
  "verifyAttempts": 0,
  "maxAttempts": 3,
  "createdAt": "2026-05-20T10:00:00.000Z"
}

Webhook Formatı

POST[Senin webhook URL'in]

DLR webhook (SMS + WhatsApp)

Mesaj durumu değiştikçe (DELIVERED/FAILED/READ vs.) sistem senin kayıtlı webhook URL'ine POST yapar. İmza: x-gambisms-signature header'ında HMAC-SHA256(body, USER_WEBHOOK_SECRET). İlk başarısızda 5 deneme exponential backoff.

shell
# Webhook URL'ini hesap ayarlarından kaydet:
# /app/settings → Webhook URL → Kaydet

# İmza doğrulama (Node.js):
import crypto from "node:crypto";
const sig = req.headers["x-gambisms-signature"];
const expected = crypto
  .createHmac("sha256", USER_WEBHOOK_SECRET)
  .update(req.rawBody)
  .digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
  return res.status(401).send("Invalid signature");
}
// process req.body...
res.status(200).send("ok");
Request body örneği
json
{
  "event": "status.delivered",
  "channel": "whatsapp",
  "campaignId": 101,
  "phone": "+905551234567",
  "providerMessageId": "wamid.HBgM...",
  "status": "DELIVERED",
  "timestamp": "2026-05-19T12:05:00Z",
  "windowType": "MARKETING"
}
Yanıt örneği (başarı)
json
// Beklenen yanıt: 2xx (HTTP 200-299). Body göz ardı edilir.
// 4xx/5xx alırsa exponential backoff retry yapılır.
POST[Senin webhook URL'in]

WhatsApp inbound message webhook

Müşterilerinizden gelen tüm WhatsApp mesajları (text, image, button reply, vb.) webhook URL'ine POST edilir. İmza aynı DLR mantığı. Ek alanlar: messageDirection, contentType, mediaUrl, templateName (eğer önceki template'e yanıtsa).

shell
# Aynı imza doğrulama akışı, yukarıda DLR örneğine bak.
Request body örneği
json
{
  "event": "message.inbound",
  "channel": "whatsapp",
  "numberId": 7,
  "phone": "+905551234567",
  "providerMessageId": "wamid.HBgM...",
  "contentType": "TEXT",
  "text": "Bonusumu nasıl alırım?",
  "timestamp": "2026-05-19T12:01:00Z",
  "windowOpened": true
}
Yanıt örneği (başarı)
json
// Beklenen yanıt: 2xx. Aynı imza doğrulama mantığı.
// İçerik: text mesajda "text"; medyalı mesajda "mediaUrl" alanı dolu gelir.

Tüm Hata Kodları

Hata yanıtları daima { ok: false, code: "...", message: "..." } formatındadır. HTTP status koduyla birlikte bu kodu kontrol et.

HTTPCodeAçıklama
401NO_KEYX-Api-Key header eksik
401INVALID_KEYAPI anahtarı geçersiz veya iptal edilmiş
402INSUFFICIENT_CREDITYetersiz bakiye
403SENDER_NOT_APPROVEDSender ID admin tarafından onaylanmamış
404NOT_FOUNDKaynak bulunamadı veya başka müşteriye ait
409DUPLICATEAynı kayıt zaten var
413TOO_MANYAlıcı limiti aşıldı
422VALIDATIONBody veya query doğrulanamadı (zod)
422TOO_LONGMesaj 5 segmenti aşıyor
422NO_RECIPIENTSGeçerli E.164 alıcı yok
422TEMPLATE_NOT_APPROVEDWhatsApp şablonu APPROVED değil
422CHANNEL_NOT_ACTIVEWhatsApp numarası aktif değil
422CSW_WINDOW_CLOSED24h CSW kapalı, template gerek
422BRAND_TERMS_NOT_ACCEPTEDMARKETING için BrandKit terms gerekli
422FREQ_CAP24h içinde aynı alıcıya tekrar gönderim engellendi
422QUALITY_LOWNumara LOW quality, MARKETING bloklandı
422OPTED_OUTAlıcı STOP/DUR ile opt-out etmiş
429RATE_LIMITHız limiti (30 req/sn varsayılan)
500INTERNALBeklenmeyen sunucu hatası