Entegrasyonlar İçin Tasarlanmış WhatsApp REST API'si ve Webhook'lar
Şablon ve mesaj gönderin; kişileri, etiketleri ve sohbetleri senkronize edin; her yanıt ve teslim bildirimi için imzalı bir webhook alın. Resmi WhatsApp Business Platformu üzerinde, kapsamlı API anahtarlarıyla /api/v1 adresinde sade REST. Kurulacak SDK yok. API, ayda 100 API mesajı içeren Developer Free’den başlayarak her pakettedir.
✦ 14 günlük ücretsiz deneme · Kendi WhatsApp Business numaranız · Mesajları Meta doğrudan faturalandırır
Tüm WhatsApp CRM'iniz İçin Tek Bir REST API
Bir çalışma alanı API anahtarıyla (Authorization: Bearer sm_live_…) kimlik doğrulayın ve /api/v1 adresini çağırın. Şablon, metin, medya ve etkileşimli mesajlar gönderin; kişileri oluşturun, güncelleyin ve etiketleyin; etiketleri, satış hattı aşamalarını, şablonları ve kampanyaları okuyun; sohbetleri listeleyin, atayın ve çözün; webhook uç noktalarını yönetin; satışları dönüşüm olarak bildirin. Her başarılı yanıt aynı {data, meta} zarfını, her hata ise üzerine karar verebileceğiniz sabit bir kodu kullanır.
- Anahtarlar Geliştiriciler → API anahtarları bölümünde oluşturulur ve yalnızca bir kez gösterilir
- Tam erişim, Yalnızca gönderme, Yalnızca okuma ve Özel ön ayarlarıyla 10 kapsam
- Mesaj ücretlerini Meta doğrudan kendi WhatsApp Business Hesabınıza faturalar; API hiçbir zaman mesaj başına ücret almaz
Güvenebileceğiniz İmzalı Webhook'lar
Bir HTTPS uç noktasını 12 olaydan herhangi birine abone edin: alınan, gönderilen, teslim edilen, okunan ve başarısız mesajlar; oluşturulan, abonelikten çıkan ve yeniden abone olan kişiler; atanan ve çözülen sohbetler; şablon durum değişiklikleri; ve tamamlanan kampanyalar. Her teslimat X-SM-Signature başlığında HMAC-SHA256 ile imzalanır, 1dk, 5dk, 30dk, 2sa, 6sa ve 12sa sonra yeniden denenir ve yeniden gönderebileceğiniz bir teslimat günlüğünde listelenir.
- En az bir kez teslim edilir: olay kimliğine göre yinelenenleri ayıklayın
- Sürekli başarısız olan uç noktalar (art arda 50 kez veya 24 saat boyunca) kapatılır ve işaretlenir
- İmzalama sırrını 24 saatlik çakışmayla değiştirin
Entegrasyonların Dayandığı Ayrıntılar
-
İdempotent gönderimler
POST isteğinde bir Idempotency-Key gönderin. Aynı anahtar ve gövde 24 saat içinde orijinal yanıtı Idempotent-Replayed: true ile döndürür; böylece yeniden deneme asla iki kez göndermez.
-
İmleçli sayfalama ve senkronizasyon
Listeler limit (1–100) ve cursor alır ve nextCursor döndürür. Kişilerde ve sohbetlerde updatedSince, artımlı senkronizasyon için yalnızca değişenleri en eskiden başlayarak döndürür.
-
Kendi referanslarınız
Bir gönderime sipariş numaranız gibi bir clientReference ekleyin. Yanıtta, GET /messages/{id} üzerinde ve her gönderildi, teslim edildi, okundu ve başarısız webhook'unda geri gelir.
-
En az yetkili anahtarlar
Her entegrasyona yalnızca ihtiyaç duyduğu kapsamlara sahip kendi anahtarını verin. Bir anahtarla oluşturulan webhook, bir olay ailesini yalnızca o anahtar ilgili okuma kapsamına sahip olduğu sürece alır.
-
İstek ve teslimat günlükleri
Panelde son 7 gündeki son 500 API isteğini anahtara göre veya yalnızca hatalar olarak filtreleyerek görün; herhangi bir webhook'u uç noktasının teslimat günlüğünden yeniden gönderin.
-
OpenAPI referansı
/api/v1/openapi.json adresinde bir OpenAPI JSON belgesi ve /api/v1/docs adresinde Swagger UI; her işlemin kapsamı x-scope olarak listelenir. Aynı referans panelde Geliştiriciler bölümünde de bulunur.
API'nin Kapsadıkları
Tüm yollar /api/v1 altındadır. Şablon ve kampanya oluşturma şimdilik yalnızca panelden yapılır: API bunları okur ve onaylı şablonları her seferinde tek mesaj olarak gönderir.
| Kaynak | Uç noktalar | Kapsam | Ne yapar |
|---|---|---|---|
| Hesap | GET /me, GET /phone-numbers | Geçerli herhangi bir anahtar | Çalışma alanı, plan, anahtar kapsamları, limitler ve anlık kullanım; kalite puanı ve mesajlaşma limitiyle numaralarınız |
| Mesajlar | POST /messages, GET /messages/{id} | messages:send, messages:read | Şablon (karuseller ve süreli teklifler dahil), metin, görsel, video, ses, belge ve etkileşimli mesajlar (etkileşimli mesajlar Team veya Business gerektirir); durum, hata nedeni ve Meta’nın fiyat kategorisi |
| Kişiler | GET/POST /contacts, GET/PATCH/DELETE /contacts/{idOrPhone} | contacts:read, contacts:write | Telefon numarasına göre oluşturma veya güncelleme, etiketler, özel öznitelikler, izin verme ve izni geri çekme; bir kişiyi satış hattı aşamasına koymak Business gerektirir |
| Etiketler ve satış hattı | GET /tags, GET /pipeline-stages | contacts:read | Kişi sayılarıyla etiketler; lead satış hattı aşamalarınız |
| Şablonlar | GET /templates, GET /templates/{id} | templates:read | Yalnızca okuma: şablonlar panelde oluşturulur ve Meta tarafından incelenir |
| Sohbetler | GET /conversations, GET /conversations/{id}, GET /conversations/{id}/messages, PATCH /conversations/{id} | conversations:read, conversations:write | Mesaj geçmişi ve windowOpen; atama, atamayı kaldırma, çözme ve yeniden açma (okuma her pakette çalışır; değişiklikler Team veya Business gerektirir) |
| Kampanyalar | GET /campaigns, GET /campaigns/{id} | campaigns:read | Yalnızca okunabilir istatistikler; kampanyalar panelde oluşturulur ve başlatılır |
| Dönüşümler | POST /conversions | conversions:write | Müşteriyi getiren kampanyanın payını alması için bir satışı bildirin (Business) |
| Webhook'lar | GET/POST /webhooks, DELETE /webhooks/{id} | webhooks:manage | Uç noktaları abone etme ve abonelikten çıkarma (REST hooks) |
Plana Göre API Limitleri
Anahtar başına değil, çalışma alanı başına sayılır. Sayılan yanıtlar RateLimit-* başlıklarını, her 429 yanıtı ise Retry-After başlığını taşır. Aylık API mesajlarına yalnızca API ile gönderilen mesajlar sayılır; kota aşıldığında gönderim 402 PLAN_MESSAGE_LIMIT_REACHED alır. Meta’nın numaranız için koyduğu mesajlaşma limiti de geçerlidir.
| Plan | İstek / dakika | API gönderimi / saat | API mesajı / ay | Webhook uç noktaları |
|---|---|---|---|---|
| Ücretsiz deneme (14 gün) | 300 | 100 | 1.000 | 5 |
| Developer Free | 60 | 2.000 | 100 | 2 |
| Developer 1K | 120 | 10.000 | 1.000 | 2 |
| Developer 10K | 300 | 10.000 | 10.000 | 2 |
| Developer 100K | 1.200 | 10.000 | 100.000 | 2 |
| Team | 300 | 10.000 | Sınırsız (adil kullanım) | 1 |
| Business | 1.200 | 10.000 | Sınırsız (adil kullanım) | 5 |
Yalnızca API mi? Bir Developer Boyutu Seçin
Developer paketleri, salt görüntülenen bir panelle birlikte API’dir: 1 koltuk, 2 webhook uç noktası ve burada gösterilen aylık API mesajları. Fiyatlara GST dahildir ve bir yıl on ay fiyatınadır. Ücretli boyutlar kimlik doğrulama şablonlarını asla durdurmaz.
| Plan | Fiyat / ay | API mesajı / ay | İstek / dakika |
|---|---|---|---|
| Developer Free | ₹0 | 100 | 60 |
| Developer 1K | ₹100 | 1.000 | 120 |
| Developer 10K | ₹499 | 10.000 | 300 |
| Developer 100K | ₹999 | 100.000 | 1.200 |
Anahtarınızı kontrol edin, ardından bir şablon gönderin
GET /me kapsam gerektirmez. Gönderim için messages:send ve onaylı bir şablon gerekir.
# 1. Check your key: workspace, plan, scopes, limits and usage
curl https://waba-api.startmessaging.com/api/v1/me \
-H "Authorization: Bearer sm_live_your_api_key"
# 2. Send an approved template, safe to retry with the same Idempotency-Key
curl -X POST https://waba-api.startmessaging.com/api/v1/messages \
-H "Authorization: Bearer sm_live_your_api_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-10421-ready" \
-d '{
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
}' const API = 'https://waba-api.startmessaging.com/api/v1';
const auth = { Authorization: 'Bearer sm_live_your_api_key' };
// 1. Check your key
const me = await fetch(`${API}/me`, { headers: auth }).then((r) => r.json());
console.log(me.data.plan.code, me.data.apiKey.scopes, me.data.limits);
// 2. Send an approved template, safe to retry with the same Idempotency-Key
const res = await fetch(`${API}/messages`, {
method: 'POST',
headers: {
...auth,
'Content-Type': 'application/json',
'Idempotency-Key': 'order-10421-ready',
},
body: JSON.stringify({
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
}),
});
const { data } = await res.json();
console.log(data.id, data.status, res.headers.get('Idempotent-Replayed')); import requests
API = 'https://waba-api.startmessaging.com/api/v1'
AUTH = {'Authorization': 'Bearer sm_live_your_api_key'}
# 1. Check your key
me = requests.get(f'{API}/me', headers=AUTH).json()['data']
print(me['plan']['code'], me['apiKey']['scopes'], me['limits'])
# 2. Send an approved template, safe to retry with the same Idempotency-Key
res = requests.post(
f'{API}/messages',
headers={**AUTH, 'Idempotency-Key': 'order-10421-ready'},
json={
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
},
)
data = res.json()['data']
print(data['id'], data['status']) <?php
$api = 'https://waba-api.startmessaging.com/api/v1';
$auth = 'Authorization: Bearer sm_live_your_api_key';
// 1. Check your key
$ch = curl_init("$api/me");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [$auth],
]);
$me = json_decode(curl_exec($ch), true)['data'];
echo $me['plan']['code'] . ' ' . implode(',', $me['apiKey']['scopes']) . "\n";
// 2. Send an approved template, safe to retry with the same Idempotency-Key
$body = <<<'JSON'
{
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
}
JSON;
$ch = curl_init("$api/messages");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
$auth,
'Content-Type: application/json',
'Idempotency-Key: order-10421-ready',
],
CURLOPT_POSTFIELDS => $body,
]);
$data = json_decode(curl_exec($ch), true)['data'];
echo $data['id'] . ' ' . $data['status']; Her webhook'u güvenmeden önce doğrulayın
X-SM-Signature değeri v1=HMAC-SHA256(secret, timestamp + "." + ham gövde) şeklindedir. Anahtar olarak whsec_ sırrının tamamını kullanın.
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
const app = express();
const SECRET = process.env.SM_WEBHOOK_SECRET; // the whole whsec_… string
function verify(rawBody, headers) {
const ts = headers['x-sm-timestamp'];
const sigs = headers['x-sm-signature'];
if (!/^\d+$/.test(ts ?? '') || typeof sigs !== 'string') return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // 5-minute tolerance
const expected = createHmac('sha256', SECRET).update(`${ts}.`).update(rawBody).digest();
return sigs.split(',').some((part) => {
const m = /^v1=([0-9a-f]{64})$/.exec(part.trim());
return m !== null && timingSafeEqual(Buffer.from(m[1], 'hex'), expected);
});
}
app.post('/webhooks/startmessaging', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.body, req.headers)) return res.sendStatus(401);
res.sendStatus(200); // answer 2xx within 5 seconds, then do the work
const event = JSON.parse(req.body);
// Delivered at least once: skip event.id if you have already handled it.
if (event.type === 'message.delivered') {
console.log(event.data.message.clientReference, 'delivered');
}
}); import hmac, hashlib, json, os, time
from flask import Flask, request
app = Flask(__name__)
SECRET = os.environ['SM_WEBHOOK_SECRET'].encode() # the whole whsec_… string
def verify(raw_body, ts, sigs):
if not ts or not ts.isdigit() or not sigs:
return False
if abs(time.time() - int(ts)) > 300: # 5-minute tolerance
return False
expected = hmac.new(SECRET, f'{ts}.'.encode() + raw_body, hashlib.sha256).hexdigest()
return any(
hmac.compare_digest(part.strip()[3:], expected)
for part in sigs.split(',') if part.strip().startswith('v1=')
)
@app.post('/webhooks/startmessaging')
def startmessaging_webhook():
raw = request.get_data()
if not verify(raw, request.headers.get('X-SM-Timestamp'), request.headers.get('X-SM-Signature')):
return '', 401
event = json.loads(raw)
# Delivered at least once: skip event['id'] if you have already handled it.
if event['type'] == 'message.delivered':
print(event['data']['message']['clientReference'], 'delivered')
return '', 200 <?php
$secret = getenv('SM_WEBHOOK_SECRET'); // the whole whsec_… string
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_SM_TIMESTAMP'] ?? '';
$sigs = $_SERVER['HTTP_X_SM_SIGNATURE'] ?? '';
function sm_verify(string $raw, string $ts, string $sigs, string $secret): bool
{
if (!ctype_digit($ts) || $sigs === '') return false;
if (abs(time() - (int) $ts) > 300) return false; // 5-minute tolerance
$expected = hash_hmac('sha256', $ts . '.' . $raw, $secret);
foreach (explode(',', $sigs) as $part) {
$part = trim($part);
if (strncmp($part, 'v1=', 3) === 0 && hash_equals($expected, substr($part, 3))) {
return true;
}
}
return false;
}
if (!sm_verify($raw, $ts, $sigs, $secret)) {
http_response_code(401);
exit;
}
http_response_code(200); // answer 2xx within 5 seconds
$event = json_decode($raw, true);
// Delivered at least once: skip $event['id'] if you have already handled it.
if ($event['type'] === 'message.delivered') {
error_log($event['data']['message']['clientReference'] . ' delivered');
} Bir satışı bildirin, kampanyaya yazın
Business paketinde POST /api/v1/conversions, satışı dönüşüm pencereniz içinde (siz değiştirmedikçe 7 gün) o numaraya teslim edilen son kampanyaya yazar ve kampanya raporu tutarı toplar.
# Business plan, scope conversions:write
curl -X POST https://waba-api.startmessaging.com/api/v1/conversions \
-H "Authorization: Bearer sm_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "+919812345678",
"orderId": "ORD-10442",
"value": 2499.5,
"currency": "INR",
"occurredAt": "2026-10-12T10:30:00+05:30"
}'
# 201 = recorded now, 200 = this orderId was reported already const res = await fetch('https://waba-api.startmessaging.com/api/v1/conversions', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.SM_API_KEY, // sm_live_your_api_key
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone: '+919812345678',
orderId: 'ORD-10442', // the same orderId again never counts the sale twice
value: 2499.5,
currency: 'INR',
}),
});
const { data } = await res.json();
console.log(data.created, data.conversion.campaign?.name ?? 'no campaign credited'); import os, requests
res = requests.post(
'https://waba-api.startmessaging.com/api/v1/conversions',
headers={'Authorization': f"Bearer {os.environ['SM_API_KEY']}"}, # sm_live_your_api_key
json={
'phone': '+919812345678',
'orderId': 'ORD-10442',
'value': 2499.5,
'currency': 'INR',
},
)
data = res.json()['data']
campaign = data['conversion']['campaign']
print(data['created'], campaign['name'] if campaign else 'no campaign credited') WhatsApp API — SSS
Bir SDK var mı?
Hayır. API, JSON ile HTTPS üzerinden sade REST'tir; bu yüzden HTTPS isteği yapabilen her dil veya araç çalışır. Belgeler cURL, Node.js, Python ve PHP örnekleri gösterir; OpenAPI belgesi ise her istek ve yanıt alanını açıklar.
Hangi planlar API'yi içerir?
Developer Free dahil her paket. Developer paketleri, aylık API mesajı sayısına göre boyutlanan, salt görüntülenen bir panelle birlikte API’dir: 100 mesajlı Developer Free, ardından 1K, 10K ve 100K. Team ve Business; ortak gelen kutusunu, kişileri ve kampanyaları ekler ve adil kullanım dahilinde aylık API mesaj sınırı yoktur. 14 günlük ücretsiz deneme 1.000 API mesajıyla API’yi içerir. Dakikadaki istek sayısı ve webhook uç noktaları pakete bağlıdır; GET /me paketinizi, limitlerinizi ve bu ayın sayısını gösterir.
API üzerinden şablon veya kampanya oluşturabilir miyim?
Henüz değil. Şablonlar panelde oluşturulur ve Meta tarafından incelenir; kampanyalar paneldeki kampanya sihirbazında hazırlanıp başlatılır. API ikisini de okur, onaylı şablonları POST /messages ile her seferinde tek mesaj olarak gönderir ve bir kampanya bittiğinde campaign.completed webhook'u ile bildirir.
API karusel ve süreli teklif gönderebilir mi?
Evet. Karusel şablonu için her kart için bir giriş içeren template.carouselCards’ı sırayla gönderin; her birinin kendi headerMediaUrl’si (https) ve isteğe bağlı parameters ile buttonParameters’ı olur. Süreli teklif için template.offerExpiresAt’i gelecekteki bir ISO 8601 zamanına ayarlayın; WhatsApp müşteriye bir geri sayım gösterir. Kampanyalar bu şablonları gönderemez; bu yüzden API’den veya gelen kutusundan tek tek gönderilirler.
Aylık API mesaj kotası bitince ne olur?
Developer paketleri ve ücretsiz deneme, ayın 1’inden itibaren sayılan belirli sayıda aylık API mesajı içerir; yalnızca API ile gönderilen mesajlar sayılır. Developer Free ve deneme kotalarında durur. Ücretli bir Developer paketi diğer mesajları boyutunun üç katında durdurur ve kimlik doğrulama şablonlarını asla durdurmaz. Limiti aşan bir gönderim, ayrıntılarında sent, stopAt ve resetsAt bulunan 402 PLAN_MESSAGE_LIMIT_REACHED alır; GET /me bu olmadan önce sayıyı gösterir. Team ve Business’ta aylık API mesaj sınırı yoktur (adil kullanım).
Zapier, Make veya Pabbly Connect'i nasıl bağlarım?
Bu pazar yerlerinde henüz resmi StartMessaging uygulaması yok. Genel modüllerini kullanın: olayları almak için catch-hook veya özel webhook tetikleyicisi, API'yi çağırmak için API anahtarınızla bir HTTP isteği eylemi. Kendi uygulamasını geliştirenler POST /webhooks ve DELETE /webhooks/{id} ile abone olup aboneliği kaldırabilir.
Bir gönderimi yeniden denersem ne olur?
Aynı Idempotency-Key ve aynı gövdeyle 24 saat içinde, orijinal yanıtı Idempotent-Replayed: true ile geri alırsınız ve hiçbir şey iki kez gönderilmez. Bir anahtarı farklı bir gövdeyle yeniden kullanmak IDEMPOTENCY_KEY_REUSED ile reddedilir. 429 veya 5xx sonrasında aynı anahtarla yeniden deneyin.
Hız limitleri nasıl çalışır?
Limitler çalışma alanı başınadır; bu yüzden daha fazla anahtar asla kapasite eklemez. Dakikadaki istek: Developer Free’de 60, Developer 1K’da 120, Developer 10K, Team ve ücretsiz denemede 300, Developer 100K ve Business’ta 1.200. Saatlik API gönderimi: ücretli paketlerde 10.000, Developer Free’de 2.000 ve denemede 100. Bir gönderim iki bütçeden de düşer ve reddedilen bir gönderim saatlik payını geri verir. 429 yanıtı Retry-After ile ne kadar bekleneceğini söyler. Meta’nın numaranız için koyduğu mesajlaşma limiti bunun üzerine uygulanır.
StartMessaging API mesajı başına ücret alıyor mu?
Hayır. StartMessaging bir Meta Tech Provider’dır: kendi WhatsApp Business Hesabınızı bağlarsınız ve Meta mesaj ücretlerini kendi fiyatlarıyla doğrudan bu hesaba fatura eder. Meta, 24 saatlik pencerenin içinde bile yardımcı şablonlar dahil teslim edilen her şablonu ve aylık ücretsiz kotayı aşan hizmet yanıtlarını ücretlendirir. StartMessaging cüzdan ve mesaj başına ek ücret olmadan sabit bir paket ücreti alır; Developer paketleri içerdikleri aylık API mesajı sayısına göre boyutlanır. Her mesajın pricing alanı, Meta’nın onu nasıl sınıflandırdığını gösterir.
WhatsApp Entegrasyonunuzu Kurun
İlk çalışma alanınız 14 günlük ücretsiz deneme alır; sonrasında Developer Free, ayda 100 mesajla API’yi açık tutar. Kendi WhatsApp Business numaranızı bağlayın, bir anahtar oluşturun ve ilk çağrınızı yapın.