Givoti SMS
Özellikler Fiyatlar Yardım Geliştiriciler İletişim
Giriş yap 24 saat ücretsiz dene
Geliştiriciler

Givoti SMS API dokümantasyonu

Kendi yazılımından WhatsApp mesajı ve doğrulama kodu gönder, rehberini yönet, olayları imzalı webhook ile al. Tüm paketlerde (denemede de) açıktır.

Temel bilgiler

Taban adres:

https://givotisms.com/v1
  • İstek ve yanıt gövdeleri JSON'dur (Content-Type: application/json, UTF-8). Medya yükleme multipart/form-data kullanır.
  • Telefon numaraları ülke koduyla, yalnızca rakam olarak döner: 905321234567. Gönderirken 0532 123 45 67, +90 532… gibi biçimler de kabul edilir; Türkiye dışı numaraları + ile yaz.
  • Tarihler ISO 8601 biçimindedir (2026-10-23T09:30:00.000Z). scheduled_at alanında ISO metni veya milisaniye cinsinden zaman damgası gönderebilirsin.
  • Gönderilen her mesaj önce kuyruğa alınır (status: "queued") ve hattın günlük limitine, bekleme süresine ve mesai saatlerine uyularak gönderilir. Sonucu webhook ile ya da GET /messages/:id ile takip et.

Kimlik doğrulama

Panelde API & Webhook sayfasından bir API anahtarı oluştur. Anahtar ulk_live_ ile başlar ve yalnızca bir kez gösterilir. Her istekte şu başlıklardan birini gönder:

Authorization: Bearer ulk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# ya da
X-API-Key: ulk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Anahtarlara yetki (kapsam) verilir; yetkisi olmayan uca yapılan istek 403 döner. İsteğe bağlı olarak anahtarı belirli IP adresleriyle ve bir bitiş tarihiyle sınırlayabilirsin.

Kapsamİzin verdiği
messages:sendMesaj, toplu mesaj, OTP gönderme; medya yükleme; sıradaki mesajı iptal
messages:readMesajları listeleme ve okuma
contacts:read / contacts:writeKişileri okuma / ekleme, güncelleme, silme
lines:readHatları listeleme
templates:readŞablonları listeleme
*Tüm yetkiler
API anahtarını tarayıcıda çalışan koda (JavaScript, mobil uygulama) gömme. İstekleri her zaman kendi sunucundan gönder.

Hız sınırları ve kotalar

  • API anahtarı başına dakikada 120 istek. Ayrıca IP başına dakikada 600 istek.
  • Aynı IP'den 15 dakikada 30 geçersiz anahtar denemesinden sonra istekler 15 dakika 429 alır.
  • Yanıtlarda X-RateLimit-Limit ve X-RateLimit-Remaining başlıkları bulunur.
  • Tek istekte en fazla 500 alıcı; toplu uçta en fazla 1.000 mesaj.
  • Gönderimler paketinin aylık mesaj hakkından düşer; hak yetmezse istek 402 döner ve hiçbir mesaj kuyruğa alınmaz.
  • Kuyruğa alınan mesajlar hattın günlük limiti ve ısınma kurallarına göre gönderilir; bu yüzden büyük listeler saatlere yayılabilir.

Hatalar

Hatalı isteklerde HTTP durum kodu ve şu gövde döner:

{
  "error": {
    "code": "validation_error",
    "message": "Alıcı listesi zorunludur",
    "details": { "to": "Alıcı listesi zorunludur" }
  }
}
HTTPcodeAnlamı
400bad_requestGövde geçerli JSON değil
401unauthorizedAnahtar yok, geçersiz, iptal edilmiş ya da süresi dolmuş
402quota_exceeded, trial_expiredAylık mesaj hakkı yetersiz / deneme süresi bitti
403forbiddenAnahtarın bu yetkisi yok, IP izinli değil ya da hesap askıda
404not_foundKayıt ya da yol bulunamadı
409conflictNumara rehberde zaten var; ya da mesaj artık iptal edilemez
413errorDosya çok büyük (en fazla 16 MB)
422validation_error, no_lineEksik / hatalı alan; kullanılabilir hat yok
429rate_limitedHız sınırı aşıldı; bir süre sonra tekrar dene
500errorSunucu hatası; isteği daha sonra tekrarla
POST

/messages

messages:send

Bir ya da birden fazla kişiye mesaj gönderir.

AlanTürAçıklama
to *metin / diziNumara ya da numara listesi (en fazla 500). Rehberdeki bir kişi için "c:42".
bodymetinMesaj metni (en fazla 4.096 karakter). Değişkenler ({ad}) ve metin seçenekleri ({Merhaba|Selam}; her mesajda biri rastgele seçilir) desteklenir. media yoksa zorunlu.
template_idsayıbody yerine kayıtlı şablonu kullanır.
variablesnesneEk değişkenler: {"siparis_no": "10482"}. Rehberdeki kişinin bilgileriyle birleşir.
medianesne{"url": "https://…/fatura.pdf", "type": "document", "caption": "…", "filename": "…"} ya da yüklenen dosya için {"file": "…"}. Türler: image, video, audio, document. En fazla 16 MB.
line_id / line_idssayı / diziGönderecek hat(lar). Boşsa varsayılan / uygun hatlar arasında dağıtılır.
scheduled_attarihİleri tarihli gönderim.
client_refmetinKendi referansın (ör. sipariş no, en fazla 100 karakter); listede filtrelenebilir ve webhook'ta döner.
curl -X POST https://givotisms.com/v1/messages \
  -H "Authorization: Bearer $ULAK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "905321234567",
    "body": "Merhaba {ad}, #{siparis_no} numaralı siparişiniz kargoya verildi.",
    "variables": { "ad": "Ayşe", "siparis_no": "10482" },
    "client_ref": "order-10482"
  }'

Yanıt 201 Created. Tek alıcıda data bir mesaj nesnesidir; birden fazla alıcıda data dizi olur ve count eklenir.

{
  "data": {
    "id": 1842, "line_id": 3, "to": "905321234567", "direction": "out",
    "type": "text", "body": "Merhaba Ayşe, #10482 numaralı siparişiniz kargoya verildi.",
    "media": null, "status": "queued", "error": null, "source": "api",
    "campaign_id": null, "client_ref": "order-10482", "wa_id": null,
    "scheduled_at": null, "sent_at": null, "delivered_at": null, "read_at": null,
    "created_at": "2026-10-23T09:30:00.000Z"
  }
}

Mesaj durumları: queued (sırada) → sending → sent (WhatsApp'a iletildi) → delivered (alıcının telefonuna ulaştı) → read (okundu). Hata durumunda failed ve error alanı; iptal edilirse cancelled. Gelen mesajlarda direction: "in" ve from alanı bulunur.

POST

/messages/bulk

messages:send

Farklı içerikli en fazla 1.000 mesajı tek istekte kuyruğa alır. Her öğe /messages gövdesiyle aynıdır. Toplam mesaj hakkı önceden kontrol edilir; yetmezse hiçbiri kuyruğa alınmaz (402).

{ "messages": [
    { "to": "905321234567", "body": "Siparişiniz hazır.", "client_ref": "A-1" },
    { "to": "905441112233", "template_id": 4, "variables": { "siparis_no": "A-2" } }
] }

Yanıt: en az biri kabul edildiyse 201, hiçbiri kabul edilmediyse 422. Gövde: {"data": [{"index": 0, "ok": true, "data": [ … ]}, {"index": 1, "ok": false, "error": {"code": "…", "message": "…"}}], "accepted": 1, "rejected": 1}.

GET

/messages

messages:read

Mesajları en yeniden eskiye listeler. Sorgu parametreleri: status (virgülle birden fazla: failed,cancelled), direction (in / out), phone, client_ref, since (ISO tarih), page, limit (varsayılan 50, en fazla 200). Yanıt: {"data": [ … ], "total": 120, "page": 1, "limit": 50}.

GET

/messages/:id

messages:read

Tek mesajı döner: {"data": { … }}.

DELETE

/messages/:id

messages:send

Henüz sırada (queued) bekleyen mesajı iptal eder. Gönderilmeye başlamış mesaj için 409 döner.

POST

/otp

messages:send

Doğrulama kodu gönderir. Kodu sen verebilir ya da Givoti SMS'e ürettirebilirsin; üretilen kod yanıtta döner, doğrulamayı kendi sisteminde yaparsın.

AlanAçıklama
to *Telefon numarası
codeKendi kodun (3–12 harf/rakam). Boşsa rastgele rakamlardan üretilir.
lengthÜretilecek kodun uzunluğu, 4–8 (varsayılan 6)
templateMesaj metni; {kod} içermeli. Varsayılan: "Doğrulama kodunuz: *{kod}*" ve yeni satırda "Bu kodu kimseyle paylaşmayın."
line_id, client_refİsteğe bağlı
curl -X POST https://givotisms.com/v1/otp -H "Authorization: Bearer $ULAK_API_KEY" \
  -H "Content-Type: application/json" -d '{"to": "905321234567", "length": 6}'
# 201 → { "data": { "id": 1843, "status": "queued", … }, "code": "482913" }
OTP mesajları da kuyruğa ve hattın bekleme süresine tabidir; yoğun kampanya sırasında gecikebilir. Kritik girişler için kodun geçerlilik süresini birkaç dakika tut.
POST

/media

messages:send

Dosyayı önceden yükler (multipart/form-data, alan adı file, en fazla 16 MB). Yanıttaki file değerini mesajda "media": {"file": "…"} olarak kullan.

curl -X POST https://givotisms.com/v1/media -H "Authorization: Bearer $ULAK_API_KEY" -F "file=@katalog.pdf"
# 201 → { "data": { "type": "document", "file": "1729675800000-a1b2c3.pdf", "url": "…", "mimetype": "application/pdf", "filename": "katalog.pdf", "size": 182044 } }
GET

/contacts

contacts:read

Parametreler: q (ad, soyad, telefon, e-posta içinde arar), phone, tag, page, limit.

GET

/contacts/:id

contacts:read

:id yerine telefon numarası da yazılabilir (/contacts/905321234567).

POST

/contacts

contacts:write

Kişi ekler. Numara zaten varsa 409 döner; güncellemek için ?upsert=true ekle.

{
  "phone": "05321234567", "name": "Ayşe", "surname": "Yılmaz",
  "email": "ayse@ornek.com", "company": "Yılmaz Tekstil", "birthday": "1990-05-17",
  "tags": ["vip"], "custom": { "sehir": "İzmir" },
  "consent": true, "groups": [2]
}

consent: true, kişinin ticari ileti onayı verdiğini kaydeder (kaynak "API" ve o anki tarih). Bu alanı yalnızca gerçekten onay aldıysan gönder; bkz. İzin ve İYS. Yanıttaki kişi nesnesi opted_out (STOP yazdı mı) ve has_whatsapp alanlarını da içerir.

PATCH

/contacts/:id

contacts:write

Gönderilen alanları günceller; custom mevcut alanlarla birleştirilir.

DELETE

/contacts/:id

contacts:write

Kişiyi siler.

GET

/lines

lines:read

Hatları döner: id, name, status (connected, disconnected, pending…), phone, is_default, daily_limit.

GET

/templates

templates:read

Şablonları döner: id, name, category, body, media.

Webhook olayları

Panelde API & Webhook → Webhook'lar → Ekle ile bir HTTPS adresi ve dinlenecek olayları seç (boş bırakırsan tüm olaylar gelir). Givoti SMS her olayda bu adrese POST gönderir:

POST /senin-adresin
Content-Type: application/json
User-Agent: Ulak-Webhook/1.0
X-Ulak-Event: message.delivered
X-Ulak-Delivery: 5531
X-Ulak-Signature: sha256=3f6c…e9a1

{
  "event": "message.delivered",
  "created_at": "2026-10-23T09:31:12.000Z",
  "data": { "id": 1842, "to": "905321234567", "status": "delivered", "client_ref": "order-10482", … }
}
OlayNe zamandata
message.queuedMesaj kuyruğa alındıMesaj nesnesi
message.sentWhatsApp'a iletildiMesaj nesnesi
message.deliveredAlıcının telefonuna ulaştıMesaj nesnesi
message.readAlıcı okudu (okundu bilgisi açıksa)Mesaj nesnesi
message.failedGönderilemediMesaj nesnesi (error dolu)
message.receivedSana mesaj geldiMesaj nesnesi + contact (id, name, surname)
line.connected / line.disconnectedHat bağlandı / koptuline_id, phone / reason
line.qrAyrılmış; şu an gönderilmiyor—
campaign.started / campaign.completedKampanya başladı / bitticampaign_id, name, sayılar
contact.opted_out / contact.opted_inKişi STOP yazdı ya da yeniden katıldıcontact_id, phone, varsa keyword / source

Yanıt ve yeniden deneme: 10 saniye içinde 2xx döndürmelisin. Aksi hâlde Givoti SMS 10 sn, 1 dk, 5 dk, 30 dk ve 2 saat sonra tekrar dener (toplam 6 deneme), sonra bu gönderimi "başarısız" olarak işaretler. Aynı olay birden fazla gelebilir; X-Ulak-Delivery numarasıyla tekrarları ayıkla. Ağır işleri yanıtı döndükten sonra yap.

İmza doğrulama

X-Ulak-Signature, istek gövdesinin (ham bayt hâlinin) webhook imza anahtarınla (secret) hesaplanmış HMAC-SHA256 özetidir, onaltılık (hex) biçimde ve sha256= önekiyle. Gövdeyi JSON olarak ayrıştırmadan önce ham metin üzerinden doğrula.

import crypto from "node:crypto";
import express from "express";

const app = express();
app.post("/ulak-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto.createHmac("sha256", process.env.ULAK_WEBHOOK_SECRET).update(req.body).digest("hex");
  const got = String(req.get("x-ulak-signature") || "");
  const ok = got.length === expected.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
  if (!ok) return res.sendStatus(401);
  const { event, data } = JSON.parse(req.body.toString("utf8"));
  res.sendStatus(200);              // önce yanıt ver
  if (event === "contact.opted_out") { /* kendi listenden çıkar */ }
});

Mağazadan Givoti SMS'e gelen webhook

Mağazan ya da yazılımın sipariş olaylarını Givoti SMS'e gönderip hazır şablonlarla bildirim yollatabilir. Bu uç API anahtarı değil, entegrasyona özel gizli bir adres (/hooks/…) kullanır. Gövde biçimi, olay adları ve imza (X-Ulak-Signature, HMAC-SHA256, base64) için Diğer platformlar kılavuzuna bak. WooCommerce ve Shopify kendi imza başlıklarıyla çalışır.

Bir uç nokta eksik mi, örnek mi lazım? Bize yaz.