Dokumentasi resmi pengiriman WhatsApp Template disetujui Meta (`APPROVED`) & Callback Receiver untuk Pihak Ketiga (Third-Party Developers).
APPROVED di Meta Dashboard./send/text, /send/image, /send/document) hanya dapat digunakan jika nomor tujuan telah membalas/mengirim pesan dalam 24 jam terakhir.REST API **KTWA - WhatsApp Gateway** menggunakan protokol HTTP/HTTPS standar dengan format payload JSON yang meneruskan pesan secara real-time ke Meta Cloud API.
https://ktwa.kurniahost.biz.id/api/v1/
Setiap request dari Pihak Ketiga wajib menyertakan 3 HTTP Header berikut:
| Header Name | Type | Keterangan |
|---|---|---|
Content-Type |
string |
Harus diisi: application/json |
X-API-Key |
string |
API Key publik milik Client API Anda. |
X-API-Secret |
string |
API Secret rahasia milik Client API Anda. |
Content-Type: application/json
X-API-Key: YOUR_API_KEY_HERE
X-API-Secret: YOUR_API_SECRET_HERE
Wajib digunakan untuk notifikasi transaksi/pesan pertama ke pelanggan (Template disetujui Meta).
{
"phone": "6281234567890",
"template_name": "order_confirmation",
"language": "id",
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Budi Santoso" },
{ "type": "text", "text": "INV-2026-001" },
{ "type": "text", "text": "Rp 150.000" }
]
}
]
}
{
"success": true,
"data": {
"id": 1052,
"wa_message_id": "wamid.HBgLNjgxMjM0...",
"recipient": "6281234567890",
"status": "sent"
},
"message": "Pesan berhasil dikirim ke Meta API"
}
Hanya berlaku dalam rentang 24 jam setelah pelanggan membalas pesan Anda.
{
"phone": "6281234567890",
"message": "Terima kasih telah menghubungi CS kami."
}
{
"phone": "6281234567890",
"document_url": "https://example.com/invoice-1001.pdf",
"filename": "Invoice_1001.pdf",
"caption": "Terlampir invoice transaksi Anda."
}
{
"success": true,
"data": {
"id": 1052,
"wa_message_id": "wamid.HBgLNjgxMjM0...",
"recipient": "6281234567890",
"status": "read"
},
"message": "Detail pesan berhasil diambil"
}
<?php
$apiUrl = 'https://ktwa.kurniahost.biz.id/api/v1/send/template';
$apiKey = 'YOUR_API_KEY_HERE';
$apiSecret = 'YOUR_API_SECRET_HERE';
$payload = [
'phone' => '6281234567890',
'template_name' => 'order_confirmation', // Wajib disetujui Meta
'language' => 'id',
'components' => [
[
'type' => 'body',
'parameters' => [
['type' => 'text', 'text' => 'Budi Santoso'],
['type' => 'text', 'text' => 'INV-2026-001'],
['type' => 'text', 'text' => 'Rp 150.000']
]
]
]
];
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $apiKey,
'X-API-Secret: ' . $apiSecret,
],
CURLOPT_POSTFIELDS => json_encode($payload)
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);
?>
<?php
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://ktwa.kurniahost.biz.id/api/v1/',
'headers' => [
'Content-Type' => 'application/json',
'X-API-Key' => 'YOUR_API_KEY_HERE',
'X-API-Secret' => 'YOUR_API_SECRET_HERE',
]
]);
$response = $client->post('send/template', [
'json' => [
'phone' => '6281234567890',
'template_name' => 'payment_receipt',
'language' => 'id',
'components' => [
[
'type' => 'body',
'parameters' => [
['type' => 'text', 'text' => 'Ahmad Sujadi'],
['type' => 'text', 'text' => 'LUNAS']
]
]
]
]
]);
$data = json_decode($response->getBody(), true);
print_r($data);
?>
const API_URL = 'https://ktwa.kurniahost.biz.id/api/v1/send/template';
const API_KEY = 'YOUR_API_KEY_HERE';
const API_SECRET = 'YOUR_API_SECRET_HERE';
async function sendWhatsAppTemplate(phone, name, invoiceNo) {
try {
const res = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
'X-API-Secret': API_SECRET
},
body: JSON.stringify({
phone: phone,
template_name: 'order_confirmation',
language: 'id',
components: [
{
type: 'body',
parameters: [
{ type: 'text', text: name },
{ type: 'text', text: invoiceNo }
]
}
]
})
});
const data = await res.json();
console.log('Response:', data);
} catch (err) {
console.error('Error:', err);
}
}
sendWhatsAppTemplate('6281234567890', 'Budi Santoso', 'INV-2026-001');
const axios = require('axios');
const waApi = axios.create({
baseURL: 'https://ktwa.kurniahost.biz.id/api/v1/',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'YOUR_API_KEY_HERE',
'X-API-Secret': 'YOUR_API_SECRET_HERE'
}
});
async function sendOtp(phone, code) {
const res = await waApi.post('send/template', {
phone: phone,
template_name: 'otp_verification',
language: 'id',
components: [
{
type: 'body',
parameters: [{ type: 'text', text: code }]
}
]
});
console.log(res.data);
}
sendOtp('6281234567890', '849201');
KTWA Gateway akan mengirimkan callback `POST` JSON saat status pengiriman template berubah (*sent*, *delivered*, *read*, *failed*).
{
"event": "message_status",
"data": {
"message_id": 1052,
"wa_message_id": "wamid.HBgLNjgxMjM0...",
"recipient": "6281234567890",
"status": "delivered",
"timestamp": "2026-07-26 08:30:00",
"error_code": null,
"error_message": null
}
}
<?php
$json = file_get_contents('php://input');
$payload = json_decode($json, true);
if ($payload && isset($payload['event']) && $payload['event'] === 'message_status') {
$data = $payload['data'];
$status = $data['status'];
// Update status di database internal Anda:
// updateDatabase($data['wa_message_id'], $status);
http_response_code(200);
echo json_encode(['status' => 'OK']);
exit;
}
?>
app.post('/api/wa-callback', (req, res) => {
const { event, data } = req.body;
if (event === 'message_status') {
console.log(`Status ${data.recipient}: ${data.status}`);
return res.status(200).json({ status: 'OK' });
}
res.status(400).end();
});
| HTTP Status | Error Message | Penjelasan & Solusi |
|---|---|---|
| 400 | Meta API error |
Template belum disetujui Meta (`APPROVED`) atau jumlah parameter `components` tidak cocok dengan template. |
| 401 | API Key dan Secret diperlukan |
Periksa apakah header X-API-Key dan X-API-Secret sudah dikirimkan dengan benar. |
| 422 | Parameter "phone" dan "template_name" wajib diisi |
Payload JSON kurang atau nomor tujuan tidak menggunakan format 628xxx. |
| 429 | Rate limit exceeded |
Jumlah request melebihi limit per menit yang ditentukan. |
| 503 | Akun WhatsApp tidak aktif |
Nomor WhatsApp pengirim pada akun Anda sedang non-aktif. |