Tüm mesajlar tek bir uçtan gönderilir: POST /messages. Mesaj türü, gövdedeki alanlarla belirlenir. Bu sayfa her türün nasıl gönderileceğini ve hangi kuralların geçerli olduğunu anlatır.
#24 saatlik müşteri hizmet penceresi
WhatsApp'ın temel kuralı şudur: müşteri size yazdığı andan itibaren 24 saat boyunca serbest mesaj (metin, medya, konum, kişi kartı, tepki, form) gönderebilirsiniz. Pencere kapalıyken yalnız Meta onaylı bir şablon gönderebilirsiniz.
| Durum | Gönderebilecekleriniz |
|---|---|
| Müşteri son 24 saatte yazdı | Şablon dahil tüm türler |
| Müşteri son 24 saatte yazmadı | Yalnız şablon |
Pencere kapalıyken serbest mesaj 422 meta_send_failed ve meta_code: 131047 döner.
#Ortak alanlar
| Alan | Zorunlu | Açıklama |
|---|---|---|
phone_number_id | Evet | Gönderen numaranızın Meta kimliği (GET /numbers). |
to | Evet (ya da recipient) | Alıcı, ülke koduyla ve + olmadan (ör. 905550000000). |
recipient | to yoksa | Telefonu görünmeyen kullanıcının kimliği (BSUID). Eş anlamlıları: recipient_id, bsuid. Bkz. Kullanıcı adı ve BSUID. |
type | Türe göre | template, text, image, video, audio, document, sticker, location, contacts, reaction, flow, interactive. |
context | Hayır | Alıntılı yanıt: { "message_id": "wamid..." }. |
biz_opaque_callback_data | Hayır | En fazla 512 karakter; durum olaylarında aynen döner. |
Başarılı gönderim 201 Created ve aşağıdaki yanıtı döner. Canlı anahtarda her başarılı gönderim 1 kredi düşer.
{
"ok": true,
"id": 48213,
"wamid": "wamid.HBgMOTA1NTUxMjM0NTY3FQIAERgSMUI4QzY3RDFFNzBBNDlGRTlBAA==",
"to": "905550000000",
"type": "template",
"status": "sent",
"mode": "live",
"simulated": false,
"credits_used": 1,
"balance": 9875
}status: "sent" mesajın Meta'ya kabul edildiğini gösterir. Teslim ve okunma message.status.delivered ve message.status.read olaylarıyla gelir. Bkz. Webhooklar.#Şablon mesajı
Onaylı bir şablonla gönderin. Değişkenler components içinde verilir.
curl -X POST https://api.verimerkezi.app/wa/messages \
-H "Authorization: Bearer $VM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "template",
"template": {
"name": "siparis_onayi",
"language": { "code": "tr" },
"components": [
{ "type": "body", "parameters": [
{ "type": "text", "text": "Ahmet" },
{ "type": "text", "text": "VM-2026-1234" }
] }
]
}
}'const { VeriMerkeziClient } = require('./verimerkezi-client');
const vm = new VeriMerkeziClient(process.env.VM_API_KEY);
const r = await vm.sendTemplate('1234567890', '905550000000', 'siparis_onayi', 'tr', [
{ type: 'body', parameters: [{ type: 'text', text: 'Ahmet' }, { type: 'text', text: 'VM-2026-1234' }] },
]);
console.log(r.wamid);import os
from verimerkezi import VeriMerkeziClient
vm = VeriMerkeziClient(os.environ['VM_API_KEY'])
r = vm.send_template('1234567890', '905550000000', 'siparis_onayi', 'tr', [
{'type': 'body', 'parameters': [{'type': 'text', 'text': 'Ahmet'}, {'type': 'text', 'text': 'VM-2026-1234'}]}
])
print(r['wamid'])<?php
require 'vendor/autoload.php';
$vm = new VeriMerkezi\VeriMerkeziClient(getenv('VM_API_KEY'));
$r = $vm->sendTemplate('1234567890', '905550000000', 'siparis_onayi', 'tr', [
['type' => 'body', 'parameters' => [['type' => 'text', 'text' => 'Ahmet'], ['type' => 'text', 'text' => 'VM-2026-1234']]],
]);
echo $r['wamid'];languagealanı"tr"ya da{ "code": "tr" }olabilir. Şablonun onaylandığı dil kodunu birebir gönderin.- Şablon, gönderen numaranın bağlı olduğu işletme hesabında (WABA)
APPROVEDolmalıdır; aksi hâlde422 template_not_in_wabadöner.
#Medya başlıklı şablon
{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "template",
"template": {
"name": "fatura_bildirimi",
"language": { "code": "tr" },
"components": [
{ "type": "header", "parameters": [
{ "type": "document", "document": { "link": "https://cdn.example.com/fatura.pdf", "filename": "fatura-ekim.pdf" } }
] },
{ "type": "body", "parameters": [
{ "type": "text", "text": "Ahmet" },
{ "type": "text", "text": "₺1.250,00" }
] }
]
}
}#Dinamik URL butonu
Şablondaki URL butonunun adresi {{1}} ile bitiyorsa, değişken kısmı her gönderimde verin:
{
"type": "button",
"sub_type": "url",
"index": 0,
"parameters": [{ "type": "text", "text": "d32eec6c,5cbd62f6" }]
}index butonun sıfır tabanlı sırasıdır. Meta kuralı gereği URL butonunda tek değişken olur ve adresin sonunda yer alır.
#Doğrulama kodu (OTP)
AUTHENTICATION kategorisindeki şablonlar için yalnız kodu vermeniz yeterlidir; gövde ve kod butonu otomatik kurulur:
{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "template",
"template": { "name": "otp_dogrulama", "language": { "code": "tr" }, "otp": "482913" }
}Kodu siz üretir ve siz doğrularsınız; Veri Merkezi yalnız kodu WhatsApp ile iletir.
#Metin mesajı
curl -X POST https://api.verimerkezi.app/wa/messages \
-H "Authorization: Bearer $VM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "phone_number_id": "1234567890", "to": "905550000000", "type": "text", "text": { "body": "Merhaba Ahmet Bey, siparişiniz kargoya verildi." } }'await vm.sendText('1234567890', '905550000000', 'Merhaba Ahmet Bey, siparişiniz kargoya verildi.');vm.send_text('1234567890', '905550000000', 'Merhaba Ahmet Bey, siparişiniz kargoya verildi.')<?php
$vm->sendText('1234567890', '905550000000', 'Merhaba Ahmet Bey, siparişiniz kargoya verildi.');text alanı { "body": "..." } nesnesi ya da düz metin olabilir. Metindeki bağlantının önizlemesini göstermek için { "body": "...", "preview_url": true } gönderin; preview_url gönderilmezse önizleme kapalıdır.
#Alıntılı yanıt
Herhangi bir mesaja context ekleyerek belirli bir mesajı yanıtlayın. Alıcıda mesaj alıntı balonuyla görünür.
{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "text",
"text": { "body": "Tabii, hemen bakıyorum." },
"context": { "message_id": "wamid.HBgMOTA1NTUxMjM0NTY3FQIAEhggQTNFQkQ2RjQ3MEIyQzk0RTE4AA==" }
}message_id, webhook'ta gelen data.wamid değeridir. Geçersizse 422 invalid_context döner.
#Medya mesajı
Görsel, video, ses, belge ve çıkartma gönderebilirsiniz. Kaynak olarak herkese açık bir https:// adresi (link) ya da POST /media ile aldığınız kimliği (id) verin.
{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "document",
"document": {
"link": "https://cdn.example.com/fatura-2026-10.pdf",
"filename": "fatura-2026-10.pdf",
"caption": "Ekim ayı faturanız ektedir."
}
}| Tür | caption | filename | Diğer |
|---|---|---|---|
image | Evet | — | JPEG, PNG; en fazla 5 MB |
video | Evet | — | MP4, 3GPP; en fazla 16 MB |
audio | — | — | voice: true sesli not; en fazla 16 MB |
document | Evet | Evet | PDF, Office, TXT; en fazla 100 MB |
sticker | — | — | WebP; en fazla 500 KB |
Ayrıntı ve sesli not: Medya.
#Tepki
{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "reaction",
"reaction": { "message_id": "wamid.HBgMOTA1NTUxMjM0NTY3FQIAEhggQTNFQkQ2RjQ3MEIyQzk0RTE4AA==", "emoji": "👍" }
}- Boş
emoji("") önceki tepkiyi kaldırır. - Tepki mesajı
contextile birlikte gönderilemez.
#Form mesajı
Yayınlanmış bir WhatsApp formunu (Flow) gönderin. Yanıt flow.completed olayıyla gelir.
{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "flow",
"flow": {
"flow_id": "1234567890123456",
"flow_token": "crm-basvuru-42",
"cta": "Formu Aç",
"body": "Başvurunuzu 1 dakikada tamamlayın.",
"screen": "WELCOME",
"data": { "ad": "Ahmet" }
}
}Ayrıntı: Formlar.
#Okundu işareti ve yazıyor göstergesi
Gelen bir mesajı okundu olarak işaretleyin ve isteğe bağlı olarak alıcıda yaklaşık 25 saniye "yazıyor…" gösterin. Kredi düşmez.
curl -X POST https://api.verimerkezi.app/wa/messages/read \
-H "Authorization: Bearer $VM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "phone_number_id": "1234567890", "message_id": "wamid.HBgMOTA1NTUxMjM0NTY3FQIAEhggQTNFQkQ2RjQ3MEIyQzk0RTE4AA==", "typing": true }'Botunuz yanıtı göndermeden hemen önce çağırın.
#Kaçan mesajları geri alın
Webhook teslimi kaçarsa GET /messages ile bir numaranın mesaj kayıtlarını eskiden yeniye okuyun (en fazla 90 gün geriye, kredi düşmez):
curl "https://api.verimerkezi.app/wa/messages?phone_number_id=1234567890&since=2026-10-09T00:00:00Z&limit=100" \
-H "Authorization: Bearer $VM_API_KEY"#Konum mesajı
Haritada açılan bir konum gönderin. latitude (−90…90) ve longitude (−180…180) zorunludur; sayı ya da sayısal metin olabilir. name ve address isteğe bağlıdır, en fazla 1000 karakter.
curl -X POST https://api.verimerkezi.app/wa/messages \
-H "Authorization: Bearer $VM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "location",
"location": { "latitude": 41.0082, "longitude": 28.9784, "name": "Merkez Ofis", "address": "Örnek Mah. Örnek Cad. No:1, İstanbul" }
}'Hatalı değerde 422 invalid_location döner; error.field hatalı alanı gösterir.
#Kişi kartı
Bir ya da birden çok kişi kartı (en çok 20) gönderin. Her kartta name.formatted_name zorunludur. phones[].wa_id verirseniz alıcı kartta Mesaj gönder ve Kişiyi kaydet düğmelerini görür.
curl -X POST https://api.verimerkezi.app/wa/messages \
-H "Authorization: Bearer $VM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "contacts",
"contacts": [{
"name": { "formatted_name": "Destek Hattı", "first_name": "Destek", "last_name": "Hattı" },
"phones": [{ "phone": "+905550000000", "type": "WORK", "wa_id": "905550000000" }],
"emails": [{ "email": "[email protected]", "type": "WORK" }]
}]
}'Hatalı kartta 422 invalid_contacts döner (ör. field: contacts[0].name.formatted_name). Konum ve kişi kartı serbest mesajdır: 24 saatlik pencere, 1 kredi ve test anahtarında simülasyon kuralları aynen geçerlidir.
#Etkileşimli mesajlar
type: "interactive" ile yanıt düğmeleri (button, en çok 3), liste (list, en çok 10 satır), bağlantı düğmesi (cta_url) ve konum isteği (location_request_message) gönderin. Gövde Meta Cloud API ile aynıdır; Meta sınırları gönderimden önce doğrulanır.
curl -X POST https://api.verimerkezi.app/wa/messages \
-H "Authorization: Bearer $VM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "1234567890",
"to": "905550000000",
"type": "interactive",
"interactive": {
"type": "button",
"body": { "text": "Yarın 14:00 randevunuzu onaylıyor musunuz?" },
"action": { "buttons": [
{ "type": "reply", "reply": { "id": "onay", "title": "Onaylıyorum" } },
{ "type": "reply", "reply": { "id": "degistir", "title": "Saati değiştir" } }
] }
}
}'| Tür | Sınırlar |
|---|---|
button | 1–3 düğme; reply.title ≤ 20, reply.id ≤ 256 (benzersiz). body.text ≤ 1024, footer.text ≤ 60. |
list | action.button ≤ 20; 1–10 bölüm, toplam 1–10 satır; satır title ≤ 24, description ≤ 72. body.text ≤ 4096. |
cta_url | display_text ≤ 20; url http(s):// ile başlar (≤ 2000). Dokunma olay üretmez. |
location_request_message | Yalnız body.text (≤ 1024). Yanıt message.received type: location olarak gelir. |
Hatalı gövdede 422 invalid_interactive döner; error.field hatalı alanı gösterir (ör. interactive.action.buttons[1].reply.title). Müşterinin seçimi message.received olayında data.interactive.reply_id, reply_title ve reply_description alanlarıyla gelir.
