Müşteri iletişiminizi kendi sistemlerinize entegre edin
Mesajilet Developer API, kurumunuzun ERP, CRM, e-ticaret ve çağrı merkezi yazılımlarının WhatsApp Business, Instagram, Messenger ve web sohbeti kanallarında müşterilerle doğrudan iletişim kurmasını sağlar. Tek bir REST yüzeyi, kanaldan bağımsız veri modeli ve imzalı olay bildirimleriyle entegrasyon süresi saatlerle ölçülür.
- REST / JSONUTF-8, ISO-8601 zaman
- Bearer anahtarKapsam ve IP kısıtı
- HMAC-SHA256İmzalı webhook olayları
- Meta Resmi APIWhatsApp Cloud API altyapısı
curl https://all.mesajilet.com/api/v1/messages/template \
-H "Authorization: Bearer msj_live_…" \
-H "Idempotency-Key: order-18421" \
-d '{ "channel": "whatsapp", "to": "905321234567",
"name": "siparis_onay",
"variables": ["Ayşe", "18421", "3 Eylül"] }'
202 Accepted
{ "success": true,
"data": { "id": "msg_1724412345678", "status": "queued" },
"request_id": "req_01j9x…" }
Platform özellikleri
channel alanı ile hedef kanal seçilir. Yeni kanallar mevcut entegrasyonunuzu etkilemez.request_id ile günlüklenir; teslimat durumları, webhook denemeleri ve hata kodları panelde raporlanır.Idempotency-Key ile çift gönderim engellenir; webhook olayları üstel geri çekilmeyle beş kez denenir, hiçbir olay sessizce kaybolmaz.Tipik entegrasyon senaryoları
Hızlı başlangıç
- Panel › Ayarlar › Geliştirici (API) sayfasından API Anahtarı Üret. Anahtar bir kez gösterilir (
msj_live_…). - Bağlantıyı doğrulayın:
curl https://all.mesajilet.com/api/v1/me \
-H "Authorization: Bearer msj_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
- İlk mesajınızı gönderin (müşteri size son 24 saatte yazdıysa serbest metin; yazmadıysa şablon):
curl -X POST https://all.mesajilet.com/api/v1/messages/template \
-H "Authorization: Bearer msj_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-18421-confirm" \
-d '{
"channel": "whatsapp",
"to": "905321234567",
"name": "siparis_onay",
"variables": ["Ayşe", "18421", "3 Eylül"]
}'$res = Http::withToken('msj_live_…')
->withHeaders(['Idempotency-Key' => 'order-18421-confirm'])
->post('https://all.mesajilet.com/api/v1/messages/template', [
'channel' => 'whatsapp',
'to' => '905321234567',
'name' => 'siparis_onay',
'variables' => ['Ayşe', '18421', '3 Eylül'],
]);
if (!$res->json('success')) {
$e = $res->json('error');
// $e['code'], $e['message'], $e['reason'], $e['fix'], $e['request_id']
}
$messageId = $res->json('data.id'); // msg_…const res = await fetch('https://all.mesajilet.com/api/v1/messages/template', {
method: 'POST',
headers: {
'Authorization': 'Bearer msj_live_…',
'Content-Type': 'application/json',
'Idempotency-Key': 'order-18421-confirm',
},
body: JSON.stringify({
channel: 'whatsapp', to: '905321234567',
name: 'siparis_onay',
variables: ['Ayşe', '18421', '3 Eylül'],
}),
});
const body = await res.json();
if (!body.success) throw new Error(`${body.error.code}: ${body.error.message} — ${body.error.fix}`);
console.log(body.data.id); // msg_…import requests
r = requests.post("https://all.mesajilet.com/api/v1/messages/template",
headers={"Authorization": "Bearer msj_live_…", "Idempotency-Key": "order-18421-confirm"},
json={"channel": "whatsapp", "to": "905321234567", "name": "siparis_onay",
"variables": ["Ayşe", "18421", "3 Eylül"]})
body = r.json()
if not body["success"]:
raise RuntimeError(f'{body["error"]["code"]}: {body["error"]["message"]} — {body["error"]["fix"]}')
print(body["data"]["id"])Yanıt 202 Accepted — mesaj kuyruğa alındı; teslim ilerlemesini webhook ile ya da GET /messages/{id} ile izleyin.
Kimlik doğrulama
Her istekte Authorization: Bearer <anahtar> başlığı. Anahtarlar firma bazlıdır; isteğe bağlı IP kısıtı ve kapsam (scope) ile daraltılabilir. Anahtar sunucuda yalnız hash olarak saklanır — kaybolursa yenisi üretilir.
Kapsamlar
| Kapsam | İzin |
|---|---|
messages:send | Mesaj gönderme (serbest + şablon) |
messages:read | Mesaj durumu sorgulama |
templates:read | Şablon listesi |
media:write | Medya yükleme |
contacts:read | Kişi / 24 saat penceresi sorgusu |
webhooks:test | Webhook test olayı gönderme |
conversations:read | Sohbet listesi ve mesaj geçmişi |
conversations:write | Sohbet kapatma / temsilciye atama |
contacts:write | Kişi oluşturma ve güncelleme |
optouts:read | Pazarlama çıkış listesi |
optouts:write | Pazarlama çıkışı ekleme/çıkarma |
blocklist:read | Engellenen numaralar |
blocklist:write | Numara engelleme/kaldırma |
insights:read | İstatistik, etiket ve temsilci listesi |
Kanallar
İsteklerde channel alanı kanalı seçer (varsayılan whatsapp). Birden fazla hat/sayfa bağlıysa connection_id ile seçin (GET /channels).
| Kanal | Alıcı (to) | Serbest mesaj penceresi | Metin | Medya | Konum | Etkileşimli | Şablon |
|---|---|---|---|---|---|---|---|
whatsapp |
Uluslararası numara, rakam: 905321234567 | 24 saat | ✓ | ✓ | ✓ | ✓ | ✓ |
instagramInstagram DM |
Webhook'taki from.id (IG-scoped kullanıcı id) | 24 saat | ✓ | ✓ | — | ✓ | — |
messengerFacebook Messenger |
Webhook'taki from.id (PSID) | 24 saat | ✓ | ✓ | — | ✓ | — |
webWeb Canlı Destek |
Ziyaretçi kimliği web_… | Sınırsız | ✓ | ✓ | — | — | — |
GET /contacts/{id}/window.Kurallar
Yanıt zarfı
// Başarı
{ "success": true, "data": { … }, "request_id": "req_01j…" }
// Hata
{
"success": false,
"error": {
"code": "WINDOW_CLOSED",
"http": 409,
"message": "Müşteri hizmet penceresi kapalı (24 saat). Serbest mesaj gönderilemez.",
"reason": "Kanal kuralı: müşterinin son mesajından 24 saat geçtikten sonra yalnız onaylı şablon gönderilebilir. Son müşteri mesajı: 2026-08-21T14:02:11+03:00.",
"fix": "POST /v1/messages/template ile onaylı bir şablon gönderin; müşteri cevap verince pencere yeniden açılır.",
"param": "to",
"docs": "https://all.mesajilet.com/developers#err-WINDOW_CLOSED",
"request_id": "req_01j…"
}
}
request_id her yanıtta (başlıkta da X-Request-Id) bulunur; destek talebinde bu kimliği iletin — isteğinizi saniyesine kadar buluruz.
Idempotency
Yazma isteklerine Idempotency-Key başlığı ekleyin (ör. sipariş no + olay). Aynı anahtar + aynı gövde 24 saat içinde tekrar gelirse mesaj yeniden gönderilmez, ilk yanıt aynen döner (Idempotent-Replayed: true). Aynı anahtar farklı gövdeyle gelirse IDEMPOTENCY_CONFLICT.
Hız sınırı
Varsayılan 1800 istek/dakika (anahtar başına; panelden değiştirilebilir) ve saniyelik patlama (burst) sınırı dakikalık ortalamanın 2 katı, en az 10/sn (1800/dk için 60/sn). Her yanıtta X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Burst; aşımda 429 RATE_LIMITED + Retry-After. WhatsApp'ın numara başına saniyelik gönderim tavanı ayrıca sunucu tarafında otomatik uygulanır — kampanya trafiği API trafiğinizi yavaşlatmaz (öncelikli kuyruk).
Test modu (sandbox)
Panelden test anahtarı (msj_test_…) üretin. Test anahtarıyla yapılan isteklerde tüm doğrulamalar (alıcı, şablon, değişken, medya) gerçek çalışır; ancak mesaj sağlayıcıya gönderilmez, sohbet kutusuna düşmez ve hizmet penceresi kontrolü atlanır. Yanıt status: "sent" ve test: true döner; tanımlı webhook'a sent → delivered → read olayları "test": true işaretiyle simüle edilir. Yanıt başlığı: X-Mesajilet-Mode: test. Canlıya geçerken yalnız anahtarı değiştirmeniz yeterlidir.
Mesaj durumları
queued → sent → delivered → read · failed (sebep error alanında: sağlayıcı kodu + Türkçe açıklama).
Uçlar
GET /me
Anahtar, hesap, modüller, lisans ve bağlı kanallar. Entegrasyonun ilk sağlık kontrolü için kullanın.
GET /channels
Her kanal için yetenekler (capabilities), pencere süresi ve bağlantılar (connection_id değerleri: wa_15, ig_3, fb_7, web_2).
POST /messages
Serbest (oturum) mesajı. Müşterinin size yazmış olması gerekir (pencere). Alanlar:
| Alan | Tip | Açıklama |
|---|---|---|
channel | string | whatsapp (varsayılan) · instagram · messenger · web |
connection_id | string | Birden çok hat/sayfa varsa; yoksa varsayılan. |
to * | string | Alıcı (kanala göre numara / kullanıcı id). |
type | string | text (varsayılan) · image · video · audio · document · location · interactive |
text | string | type=text için gövde (WhatsApp 4096, Instagram 1000, Messenger 2000 karakter; uzun metin IG/Messenger'da otomatik bölünür). |
media | object | {"id": "…"} (POST /media'dan) veya {"url": "https://…"} (herkese açık). IG/Messenger/web yalnız url. |
caption, filename | string | Medya açıklaması / belge adı. |
latitude, longitude, name, address | number/string | type=location. |
interactive | object | {"kind":"button","text":"…","buttons":[{"id":"yes","title":"Evet"}]} (≤3) veya {"kind":"list","text":"…","button":"Seçin","sections":[{"title":"…","rows":[{"id":"a","title":"…","description":"…"}]}]} (≤10 satır). Müşterinin seçimi webhook'ta button.payload olarak gelir. |
reply_to | string | Alıntılanacak mesajın external_id'si (wamid / mid). |
// Görsel + açıklama (WhatsApp)
{ "to": "905321234567", "type": "image", "media": { "url": "https://cdn.site.com/u/fatura.jpg" }, "caption": "Faturanız ektedir." }
// Instagram DM'e hızlı yanıt butonları
{ "channel": "instagram", "to": "17841400000000000", "type": "interactive",
"interactive": { "kind": "button", "text": "Siparişiniz hazır. Teslimat tercihiniz?", "buttons": [ {"id":"kargo","title":"Kargo"}, {"id":"magaza","title":"Mağazadan al"} ] } }
POST /messages/template
Onaylı WhatsApp şablonu. Pencere gerekmez; konuşmayı başlatmanın tek yolu budur. Değişkenler şablonla birebir doğrulanır — eksik/fazla değişken anında açıklayıcı hata döner (Meta'nın belirsiz 131008'ini beklemezsiniz).
| Alan | Açıklama |
|---|---|
name * | Şablon adı. Liste: GET /templates. |
language | Genelde gerekmez — dil şablon adından çözülür. Yalnız aynı adı birden çok dilde tuttuysanız zorunlu olur; o durumda hata gövdesi mevcut dilleri listeler. |
variables | POSITIONAL şablonda dizi ["Ayşe","18421"]; NAMED şablonda nesne {"ad":"Ayşe","siparis":"18421"}. Satır sonu içeremez. |
header | Medya başlıklı şablon: {"media_id":"…"} veya {"url":"https://…","filename":"fatura.pdf"}. Metin başlıkta değişken varsa {"text":"…"}. Boş bırakılırsa şablonun kendi örnek medyası kullanılır. |
buttons | Dinamik URL butonu için [{"index":0,"text":"18421"}]. |
RECIPIENT_OPTED_OUT ile reddedilir — yasal koruma otomatiktir.GET /messages/template
Aynı gönderimin tek URL'lik hâli. Başlık ya da JSON gövdesi kuramayan çağırıcılar için: e-ticaret panelinin "sipariş durumu değişince şu adresi çağır" alanı, otomasyon araçları, hızlı deneme.
Anahtarı adresin içine koyabilirsiniz — tek parça URL, başlık gerekmez:
https://all.mesajilet.com/api/v1/messages/template/msj_live_…?to=905321234567&name=kargo_cikti
Başlık kurabiliyorsanız tercih edin; o zaman anahtar adreste görünmez:
https://all.mesajilet.com/api/v1/messages/template?to=905321234567&name=kargo_cikti
Authorization: Bearer msj_live_…
| Parametre | Açıklama |
|---|---|
to * | Alıcı numarası. phone da kabul edilir. |
name * | Şablon adı. template da kabul edilir. |
var1, var2… | Sıralı değişkenler: &var1=Ahmet&var2=18421. İsimli şablonda &vars[ad]=Ahmet. |
| Anahtar | Üç biçim: adres yolunda /messages/template/anahtar, sorguda &api_key=…, ya da Authorization başlığı. İlk ikisi yalnız bu GET ucunda geçerlidir. |
Idempotent-Replayed: true ile döner. GET adresleri istem dışı tekrar çağrılır: tarayıcı önden yükler, vekil sunucu yeniden dener, kullanıcı yenilemeye basar. Kendi idempotency_key değerinizi verirseniz koruma 24 saate çıkar.Referer başlığına ve tarayıcı geçmişine düşer. Bu ucu kullanacaksanız yalnız messages:send yetkili ayrı bir anahtar üretin, sızdığında tek tıkla iptal edin. Panel istek günlüğüne anahtar yazılmaz.GET /messages/{id}
Gönderim yanıtındaki id (msg_…) ile durum, external_id (wamid/mid) ve varsa hata detayı.
GET /templates
Filtreler: status, language, category. Her şablonda parameter_format, variables.names, header.format, dinamik butonlar ve örnek değerler döner — istek gövdenizi buna göre kurun.
POST /media
multipart/form-data, alan adı file. Dönen id 30 gün geçerlidir; aynı dosyayı binlerce alıcıya gönderirken URL yerine id kullanın (hız + Meta 131053 hatası yok). Sınırlar: görsel 5 MB, ses/video 16 MB, belge 100 MB.
curl -X POST https://all.mesajilet.com/api/v1/media -H "Authorization: Bearer msj_live_…" -F "[email protected]"
GET /contacts/{id}/window?channel=whatsapp
window_open, last_inbound_at, closes_at ve can_send.session_message / template. Göndermeden önce hangi yolu kullanacağınıza karar verin.
POST /webhooks/test
Gövde: {"event":"message.received"}. Tanımlı adresinize örnek olay gönderir, sunucunuzun HTTP yanıtını ve süresini döner.
GET /conversations
Sohbet listesi. Süzgeçler: status (open/closed/bot), channel, agent_id,
unread=true, phone, from/to. Sayfalama: limit (≤100),
cursor, order (desc/asc).
curl "https://all.mesajilet.com/api/v1/conversations?status=open&limit=25" -H "Authorization: Bearer msj_live_…"
// Yanıt
{ "success": true, "data": {
"conversations": [ { "phone": "905321234567", "channel": "whatsapp", "status": "open",
"agent": { "id": 8, "name": "Zehra" }, "unread_count": 2,
"last_message": { "text": "Kargom nerede?", "direction": "in", "at": "2026-08-29T10:12:00+03:00" } } ],
"pagination": { "limit": 25, "count": 25, "has_more": true, "next_cursor": "NDIzOTQ0" } } }
next_cursor değerini bir sonraki isteğe
?cursor= olarak geçirin; has_more:false gelene kadar sürdürün. İmleç kullanıldığı için
araya yeni sohbet girse bile aynı kayıt iki kez dönmez.GET /conversations/{phone}
Tek sohbetin özeti + kişi bilgisi. Numara 0532…, +90532… ya da 90532… biçiminde verilebilir.
GET /conversations/{phone}/messages
Mesaj geçmişi, en yeniden eskiye. Süzgeç: direction (in/out), limit (≤100).
Sayfalama zaman damgasıyla: yanıttaki pagination.before değerini ?before= olarak gönderin.
PATCH /conversations/{phone}
Sohbeti kapatır/açar veya temsilciye atar.
curl -X PATCH https://all.mesajilet.com/api/v1/conversations/905321234567 -H "Authorization: Bearer msj_live_…" -H "Content-Type: application/json" -d '{"status":"closed"}'
// Temsilciye atama (durum otomatik "open" olur)
{ "agent_id": 8 }
GET /contacts
Kişi listesi. search ad, görünen ad, kullanıcı adı, e-posta ve telefonda arar
(telefon yazımı serbest: 0532 123 45 67 da bulur).
POST /contacts · PATCH /contacts/{phone}
Alanlar: phone*, name, email, display_name, username.
POST varsa günceller, yoksa oluşturur (upsert) — yeni kayıtta 201 döner.
GET /opt-outs · POST · DELETE /opt-outs/{phone}
Pazarlama iletisi istemeyenler. Bu listedeki numaraya kampanya ve akış mesajı gönderilmez.
DELETE kişiyi listeden çıkarır ve tekrar pazarlama mesajı almaya başlar.
Yalnız açık rıza yeniden alındıysa kullanın.GET /blocked-numbers · POST · DELETE /blocked-numbers/{phone}
Engellenen numaralar — bu listedekilere hiçbir mesaj gitmez. Pazarlama çıkışından farkı: opt-out yalnız pazarlamayı durdurur, engel tüm iletişimi keser.
GET /stats
Dönem özeti (varsayılan son 30 gün; from/to ile değiştirilir): gelen/giden mesaj,
teslim, okunma, başarısız sayıları ve oranları + sohbet durumu dağılımı.
GET /labels · GET /agents
Etiket ve temsilci tanımları. Temsilci kaydında parola/oturum bilgisi dönmez.
Webhook — olay bildirimi
Panel › Geliştirici › Webhook Tanımla: https adresiniz + abone olacağınız olaylar. Her olay POST ile JSON gövde olarak gelir; 10 saniye içinde 2xx dönün (işlemeyi kuyruğa atın).
| Olay | Ne zaman |
|---|---|
message.received | Gelen mesaj (metin, medya, buton yanıtı, konum…) |
message.status | Mesaj durumu (sent → delivered → read / failed + sebep) |
template.status | Şablon onay durumu (APPROVED / REJECTED + ret sebebi) |
// Zarf
{
"id": "whd_01j…", // teslimat kimliği — aynı id iki kez gelirse yok sayın (dedupe)
"event": "message.received",
"api_version": "v1",
"created_at": "2026-08-23T10:15:02+03:00",
"company_id": 12,
"attempt": 1,
"data": {
"message": {
"id": "msg_in_1724412902123",
"external_id": "wamid.HBgNOTA1…",
"channel": "whatsapp",
"connection_id": "wa_15",
"from": { "id": "905321234567", "name": "Ayşe Yılmaz" },
"type": "text", // text | image | video | audio | document | location | …
"text": "Siparişim ne zaman gelir?",
"media": { "url": "https://…", "mime_type": "image/jpeg" }, // medyalı mesajda
"button": { "payload": "kargo", "title": "Kargo" }, // buton/liste yanıtında
"reply_to": "wamid.…", // alıntılı cevapta
"received_at": "2026-08-23T10:15:01+03:00"
}
}
}
// message.status → data.message: { id, external_id, channel, to, status: "delivered", error: null|{code,title,detail,provider_message}, occurred_at }
// Kapsam: API ve panelden gönderilen mesajlar. Toplu KAMPANYA alıcılarının durumları olay olarak GÖNDERİLMEZ
// (on binlerce olayla sunucunuzu boğmamak için); kampanya raporu panelde ve kampanya dışa aktarımında yer alır.
// template.status → data.template: { name, language, category, status: "APPROVED"|"REJECTED"|…, rejection_reason }
İmza doğrulama
Her istekte X-Mesajilet-Signature: t=<unix>,v1=<hex>. v1 = HMAC_SHA256(secret, t + "." + hamGövde). Ham gövdeyi (yeniden serileştirmeden) kullanın; t 5 dakikadan eskiyse reddedin (tekrar saldırısı koruması). Diğer başlıklar: X-Mesajilet-Event, X-Mesajilet-Delivery-Id, X-Mesajilet-Attempt.
$secret = getenv('MESAJILET_WEBHOOK_SECRET');
$raw = file_get_contents('php://input');
$hdr = $_SERVER['HTTP_X_MESAJILET_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $hdr), $p); // ['t' => '…', 'v1' => '…']
$expected = hash_hmac('sha256', $p['t'] . '.' . $raw, $secret);
if (!hash_equals($expected, $p['v1'] ?? '') || abs(time() - (int) $p['t']) > 300) {
http_response_code(401); exit;
}
$event = json_decode($raw, true);
// … kuyruğa at, hemen 200 dön
http_response_code(200);import crypto from 'node:crypto';
// Express: app.post('/webhook', express.raw({ type: '*/*' }), handler)
function handler(req, res) {
const sig = Object.fromEntries((req.get('X-Mesajilet-Signature') || '').split(',').map(kv => kv.split('=')));
const expected = crypto.createHmac('sha256', process.env.MESAJILET_WEBHOOK_SECRET)
.update(`${sig.t}.${req.body}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(sig.t)) < 300;
if (!fresh || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1 || ''))) return res.sendStatus(401);
const event = JSON.parse(req.body);
res.sendStatus(200); // önce yanıt, sonra işle
queue.add(event);
}import hmac, hashlib, time, json
from flask import request, abort
def webhook():
raw = request.get_data()
parts = dict(kv.split("=", 1) for kv in request.headers.get("X-Mesajilet-Signature", "").split(","))
expected = hmac.new(SECRET.encode(), f"{parts['t']}.".encode() + raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts.get("v1", "")) or abs(time.time() - int(parts["t"])) > 300:
abort(401)
event = json.loads(raw)
# kuyruğa at
return "", 200Yeniden deneme ve sağlık
2xx dışı yanıt ya da 10 sn zaman aşımı → 5 deneme: 1 dk, 5 dk, 30 dk, 2 sa, 12 sa sonra. Her deneme panelde görünür (HTTP kodu, yanıt gövdesinin ilk 500 karakteri). 50 ardışık başarısızlıkta adres otomatik devre dışı kalır; düzeltip panelden yeniden etkinleştirin. 410 Gone dönerseniz adres anında kapatılır. Olaylar sırasız gelebilir (ör. read, delivered'dan önce) — durumu ileri yönde güncelleyin.
Hata kataloğu
Tüm kodlar aşağıdadır; her yanıt reason (neden) ve fix (ne yapmalı) alanlarını taşır. Kodlar kararlıdır — programatik olarak bunlara göre dallanabilirsiniz.
| Kod | HTTP | Mesaj | Sebep → Çözüm |
|---|---|---|---|
| MISSING_API_KEY | 401 | Authorization başlığı eksik. | İstekte "Authorization: Bearer msj_live_…" başlığı yok. → Panel › Geliştirici sayfasından ürettiğiniz anahtarı Authorization: Bearer <anahtar> başlığıyla gönderin. |
| INVALID_API_KEY | 401 | API anahtarı geçersiz. | Anahtar biçimi hatalı ya da sistemde böyle bir anahtar yok. → Anahtarı eksiksiz kopyaladığınızdan emin olun (msj_live_ ile başlar, 49 karakter). Emin değilseniz yeni anahtar üretin. |
| API_KEY_REVOKED | 401 | API anahtarı iptal edilmiş. | Bu anahtar panelden iptal edildi; artık hiçbir isteği yetkilendirmez. → Geçerli bir anahtar kullanın veya Panel › Geliştirici sayfasından yeni anahtar üretin. |
| IP_NOT_ALLOWED | 403 | Bu IP adresinden erişime izin verilmiyor ({ip}). | Anahtar belirli IP adresleriyle sınırlandırılmış; isteğiniz başka bir adresten geldi. → İsteği izinli IP'den gönderin ya da anahtarın IP kısıtını panelden güncelleyin. |
| SCOPE_DENIED | 403 | Bu anahtarın "{scope}" yetkisi yok. | Anahtar sınırlı kapsamla (scope) üretilmiş; bu uç o kapsamı gerektiriyor. → Panel › Geliştirici sayfasından anahtarın kapsamlarını genişletin ya da tam yetkili yeni anahtar üretin. |
| MODULE_DISABLED | 403 | Geliştirici API bu hesapta etkin değil. | API modülü hesabınız için açılmamış. → Mesajilet yöneticinizle iletişime geçip API modülünün açılmasını isteyin. |
| WEBHOOK_MODULE_DISABLED | 403 | Webhook modülü bu hesapta etkin değil. | Webhook modülü hesabınız için açılmamış. → Mesajilet yöneticinizle iletişime geçip Webhook modülünün açılmasını isteyin. |
| ACCOUNT_INACTIVE | 403 | Hesap pasif durumda. | Firma hesabı yönetici tarafından pasife alınmış. → Mesajilet yöneticinizle iletişime geçin. |
| LICENSE_EXPIRED | 402 | Lisans süresi dolmuş. | Hesabın lisansı sona erdiği için gönderim yapılamıyor. → Lisansınızı yenileyin; yenilendiği anda API otomatik açılır. |
| RATE_LIMITED | 429 | Hız sınırı aşıldı: dakikada en fazla {limit} istek. | Bu anahtar izin verilen istek sayısını aştı. → {retry_after} saniye bekleyip tekrar deneyin. X-RateLimit-* başlıklarını izleyerek istek hızınızı ayarlayın; kalıcı artış için yöneticinize başvurun. |
| IDEMPOTENCY_CONFLICT | 409 | Idempotency-Key farklı bir istek gövdesiyle daha önce kullanılmış. | Aynı Idempotency-Key, içeriği farklı bir istekle 24 saat içinde yeniden gönderildi. → Her farklı istek için benzersiz bir Idempotency-Key üretin (ör. UUID). Aynı isteği güvenle tekrarlamak için aynı anahtar + aynı gövde kullanın. |
| INVALID_JSON | 400 | İstek gövdesi geçerli JSON değil. | Gövde ayrıştırılamadı (sözdizimi hatası ya da Content-Type eksik). → Content-Type: application/json gönderin ve gövdeyi bir JSON doğrulayıcıyla kontrol edin. |
| VALIDATION_FAILED | 422 | İstek doğrulaması başarısız: {detail} | Bir veya daha fazla alan eksik ya da hatalı biçimde. → "errors" listesindeki her alanı düzeltin; alan açıklamaları için dokümantasyona bakın. |
| PAYLOAD_TOO_LARGE | 413 | İstek gövdesi çok büyük (en fazla {limit}). | Gövde boyutu sınırı aşıyor. → Medya dosyalarını önce POST /v1/media ile yükleyip dönen id ile gönderin. |
| CHANNEL_UNSUPPORTED | 422 | Desteklenmeyen kanal: "{channel}". | Bu kanal adı sistemde tanımlı değil. → Geçerli değerler: {supported}. Hesabınıza bağlı kanallar için GET /v1/channels çağırın. |
| CHANNEL_NOT_CONNECTED | 409 | "{channel}" kanalı bu hesaba bağlı değil. | Hesapta bu kanal için aktif bir bağlantı (hat/sayfa) yok. → Panelden kanalı bağlayın; mevcut bağlantılar için GET /v1/channels çağırın. |
| CONNECTION_NOT_FOUND | 404 | Bağlantı bulunamadı: {connection_id}. | Verilen connection_id bu hesaba ait aktif bir bağlantıya karşılık gelmiyor. → GET /v1/channels ile geçerli connection_id değerlerini alın. |
| CHANNEL_FEATURE_UNSUPPORTED | 422 | "{channel}" kanalı "{feature}" özelliğini desteklemiyor. | Bu özellik kanalın altyapısında mevcut değil (ör. Instagram'da şablon mesajı yoktur). → Kanal özellikleri için GET /v1/channels yanıtındaki "capabilities" alanına bakın. |
| RECIPIENT_INVALID | 422 | Alıcı tanımlayıcısı geçersiz: "{to}". | {why} → WhatsApp için uluslararası biçimde rakamlar (ör. 905321234567); Instagram/Messenger için webhook'ta gelen kullanıcı id'si. |
| RECIPIENT_BLOCKED | 403 | Bu alıcı engellenmiş: {to}. | Numara/kullanıcı panelden engellenenler listesine alınmış. → Panel › Engellenen Numaralar'dan kaldırın ya da bu alıcıya gönderim yapmayın. |
| RECIPIENT_OPTED_OUT | 403 | Alıcı pazarlama mesajlarını durdurmuş: {to}. | Kişi daha önce "mesaj istemiyorum" dedi; pazarlama kategorili şablon gönderilemez. → Yalnız işlemsel (UTILITY) şablon gönderin ya da kişiyi listeden çıkarmayın (yasal yükümlülük). |
| WINDOW_CLOSED | 409 | Müşteri hizmet penceresi kapalı ({hours} saat). Serbest mesaj gönderilemez. | Kanal kuralı: müşterinin son mesajından {hours} saat geçtikten sonra yalnız onaylı şablon gönderilebilir. Son müşteri mesajı: {last_inbound}. → POST /v1/messages/template ile onaylı bir şablon gönderin; müşteri cevap verince pencere yeniden açılır. |
| WINDOW_NEVER_OPENED | 409 | Bu alıcı size hiç yazmamış; serbest mesaj gönderilemez. | Kanal kuralı: konuşmayı yalnız müşteri başlatabilir (ilk temas şablonla yapılır). → POST /v1/messages/template ile onaylı bir şablon gönderin. |
| TEMPLATE_NOT_FOUND | 404 | Şablon bulunamadı: "{name}". | Bu adla hesabınızda kayıtlı bir şablon yok. → GET /v1/templates ile mevcut şablonları listeleyin; ad birebir eşleşmeli. |
| TEMPLATE_NOT_APPROVED | 422 | Şablon "{name}" henüz onaylı değil (durum: {status}). | Meta yalnız APPROVED durumundaki şablonların gönderimine izin verir. → Şablon onaylanana kadar bekleyin ya da GET /v1/templates?status=APPROVED ile onaylı bir şablon seçin. |
| TEMPLATE_VARIABLES_MISSING | 422 | Şablon değişkenleri eksik: {missing}. | Şablon gövdesi {expected} değişken bekliyor; istekte {given} verildi. → "variables" alanını şablondaki tüm yer tutucularla doldurun. Değişken şeması için GET /v1/templates yanıtındaki "variables" alanına bakın. |
| TEMPLATE_VARIABLES_FORMAT | 422 | Şablon değişken biçimi uyumsuz: şablon {format} parametre kullanıyor. | POSITIONAL şablonda "variables" sıralı dizi, NAMED şablonda {ad: değer} nesnesi olmalı. → "variables" alanını şablonun parametre biçimine göre gönderin (GET /v1/templates → parameter_format). |
| TEMPLATE_HEADER_MISSING | 422 | Şablon başlığı {format} medya bekliyor; "header" verilmedi. | Şablon medya başlıklı; gönderimde medya referansı zorunlu. → "header": {"type":"{type}", "media_id":"…"} ya da {"url":"https://…"} ekleyin (media_id için POST /v1/media). |
| TEMPLATE_BUTTON_PARAM_MISSING | 422 | Dinamik URL butonu ({index}) için parametre eksik. | Şablonda {{…}} içeren URL butonu var; değeri verilmedi. → "buttons": [{"index": {index}, "text": "…"}] ile buton parametresini gönderin. |
| MEDIA_REQUIRED | 422 | Medya referansı eksik. | Medya tipli mesajda "media.id" ya da "media.url" verilmedi. → POST /v1/media ile yükleyip dönen id'yi kullanın ya da herkese açık bir https URL verin. |
| MEDIA_TOO_LARGE | 413 | Medya boyutu sınırı aşıyor: {size} (en fazla {limit}). | Kanalın medya tipi için izin verilen boyut aşıldı. → Dosyayı sıkıştırın ya da küçültün. Sınırlar: görsel 5 MB, ses 16 MB, video 16 MB, belge 100 MB. |
| MEDIA_TYPE_UNSUPPORTED | 415 | Desteklenmeyen medya türü: {mime}. | Bu MIME türü kanal tarafından kabul edilmiyor. → Desteklenen türler: görsel jpeg/png, video mp4/3gp, ses aac/mp3/ogg/amr, belge pdf/doc/xls/ppt/txt. |
| MEDIA_URL_INVALID | 422 | Medya URL'si geçersiz: {url}. | URL https ile başlamalı ve herkese açık olmalı (kimlik doğrulama istememeli). → Dosyayı herkese açık https adresinde yayınlayın ya da POST /v1/media ile yükleyin. |
| MEDIA_UPLOAD_FAILED | 502 | Medya kanala yüklenemedi. | Kanal sağlayıcısı dosyayı kabul etmedi: {why} → Dosya biçimini/boyutunu kontrol edip tekrar deneyin. Sorun sürerse request_id ile destek alın. |
| MESSAGE_NOT_FOUND | 404 | Mesaj bulunamadı: {id}. | Bu kimlikle hesabınıza ait bir mesaj yok (ya da 90 günden eski). → Gönderim yanıtındaki "id" değerini (msg_…) kullanın. |
| WEBHOOK_NOT_CONFIGURED | 409 | Tanımlı aktif webhook adresi yok. | Hesapta etkin bir webhook endpoint bulunmuyor. → Panel › Geliştirici › Webhook bölümünden adres ve olayları tanımlayın. |
| WEBHOOK_URL_INVALID | 422 | Webhook adresi kabul edilmedi: {why} | Adres güvenlik kurallarına uymuyor. → https:// ile başlayan, internetten erişilebilir bir adres verin. |
| WEBHOOK_UNREACHABLE | 502 | Webhook adresine ulaşılamadı: {why} | Test olayı gönderildi ancak sunucunuz 2xx dönmedi ya da yanıt vermedi. → Sunucunuzun POST isteklerini kabul ettiğinden, 10 sn içinde 2xx döndüğünden ve TLS sertifikasının geçerli olduğundan emin olun. |
| PROVIDER_REJECTED | 502 | Kanal sağlayıcısı mesajı reddetti: {title} | {reason} → {fix} |
| PROVIDER_UNAVAILABLE | 503 | Kanal sağlayıcısı geçici olarak yanıt vermiyor. | Meta/WhatsApp altyapısında geçici kesinti ya da yoğunluk var. → İsteği üstel bekleme ile (30 sn, 2 dk, 5 dk) tekrar deneyin; aynı Idempotency-Key ile tekrar güvenlidir. |
| NOT_FOUND | 404 | Uç bulunamadı: {method} {path}. | Böyle bir API ucu yok ya da sürüm öneki eksik. → Uçlar /api/v1/… altındadır; doğru yol için dokümantasyona bakın. |
| METHOD_NOT_ALLOWED | 405 | {method} bu uçta desteklenmiyor. | Uç farklı bir HTTP yöntemi bekliyor. → İzin verilen yöntemler: {allowed}. |
| INTERNAL_ERROR | 500 | Beklenmeyen bir sistem hatası oluştu. | İstek işlenirken sunucu tarafında hata oluştu; ekibimize otomatik bildirildi. → İsteği tekrar deneyin; sorun sürerse request_id ile destek ekibine yazın. |
Mesajlardaki {…} yer tutucuları gerçek değerlerle doldurulur (ör. {hours} → 24). PROVIDER_REJECTED yanıtında meta_error alanı sağlayıcının ham hatasını (kod + mesaj) da taşır.
Sürüm notları
v1 — 31.08.2026: İlk sürüm. Kanallar: WhatsApp, Instagram DM, Messenger, Web. Olaylar: message.received, message.status, template.status. OpenAPI 3.1: /developers/openapi.json.
Geriye dönük uyumluluk: /v1 altında alan kaldırılmaz; yeni alanlar eklenebilir — istemcinizi bilinmeyen alanlara toleranslı yazın.