API PLATFORMU
MMF Relay API dokümantasyonu
İş ortağı kendi panelinden MMF Relay'i kullanır: kişi ve rıza defterini okur, mesaj gönderir, olayının akıbetini görür, kendi müşterisi için çalışma alanı açar. Sözleşme HMAC imzalıdır, kimlik çerezle değil imzayla kurulur ve çalışma alanı isteğe hiç girmez — anahtarın kendisinden okunur.
Taban adreshttps://api.mmfrelay.com
Nasıl başlanır
Dört adım. Üçüncü adıma kadar hiçbir müşteriye mesaj gitmez; kurulumu canlı veriyle değil /v1/ping ile doğrularsınız.
Anahtarı alın
Çalışma alanının panelinde
/ayarlar/apiekranından anahtar üretilir; ekran tenant.settings.manageyetkisi ister. Anahtar üretilirken hangi kapsamları taşıyacağı seçilir. Sır yalnız bir kez gösterilir — kasada şifreli durduğu için geri gösterilemez, kaybedilirse yalnız yenisi üretilir.İmzalı ilk isteği atın
GET https://api.mmfrelay.com/v1/ping. Kapsam istemez, yalnız geçerli imza ister; yanlış imzaAPI-1003, penceresi kaçmış zaman damgasıAPI-1004verir.Kapsamları yanıttan doğrulayın
/v1/pingyanıtıtenant_id,partnerve anahtarın gerçekten taşıdığıscopeslistesini döndürür. Eksik bir kapsam burada görünür; ilk gerçek istekteAPI-1005olarak değil.Gerçek işi yapın
Okuma uçlarıyla başlayın (
/v1/templates,/v1/contacts), sonra gönderime geçin. Şablon mesajıPOST /v1/messages, serbest metinPOST /v1/conversations/{id}/messages.
Taban adres ve kimlik doğrulama
Taban adres https://api.mmfrelay.com. Her istek üç başlık taşır: anahtar kimliği satırı seçer, imza isteğin doğruluğunu kanıtlar. Çalışma alanı isteğe hiç girmez — anahtarın satırından okunur, dolayısıyla gövdedeki hiçbir değer hangi çalışma alanına yazılacağını seçemez.
| Başlık | Ne taşır |
|---|---|
| X-MMF-Key | Anahtar kimliği (32 onaltılık hane). Satırı bu seçer; gizli değildir, tahmin edilemez olması yeterlidir. |
| X-MMF-Timestamp | Unix saniyesi. Sunucu saatinden ±300 saniyeden fazla sapan istek, imza doğru olsa bile reddedilir (API-1004). |
| X-MMF-Signature | sha256=<64 onaltılık hane>. Gizli anahtarla üretilen HMAC-SHA256 özeti; isteğin doğruluğunu kanıtlar. |
İmza tabanı
zaman_damgası + "." + METOT + "." + yol + "." + ham_gövdeimza = "sha256=" + HMAC_SHA256(taban, gizli_anahtar). Metot büyük harftir. Yol sorgu dizesi olmadan yazılır ve sunucuda yeniden kurulur (sondaki eğik çizgi atılır); başlıktan gelen bir değere güvenilmez. Gövdesiz isteklerde son parça boş dizedir — taban zaten metot ve yolla ayrıştığı için bu güvenlidir: bir ucun imzası başka bir uçta, başka bir metotla veya başka bir gövdeyle yeniden oynatılamaz.
taban = "1756400000.GET./v1/usage."
imza = "sha256=" + HMAC_SHA256(taban, gizli_anahtar)
istek = GET https://api.mmfrelay.com/v1/usageEn sık yapılan iki hata
Gövdeyi iki kez serileştirmeyin. İmzalanan dize ile gönderilen baytlar birebir aynı olmalı; ikinci bir json_encode alan sırasını veya kaçış biçimini değiştirir ve imza tutmaz.
Sorgu dizesini imzaya katmayın. /v1/contacts?limit=100 değil /v1/contacts. Sorgu parametreleri isteğin URL'sinde durur, imza tabanında durmaz.
Zaman penceresi
X-MMF-Timestamp Unix saniyesidir ve tolerans ±300 saniyedir. Pencere imzadan önce sınanır: eski bir isteğin imzası doğru olsa bile penceresi kapanmışsa tekrar oynatılamaz. Sunucunuzun saati kayıyorsa istekler API-1004 alır — NTP ile senkron tutun.
Gövde sınırı
Sözleşme sınırı 256 KiB; aşan istek API-2001 alır. Tek istisna medya ucudur (POST /v1/conversations/{id}/media): base64 dosya taşıdığı için tavanı 24 MiB'dır. Base64 yaklaşık %33 şişirdiği için pratik dosya tavanı ~16 MB olur.
Çalışan örnekler
İkisi de aynı iki kuralı gösterir: gövde bir kez üretilir ve yola sorgu dizesi girmez.
PHP
<?php
// Anahtar panelden alınır: /ayarlar/api → "Yeni anahtar". Sır YALNIZ bir kez
// gösterilir; kaybedilirse geri gösterilemez, yalnız yenisi üretilir.
$keyId = getenv('MMF_KEY_ID'); // X-MMF-Key başlığına gider
$secret = getenv('MMF_SECRET'); // yalnız imza üretiminde kullanılır
function mmfRequest(string $method, string $path, ?array $payload = null): array
{
global $keyId, $secret;
// Gövde BİR KEZ üretilir: imzalanan dize ile gönderilen baytlar aynı olmalı.
// Gövdesiz isteklerde (GET, DELETE, /v1/templates/sync) taban boş dizedir.
$body = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_UNICODE);
$timestamp = (string) time();
// taban = zaman + "." + METOT + "." + yol + "." + ham_gövde
// Yola sorgu dizesi GİRMEZ: "/v1/contacts?limit=100" değil "/v1/contacts".
$base = $timestamp . '.' . $method . '.' . $path . '.' . $body;
$signature = 'sha256=' . hash_hmac('sha256', $base, $secret);
$ch = curl_init('https://api.mmfrelay.com' . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-MMF-Key: ' . $keyId,
'X-MMF-Timestamp: ' . $timestamp,
'X-MMF-Signature: ' . $signature,
],
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['status' => $status, 'body' => json_decode($raw, true)];
}
// 1) Kurulum doğrulaması: kapsam eksiği üretime çıkmadan burada görünür.
$ping = mmfRequest('GET', '/v1/ping');
// => ["ok" => true, "tenant_id" => "...", "partner" => "...", "scopes" => [...]]
// 2) Şablon mesajı. Yanıt 202 ve status "queued": mesaj kuyruğa alındı,
// Meta'ya isteği kuyruk tüketicisi atar. 200 demek yanlış olurdu.
$sent = mmfRequest('POST', '/v1/messages', [
'phone' => '+905321234567',
'template_name' => 'fatura_bildirimi',
'template_language' => 'tr',
'variables' => [
['key' => 'musteri_adi', 'target' => 'body', 'value' => 'Ayşe Yılmaz'],
],
]);
if ($sent['status'] !== 202) {
// Hata gövdesi her zaman { "error": { "code": "API-xxxx", "message": "..." } }
error_log('MMF Relay: ' . $sent['body']['error']['code']);
}
Node.js
import { createHmac } from "node:crypto";
const BASE_URL = "https://api.mmfrelay.com";
const KEY_ID = process.env.MMF_KEY_ID;
const SECRET = process.env.MMF_SECRET;
/**
* @param {"GET"|"POST"|"PUT"|"PATCH"|"DELETE"} method
* @param {string} path Sorgu dizesi HARİÇ yol, ör. "/v1/contacts"
* @param {object} [payload]
* @param {Record<string,string>} [query]
*/
async function mmfRequest(method, path, payload, query) {
// Gövde bir kez üretilir; imzalanan dize ile gönderilen baytlar aynı olmalı.
const body = payload === undefined ? "" : JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
// taban = zaman + "." + METOT + "." + yol + "." + ham_gövde
// Sorgu dizesi tabana GİRMEZ; sunucu yolu kendisi yeniden kurar.
const signature = createHmac("sha256", SECRET)
.update(`${timestamp}.${method}.${path}.${body}`)
.digest("hex");
const search = query ? `?${new URLSearchParams(query).toString()}` : "";
const response = await fetch(`${BASE_URL}${path}${search}`, {
method,
headers: {
"Content-Type": "application/json",
"X-MMF-Key": KEY_ID,
"X-MMF-Timestamp": timestamp,
"X-MMF-Signature": `sha256=${signature}`,
},
body: body === "" ? undefined : body,
});
return { status: response.status, body: await response.json() };
}
// 1) Kurulum doğrulaması.
const ping = await mmfRequest("GET", "/v1/ping");
console.log(ping.body.scopes); // anahtarın gerçekten taşıdığı kapsamlar
// 2) Sorgu dizeli okuma: imza yalnız "/v1/contacts" üzerinden hesaplanır.
// ?phone= tam eşleşmedir ve normalleştirme sunucudadır: +90…, 90…, 0…
// ve çıplak biçim aynı kişiyi bulur; bozuk numara API-2003 döner.
const page = await mmfRequest("GET", "/v1/contacts", undefined, {
phone: "05321234567",
tag: "vip",
});
// { data: [...], next_cursor: "..." | null }
// 3) Şablon mesajı. Aynı işi tekrar denerken Idempotency-Key kullanın:
// aynı anahtar + aynı gövde saklanan yanıtı döndürür, ikinci mesaj çıkmaz.
const sent = await mmfRequest("POST", "/v1/messages", {
contact_id: page.body.data[0]?.contact_id,
template_name: "fatura_bildirimi",
template_language: "tr",
variables: [{ key: "musteri_adi", target: "body", value: "Ayşe Yılmaz" }],
});
if (sent.status !== 202) {
console.error(sent.body.error.code, sent.body.error.message);
}
Yetki kapsamları
API anahtarının kullanıcısı yoktur, dolayısıyla rolü de yoktur; yetkisi bir kapsam listesidir. Kapsam uç bazındadır, iş ortağı bazında değil: bir gün yalnız okuma yetkisi vermek istediğinizde gönderim yetkisini de vermek zorunda kalmayasınız diye. messages:send bilerek contacts:write'tan ayrıdır — kişi güncellemek ile müşteriye mesaj yollamak aynı riske sahip değildir.
Anahtar bir ucun istediği kapsamı taşımıyorsa istek API-1005 ile döner ve uç hiç çalışmaz.
| Kapsam | Ne açar |
|---|---|
| events:read | Olay durumunu oku |
| usage:read | Kullanım ve kotayı oku |
| templates:read | Şablonları oku |
| templates:write | Şablon oluştur ve düzenle |
| contacts:read | Kişileri ve rızaları oku |
| contacts:write | Kişi, etiket ve rıza yaz |
| conversations:read | Konuşmaları oku |
| conversations:write | Konuşma durumu ve not yaz |
| messages:send | Mesaj gönder |
| campaigns:read | Toplu gönderimleri oku |
| campaigns:write | Toplu gönderim oluştur ve başlat |
| automations:read | Otomasyonları ve çalışmaları oku |
| automations:write | Otomasyon aç/kapat ve düzenle |
| connection:read | Bağlantı ve numara durumunu oku |
| tenants:provision | Çalışma alanı aç ve bağlama bağlantısı üret Yalnız MMF Relay yönetimi verir; panel seçicisinde görünmez. |
Uçlar (44)
Yol eşleşmesi metottan bağımsız kaydedilir: yanlış metotla çağrılan bir uç 404 değil 405 alır ve yanıt Allow başlığı taşır. Böyle bir uç yoksa API-2004 döner.
Sağlık ve durum
Entegrasyonu kurarken ilk çağrılan uçlar. /v1/ping kapsam istemez: geçerli imza yeter ve yanıt anahtarın hangi çalışma alanına, hangi kapsamlarla bağlı olduğunu söyler.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/ping | — (yok) | Kimlik ve kapsam listesi; kurulum doğrulaması. |
| GET | /v1/usage | usage:read | Aylık kota, ek kota havuzu, kullanım ve tahmini maliyet. Rakam tahmindir: mesaj faturasını Meta doğrudan işletmeye keser. quota_alert alanı, gönderilebilecek toplam hakkın %80'i tükenince uyarı taşır. |
| GET | /v1/connection | connection:read | Numara bağlı mı, hangi akışla bağlı, verim tavanı ve kalite derecesi ne. Mesajlaşma kademesi ham (messaging_tier) ve sayısal (messaging_tier_limit) döner; bilinmiyorsa ikisi de null. |
Olaylar
Olay ucundan (ör. ParkHesap) gönderilmiş kayıtların akıbeti. Ham gövde geri dönmez: iş ortağı onu zaten kendisi gönderdi ve geri vermek gereksiz bir kişisel veri yolu açardı.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/events | events:read | Gönderilen olayların işlenme durumu. ?status= ile süzülür. |
| GET | /v1/events/{event_id} | events:read | Tek olayın durumu ve atlanmışsa gerekçesi. |
Şablonlar
Dört yazma ucu da panelin şablon çekirdeğinden geçer: yerel doğrulayıcı Meta'ya istekten önce çalışır. Onaylı şablonda düzenleme hakkı 24 saatte 1, 30 günde 10'dur — yerelde reddedilen bir deneme bu hakkı yakmaz.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/templates | templates:read | Bütün şablonlar; durum, değişken yuvaları ve gönderilebilirlik. |
| POST | /v1/templates | templates:write | Şablon oluşturur ve Meta onayına gönderir. |
| POST | /v1/templates/sync | templates:write | Meta'daki durumları yerel aynaya çeker. Gövdesiz. |
| PATCH | /v1/templates/{template_code} | templates:write | Şablonu düzenler. name ve language gönderilemez; onaylı şablonda kategori kilitlidir. |
| DELETE | /v1/templates/{template_code} | templates:write | Şablonu siler (tek sürüm, bütün diller değil). Silinen ad 30 gün yeniden kullanılamaz. |
Kişiler ve rıza
Kişi listesi e-posta taşımaz, tekil kişi taşır; rıza kanıtı (evidence) da yalnız tekil kişide döner. Toplu okuma kişisel verinin en kolay sızdığı yoldur.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/contacts | contacts:read | Kişi listesi (imleçli). ?status=, ?tag=, ?consent=, ?phone= süzgeçli. ?phone= tam eşleşmedir ve dört yaygın biçimi de kabul eder; çözülemeyen numara API-2003 alır. |
| GET | /v1/contacts/{contact_id} | contacts:read | Kişi + rıza defteri + değiştirilemez rıza geçmişi. |
| POST | /v1/contacts | contacts:write | Kişi oluşturur; kaynak integration olarak yazılır. Kayıtlı numarada 409 (API-3002) döner ve hata gövdesi çakışan kişinin contact_id değerini taşır. |
| PATCH | /v1/contacts/{contact_id} | contacts:write | Kişiyi günceller. Alanın hiç gönderilmemesi «dokunma», null gönderilmesi «temizle» demektir. |
| PUT | /v1/contacts/{contact_id}/consent | contacts:write | Rıza yazar. granted her zaman kanıt ister; withdrawn üzerine yazılamaz (API-6007). |
| POST | /v1/contacts/{contact_id}/tags | contacts:write | Etiket ekler ve tag_added otomasyon kurallarını uyandırır. Etiket normalleştirilir. |
| DELETE | /v1/contacts/{contact_id}/tags/{tag} | contacts:write | Etiket kaldırır. İdempotenttir: olmayan etiketi kaldırmak da başarıdır. |
Gelen kutusu
Konuşma okuma, konuşma yönetimi ve pencere içi gönderim. Müşteriye ÇIKAN her şey messages:send ister; panelde kalan (not, okundu) conversations:write ile yeter.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/conversations | conversations:read | Konuşma listesi ve hizmet penceresi durumu. Son mesajın önizlemesini taşımaz. |
| GET | /v1/conversations/{conversation_id}/messages | conversations:read | Mesajlar, teslimat durumu ve medya üstverisi. |
| GET | /v1/messages/{message_id} | conversations:read | Tek mesaj, kimliğiyle. POST /v1/messages yanıtındaki message_id ile akıbet sorgulanır; gövde konuşma mesajları listesindeki elemanla birebir aynıdır. |
| PATCH | /v1/conversations/{conversation_id} | conversations:write | Konuşma durumu (open / pending / resolved) ve temsilci ataması. |
| POST | /v1/conversations/{conversation_id}/messages | messages:send | Serbest metin VEYA butonlu/listeli interaktif mesaj. Yalnız hizmet penceresi içinde. |
| POST | /v1/conversations/{conversation_id}/media | messages:send | Medya mesajı (base64). Gövde tavanı 24 MiB; Meta'ya yükleme kuyruktadır. |
| POST | /v1/conversations/{conversation_id}/reactions | messages:send | Mesaja emoji tepkisi. Tepki kaldırma desteklenmez. |
| POST | /v1/conversations/{conversation_id}/notes | conversations:write | Dahili not. Müşteriye gitmez, kuyruğa girmez, ücretlenmez; pencere kapalıyken de yazılır. |
| POST | /v1/conversations/{conversation_id}/read | conversations:write | Okunmamış sayacını sıfırlar ve okundu bildirimini en iyi çabayla dener. Gövdesiz. |
Şablon mesajı
Hizmet penceresinden bağımsız tek gönderim yolu; dayanağı pencere değil rıza defteridir. Yanıt 202 ve status: queued taşır, çünkü Meta'ya isteği yalnız kuyruk tüketicisi atar.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| POST | /v1/messages | messages:send | Onaylı şablon mesajını kuyruğa bırakır. Idempotency-Key yalnız burada desteklenir. |
Otomasyonlar
Kural yazımı tam sözleşme ister: tetikleyici, koşullar, bekleme süresi ve bütün değişken yuvalarını kapatan bağlamalar. «Olay → şablon adı» kısayolu yoktur — şablon adı tek başına mesaj üretmez.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/automations | automations:read | Kurallar; tetikleyici, koşul ve bağlamalarıyla. |
| GET | /v1/automations/{automation_id}/runs | automations:read | Çalışma günlüğü ve atlama gerekçesi. Kişi adı ve telefon taşımaz, contact_id taşır. |
| POST | /v1/automations | automations:write | Kural kurar. Kural draft doğar ve hiç değerlendirilmez; açmak ayrı bir adımdır. |
| PUT | /v1/automations/{automation_id} | automations:write | Kural içeriğini bütünüyle değiştirir ve durumu korur; kuralı gizlice açmaz. |
| PATCH | /v1/automations/{automation_id} | automations:write | Kuralı açar veya duraklatır. Gövde yalnız { active } taşır. |
| DELETE | /v1/automations/{automation_id} | automations:write | Kuralı ve çalışma günlüğünü siler. |
Kampanyalar
Toplu gönderim. Alıcılar başlatma anında dondurulur: gönderim sırasında kitle yeniden hesaplanmaz, çünkü çağıran belirli bir sayıyı ve maliyeti onayladı. Medya başlıklı şablonla kampanya API'den kurulamaz (API-8004).
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| GET | /v1/campaigns | campaigns:read | Toplu gönderim listesi ve sayaçları. |
| GET | /v1/campaigns/{campaign_id} | campaigns:read | Kampanya özeti + alıcı raporu (kendi imleciyle sayfalanır). |
| POST | /v1/campaigns | campaigns:write | Kampanya taslağı kurar: kitle, şablon, değişken bağlamaları. |
| PATCH | /v1/campaigns/{campaign_id} | campaigns:write | Taslağı düzenler. Başlatılmış kampanya kilitlidir (API-8007). |
| DELETE | /v1/campaigns/{campaign_id} | campaigns:write | Yalnız taslağı siler. Başlatılmış kampanya silinmez; raporu Meta maliyetinin kanıtıdır. |
| POST | /v1/campaigns/{campaign_id}/launch | campaigns:write | Kampanyayı hazırlamaya alır (status: preparing); alıcılar başlatma anının damgasıyla arka planda dondurulur, hazırlık bitince kampanya kendiliğinden koşar. İlerleme GET ile izlenir. |
| POST | /v1/campaigns/{campaign_id}/pause | campaigns:write | Duraklatır; her dağıtım turunun başında etkir. |
| POST | /v1/campaigns/{campaign_id}/resume | campaigns:write | Sürdürür ve dağıtımı yeniden kuyruğa bırakır. |
Çalışma alanı açma (sağlayıcı)
Yalnız tenants:provision kapsamı taşıyan anahtarla çalışır ve bu kapsamı yalnız MMF Relay yönetimi verir; panelin kapsam seçicisinde hiç görünmez, panel yolundan istenirse sunucuda reddedilir (API-5007). Sağlayıcı zinciri düzdür: açılan alanın kendi anahtarı bu kapsamı taşımaz.
| Metot | Yol | Kapsam | Ne yapar |
|---|---|---|---|
| POST | /v1/tenants | tenants:provision | Çalışma alanı açar; anahtarı (sır yalnız bu yanıtta) ve numara bağlama bağlantısını döndürür. |
| GET | /v1/tenants | tenants:provision | Açılan alanlar; paket ataması ve bağlantı durumlarıyla. |
| GET | /v1/tenants/{tenant_id}/connect-link | tenants:provision | Bağlama bağlantısını yeniler. Yenisi öncekini iptal eder; eski bağlantı yeniden üretilemez. |
Sayfalama, idempotency ve hız sınırı
Sayfalama — imleç, sayfa numarası değil
Sayfa numarası yoktur. Liste okunurken yeni kayıt yazılır ve sayfa numarası kayar: 2. sayfayı isteyen çağıran, araya giren bir kayıt yüzünden bir satırı iki kez veya hiç görmez. Bütün liste uçları aynı zarfı döndürür.
{
"data": [ /* ... */ ],
"next_cursor": "eyJrIjoiY29udGFjdHMiLCJ2IjoiMjAy…" | null
}?limit=varsayılan 50, tavan 200. Geçersiz değer hata değil varsayılandır ve tavan sessizce uygulanır: 10.000 satır isteyen bir çağıranı reddetmek yerine 200 vermek, isteğin yine de işine yaraması demektir.?cursor=bir önceki yanıtınnext_cursordeğeridir. İmleç base64url ve opaktır; ayrıştırmayın.- İmleç liste türünü de taşır: bir listenin imleciyle başka bir listeyi sayfalamak
API-2007verir. Sessizce yanlış sonuç dönmez. next_cursornullise liste bitmiştir.
Idempotency-Key
Yalnız POST /v1/messages destekler. Başlık 1–200 yazdırılabilir ASCII karakterdir. Aynı işi yeniden denerken kullanın: ağ zaman aşımından sonra atılan ikinci istek sessizce ikinci bir mesaj üretmesin diye.
| Durum | Yanıt |
|---|---|
Aynı anahtar + aynı gövde, ilk istek 202 ile tamamlanmış | Saklanan yanıt, üstünde Idempotency-Replay: true başlığı |
| İlk istek bir hatayla dönmüş (4xx/5xx) | Anahtar serbest bırakılır; yeniden deneme isteği yeniden çalıştırır |
| Aynı anahtar + aynı gövde, ilk istek hâlâ işleniyor | API-3002 |
| Aynı anahtar + farklı gövde | API-2006 |
Yalnız mesaj gerçekten üretilen sonuç (202) saklanır. Sözleşmenin tek amacı çift mesaj göndermemektir; hata yanıtında ortada gönderilmiş bir mesaj yoktur, dolayısıyla saklanacak bir şey de yoktur. API-6001 (rıza yok) veya API-4002 (kota dolu) alan bir istek anahtarı kilitlemez: rızayı yazdıktan ya da kota yükseltildikten sonra aynı anahtarla yeniden deneme mesajı gönderir. Kayıt 24 saat yaşar.
Hız sınırı
Sınır anahtar başınadır ve token bucket'tır.
| Tür | Hız | Patlama |
|---|---|---|
Okuma (GET) | 20 istek/sn | 40 |
| Yazma (POST / PUT / PATCH / DELETE) | 5 istek/sn | 10 |
Her yanıt X-RateLimit-Remaining ve X-RateLimit-Reset (epoch saniye) taşır. Sınır aşıldığında API-4001 döner ve yanıt ayrıca Retry-After taşır; o değer hiçbir zaman sıfır olamaz.
Sıra kimlik → hız sınırı → kapsam'dır: imzasız bir sel jeton yakamaz, kapsamı eksik bir anahtar da sınırdan muaf değildir. Bu sınır bizim sınırımızdır; Meta'nın numara başına tavanı (Cloud API 80 mesaj/sn, coexistence 20 mesaj/sn ve sabit, aynı kişiye ~0,17 mesaj/sn) ayrı bir şeydir ve gönderim yolunda ayrıca uygulanır.
Rıza asimetrisi
Panel withdrawn üzerine granted yazabilir; API yazamaz.
Bir kişinin rızası withdrawn iken API'den gelen granted yazılmaz ve istek API-6007 alır. Aynı görünen iki yolun farklı davranması, söylenmediğinde entegratörün saatlerini yakar; bu yüzden burada açıkça yazıyor.
Gerekçe: geri alma mutlaktır. Bir kişi «artık yazmayın» dedikten sonra o kaydı tersine çevirmek yeni ve daha güçlü bir kanıt gerektirir — kişinin kendisinin yeniden rıza vermesi. İşletmenin panelden bilinçli bir işlemi bu kanıtı taşıyabilir: ekranda oturan bir kullanıcı, adı denetim kaydında duran biri, kanıt alanını dolduran biri. Bir entegrasyon anahtarının rutin senkron yazması taşıyamaz: bir ERP kaydı «bu kişi tekrar rıza verdi» demez, «bu kişiyle ticari ilişkim var» der ve ikisi aynı şey değildir.
İkinci fark: API'den gelen her granted kanıt (evidence) taşımak zorundadır, kanal ne olursa olsun. Panel yalnız devralınan kanallarda ister. Kanıtsız rıza Meta denetiminde savunulamaz ve bir makinenin ürettiği rıza kaydının dayanağını sonradan bulmak imkânsızdır.
withdrawn yazmak her zaman serbesttir ve kanıt istemez: çıkış talebini evraka bağlamak, geri almayı geciktirmek olurdu.
Hizmet penceresi
WhatsApp'ta serbest metin yalnız müşterinin son mesajından itibaren 24 saat içinde gönderilebilir. MMF Relay'de bu iki gönderim yolunu birbirinden ayırır:
| Uç | Pencere kapısı | Dayanağı |
|---|---|---|
| POST /v1/messages | Yok. Onaylı şablon pencereden bağımsız gider. | Rıza defteri (kategori bazında) |
| POST /v1/conversations/{id}/messages · /media · /reactions | Var. Pencere kapalıysa API-6010 ve mesaj satırı bile yazılmaz. | Müşterinin son mesajından itibaren 24 saat |
| POST /v1/conversations/{id}/notes · /read | Yok. Müşteriye bir şey gitmiyor. | — |
Pencere kapısı rıza kategorisine bakmaz: «DUR» yazan bir müşterinin kendi sorusuna yanıt engellenmez, ama tanıtım bu kanaldan meşrulaşmaz. Konuşmanın penceresi açık mı, GET /v1/conversations yanıtından okunur.
Gönderim uçlarının hepsi 202 ve status: "queued" döndürür. 200 demek mesajın Meta'ya teslim edildiğini söylemek olurdu ve o an henüz doğru değildir: Meta'ya isteği yalnız kuyruk tüketicisi atar. Teslimat durumu GET /v1/conversations/{id}/messages üzerinden okunur.
Teslimat bildirimi (giden webhook)
Mesajın durumu değiştiğinde biz size imzalı bir POST göndeririz; durumu sormanız gerekmez. Uç çalışma alanı başına bir tanedir ve /ayarlar/api ekranından kurulur. Kaydedildiği anda bir imza anahtarı üretilir ve yalnız o an gösterilir.
Sözleşme, sizin bize gönderdiğiniz olay ucunun aynasıdır: aynı başlık adları, aynı imza tabanı. Gönderirken yazdığınız imza kodunu alırken doğrulama için yeniden kullanabilirsiniz.
| Başlık | Ne taşır |
|---|---|
X-MMF-Event | message.status veya webhook.test |
X-MMF-Timestamp | Unix epoch saniye (tam sayı, UTC) |
X-MMF-Signature | sha256= + HMAC-SHA256 özeti |
X-MMF-Delivery | Her HTTP denemesi için benzersiz; yeniden denemede değişir |
imza_tabanı = X-MMF-Timestamp + "." + ham_gövde{
"event_id": "evt_9f2c…", // yeniden denemede AYNI kalır
"event": "message.status",
"occurred_at": "2026-09-15T08:14:02.000Z",
"data": {
"message_id": "0f1a…", // POST /v1/messages yanıtındaki kimlik
"conversation_id": "7c33…",
"contact_id": "b81e…",
"status": "delivered", // sent | delivered | read | failed
"error_code": null, // yalnız failed: MSG-xxxx
"meta_error_code": null, // yalnız failed: Meta'nın sayısal kodu
"meta_error_subcode": null
}
}- Tekilleştirmeyi
event_idile yapın. Kimlik(çalışma alanı, mesaj, durum)üçlüsünden türetilir: aynı durum ikinci kez üretilse bile kimlik aynı çıkar.X-MMF-Deliveryile karıştırmayın — o her denemede değişir. - Hızlı ve 2xx dönün. Yanıtın gövdesi okunmaz.
408,429ve5xxyeniden denenir (1 → 2 → 4 → 8 dakika, en fazla 5 deneme); diğer4xxterminaldir. İstek 10 saniyede zaman aşımına uğrar. - Sıra garantisi yoktur. Meta'nın bildirimleri sırasız gelir (
delivered,sent'ten önce ulaşabilir) ve biz de aynı sırayla iletiriz. Durumu yalnız ileri yönde işleyin. wa_message_idgönderilmez. Meta'nın kimliği base64 gövdesinde alıcının telefon numarasını taşıyor ve günlüğünüze düşerdi. Eşleştirmemessage_idile yapılır — gönderirken zaten size döndü.acceptedveplayedbildirilmez. Birincisi bizim iç durumumuzdur (Meta isteği kabul etti) ve Meta'nınsentbildirimi saniyeler içinde gelir; ikincisi kapsam dışıdır.- Art arda 20 başarısız teslimatta uç kapatılır ve panelde gerekçesiyle görünür. Adres yeniden kaydedilince açılır ve sayaç sıfırlanır.
Hata kodları
Kod kararlıdır ve değişmez; metin değişebilir. Günlüğünüzde koda dallanın, metne değil. Her hata yanıtı aynı zarfı taşır:
{
"error": {
"code": "API-6002",
"message": "Kişi bu kategoride rızasını geri almış; mesaj gönderilmedi."
}
}Biçim hatalarında mesajın sonuna hangi alanın bozuk olduğu eklenir ( (alan: template_language) ). Şablon yazma hataları ayrıca bir support_id taşır; o kimlikle bize yazarsanız sunucu günlüğündeki Graph kaydını buluruz.
1xxx · Kimlik ve yetki
Anahtar, imza, zaman penceresi ve kapsam. Biçimsiz bir anahtar ile hiç var olmayan bir anahtar aynı kodu alır: hangi anahtarların var olduğu dışarıya sızdırılmaz.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-1001 | 401 | API anahtarı bulunamadı veya biçimsiz. |
| API-1002 | 401 | Bu API anahtarı artık geçerli değil. |
| API-1003 | 401 | İmza doğrulanamadı. |
| API-1004 | 401 | Zaman damgası kabul penceresinin dışında. |
| API-1005 | 403 | Bu anahtar bu işlem için gereken yetkiyi taşımıyor. |
2xxx · İstek biçimi ve medya sınırları
Bilinmeyen alan yok sayılır; tanınan alan biçimsizse istek reddedilir ve yanıt hangi alanın bozuk olduğunu söyler.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-2001 | 413 | Gövde 256 KiB sınırını aşıyor. |
| API-2002 | 400 | Gövde JSON olarak ayrıştırılamadı. |
| API-2003 | 400 | Gövde şemaya uymuyor. |
| API-2004 | 404 | Böyle bir uç yok. |
| API-2005 | 405 | Bu uç bu HTTP metodunu kabul etmiyor. |
| API-2006 | 409 | Bu Idempotency-Key daha önce farklı bir gövdeyle kullanıldı. |
| API-2007 | 400 | Sayfalama imleci geçersiz. |
| API-2008 | 422 | WhatsApp bu dosya türünü kabul etmiyor. |
| API-2009 | 413 | Dosya, WhatsApp'ın bu tür için izin verdiği boyutu aşıyor. |
3xxx · Kaynak durumu
Kayıt bu çalışma alanında yok, ya da işlemi kabul edecek durumda değil.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-3001 | 404 | Kayıt bulunamadı. |
| API-3002 | 409 | Kaynak bu işlemi kabul edecek durumda değil. |
| API-3003 | 409 | Çalışma alanının WhatsApp bağlantısı yok. Numara bağlanmadan mesaj gönderilemez. |
4xxx · Hız ve kota
İkisi de 429 döner ama farklı şeyler söyler: biri «çok hızlısınız», diğeri «bu ayın hakkı bitti».
| Kod | HTTP | Anlamı |
|---|---|---|
| API-4001 | 429 | İstek sınırı aşıldı. |
| API-4002 | 429 | Aylık kota ve ek kota havuzu tükendi. |
5xxx · Anahtar yönetimi (panel)
Bu seri uca değil panele konuşur: anahtarı üreten yol paneldedir ve hatası kullanıcıya görünür. Destek kaydında API-5001 gören kişi hangi ekrana bakacağını bilir.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-5001 | 404 | Bu API anahtarı bulunamadı. |
| API-5002 | 409 | Bu API anahtarı zaten iptal edilmiş. |
| API-5003 | 400 | En az bir yetki kapsamı seçin. |
| API-5004 | 400 | İş ortağı adı yalnız küçük harf, rakam ve alt çizgi içerebilir; harfle başlamalı ve 2-40 karakter olmalıdır. |
| API-5005 | 400 | Etiket en fazla 120 karakter olabilir. |
| API-5006 | 400 | Tanınmayan bir yetki kapsamı gönderildi. |
| API-5007 | 403 | Bu yetki kapsamını yalnız MMF Relay yönetimi verebilir. Sağlayıcı olmak için bizimle iletişime geçin. |
| API-5008 | 400 | Webhook adresi kabul edilmedi. Adres https:// ile başlayan, gerçek bir alan adı taşıyan ve kimlik bilgisi içermeyen bir adres olmalı. |
| API-5009 | 404 | Bu çalışma alanında kurulu bir webhook ucu yok. Önce bir adres kaydedin. |
6xxx · Gönderim ve rıza kapıları
Ortak gönderim servisinin her atlama gerekçesine birebir karşılık gelir. «Mesaj gitmedi» tek bir koda indirgenmez; teşhis kabiliyeti tam olarak buradadır.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-6001 | 422 | Kişinin bu kategoride rıza kaydı yok; mesaj gönderilmedi. |
| API-6002 | 422 | Kişi bu kategoride rızasını geri almış; mesaj gönderilmedi. |
| API-6003 | 422 | Kişi aktif değil (arşivli veya engelli); mesaj gönderilmedi. |
| API-6004 | 422 | Şablon onaylı değil; onaysız şablonla gönderim yapılmaz. |
| API-6005 | 422 | Şablon standart kopyasından sapmış; panelden geri döndürülmeden gönderilmez. |
| API-6006 | 422 | Şablonun beklediği bir değişken eksik veya boş. |
| API-6007 | 409 | Rıza geri alınmış durumda; API üzerinden yeniden rıza yazılamaz. |
| API-6008 | 422 | Bu şablon API üzerinden gönderilemez (medya başlığı veya desteklenmeyen bileşen). |
| API-6009 | 409 | Bu çalışma alanına henüz paket atanmadı; paket atanana kadar mesaj gönderilemez. Paket ataması için MMF Relay yönetimiyle iletişime geçin. |
| API-6010 | 422 | 24 saatlik hizmet penceresi kapalı; bu konuşmaya serbest mesaj gönderilemez. Onaylı şablonla göndermek için POST /v1/messages kullanın. |
| API-6011 | 409 | Meta bu numaranın gönderimini kalite nedeniyle kısıtladı; şablon gönderimi duraklatıldı. Ne zaman açılacağı GET /v1/connection yanıtındaki send_restricted_until alanındadır. |
| API-6012 | 429 | Meta'nın bu numara için 24 saatlik mesajlaşma kademesi (benzersiz alıcı tavanı) doldu. Pencere kayan 24 saattir; tavan GET /v1/connection yanıtındaki messaging_tier_limit alanındadır. |
7xxx · Şablon yazma
Meta'nın kod 100 altındaki alt kodları asıl gerekçeyi taşır ve ayrı kodlarla döner. Bu yanıtlar ayrıca bir support_id taşır; Graph hatası her durumda aynı kimlikle sunucu günlüğüne yazılır.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-7001 | 409 | Bu ad ve dilde bir şablon zaten var. Silinen şablonun adı 30 gün boyunca yeniden kullanılamaz. |
| API-7002 | 422 | Meta şablon içeriğini geçersiz buldu. |
| API-7003 | 422 | Meta gövde metninin biçimini geçersiz buldu. Kalın/italik işaretlerinin çift olduğundan ve değişkenlerin metnin başında veya sonunda durmadığından emin olun. |
| API-7004 | 422 | Meta başlık biçimini geçersiz buldu. Başlıkta biçimlendirme desteklenmez ve en fazla bir değişken kullanılabilir. |
| API-7005 | 422 | Meta alt bilgi biçimini geçersiz buldu. Alt bilgide değişken ve biçim işareti kullanılamaz. |
| API-7006 | 422 | Şablonda metne göre çok fazla değişken var. Değişken sayısını azaltın veya metni uzatın. |
| API-7007 | 422 | Değişken metnin en başında veya en sonunda duramaz. Değişkenin önüne veya arkasına metin ekleyin. |
| API-7008 | 422 | Bir alan Meta'nın karakter sınırını aşıyor. |
| API-7009 | 409 | Meta bu şablonun düzenlenmesini reddetti; şablonda hiçbir değişiklik uygulanmadı. Ayrıntı için destek koduyla iletişime geçin. |
| API-7010 | 409 | Onaylı şablonun kategorisi değiştirilemez; yalnız içerik ve geçerlilik süresi düzenlenebilir. |
| API-7011 | 409 | Şablon bu durumda düzenlenemez; yalnız APPROVED, REJECTED veya PAUSED şablonlar düzenlenebilir. |
| API-7012 | 422 | Meta bu içeriği politika gereği engelledi. |
| API-7013 | 429 | Meta'nın istek sınırına ulaşıldı. Bir süre sonra tekrar deneyin. |
| API-7014 | 409 | Meta erişim yetkisi geçersiz; WhatsApp bağlantısı panelden yenilenmeli. |
| API-7015 | 503 | Meta geçici olarak erişilemiyor. Daha sonra tekrar deneyin. |
| API-7016 | 502 | Meta şablon işlemi tamamlanamadı. |
| API-7017 | 409 | Bu şablon API üzerinden düzenlenemeyen bir bileşen taşıyor (medya başlığı, carousel veya salt okunur buton). |
| API-7018 | 409 | Meta bu işlem için gerekli izni vermiyor. |
8xxx · Kampanya (80xx) ve otomasyon (81xx) yazma
Kapılarla kesişen gerekçeler kendi serilerinde kalır: onaysız şablon API-6004, eksik değişken API-6006, desteklenmeyen bileşen API-6008, paketsiz alan API-6009.
| Kod | HTTP | Anlamı |
|---|---|---|
| API-8001 | 422 | Seçilen etiketlerle gönderilebilecek kimse bulunamadı. |
| API-8002 | 422 | Hedef kitle tek kampanya için çok büyük. Etiketle daraltın veya kampanyayı bölün. |
| API-8003 | 422 | Zamanlanan an geçmişte olamaz. |
| API-8004 | 422 | Bu şablonun başlığı bir dosya istiyor; kampanya medyası API üzerinden yüklenemez. Medyalı kampanyayı panelden kurun. |
| API-8005 | 409 | Kampanyanın başlık dosyası hazır değil. Kampanyayı silip panelden yeniden kurun. |
| API-8006 | 502 | Başlık dosyası Meta'ya yüklenemedi. Bir süre sonra yeniden başlatmayı deneyin. |
| API-8007 | 409 | Başlatılmış bir kampanyanın içeriği değiştirilemez veya silinemez. |
| API-8008 | 409 | Bu kampanya duraklatılamaz veya sürdürülemez. |
| API-8101 | 409 | Bu çalışma alanında daha fazla otomasyon kuralı oluşturulamaz (tavan 50). |
| API-8102 | 409 | ParkHesap tetikleyicili kural için önce ParkHesap entegrasyonu bağlanmalı. |
| API-8103 | 422 | Olay alanı kaynaklı bir değişken, bu olayın sözlüğünde olmayan bir alanı istiyor. |
| API-8104 | 422 | Tam belge adresi buton değişkenine bağlanamaz; buton için belge erişim kodu alanını kullanın. |
9xxx · Beklenmeyen hata
API-9001 (HTTP 500) bizim tarafımızdaki beklenmeyen bir hatadır. İç ayrıntı dışarı sızmaz ama yanıt bir request_id taşır ve aynı kimlik sunucu günlüğüne yazılır: destek kaydında o kimliği verin. Bu kod yeniden denenebilir.
Bilerek yapılmayanlar
Aşağıdakiler eksiklik değil karardır. Gerekçesiyle birlikte yazıyoruz ki entegratör olmayan bir şeyi aramakla vakit kaybetmesin.
- Meta'nın uçları proxy'lenmedi.
POST /v1/messageskendi gönderim yolunu kurmaz; ortak servisi çağırır. Bir gönderim noktası ortak yoldan çıkarsa dört şeyi birden kaybeder: kota kapısı çalışmaz, mesaj gelen kutusunda görünmez, ücretlendirme kaydı yazılmaz ve teslimat bildirimi eşleşecek bir kimlik bulamaz. - Numara bağlama sunucudan sunucuya yapılamaz. Meta'nın Embedded Signup akışı bir tarayıcı akışıdır: kurumun kendi Meta hesabıyla giriş yapması ve numarayı doğrulaması gerekir. Böyle bir onay API'den vekâleten verilemez. Çözüm tek kullanımlık bağlama bağlantısıdır (
POST /v1/tenantsyanıtındakiconnect_link, 7 gün geçerli). - Şablon ve kampanya medyası API'den yüklenemez. Medya tanıtıcısı yalnız panelin yeniden başlatılabilir yükleme akışından alınabilir; kampanya medyası ise kampanyanın kendisine aittir (tek dosya, tek Meta kimliği) ve Meta kimliği 30 günde ölür. Medyalı kampanya panelden kurulur, başlatması API'den yapılabilir. Karusel şablonlar da kapsam dışıdır.
- «Olay → şablon adı» kısayolu yok. API'den kural kurmak, panelin kurduğu her şeyi söylemektir: tetikleyici, koşullar, bekleme süresi ve bütün yuvaları kapatan bağlamalar. Şablon adı tek başına mesaj üretmez ve kuralın koşullarını düşürerek baypas etmenin yolu yoktur.
- Tepki kaldırma ucu yok. Meta gönderim tarafı için bir «kaldır» gövdesi dokümante etmedi. Boş
emojisessizce yutulmaz,API-2003alır. - Yazıyor göstergesi ucu yok. Gösterge bir mesaj değildir, saklanmaz ve 25 saniyede söner.