---
title: "Kirim Pesan WhatsApp via API: Contoh cURL, Node, Python, PHP"
description: "Cara mengirim pesan WhatsApp dengan API: contoh cURL, Node.js, Python, dan PHP yang berfungsi untuk template dan pesan sesi, plus pelacakan status dan error."
canonical: "https://omnichannels.ai/id/blog/send-whatsapp-message-api-examples/"
language: "id"
updated: "2026-10-16"
publisher: "BroadNet Technologies"
brand: "OmniChannels.ai"
category: "integration"
primaryQuery: "kirim pesan whatsapp api"
keywords: ["contoh whatsapp api curl", "kirim template whatsapp node.js", "whatsapp api python", "whatsapp api php", "kirim pesan whatsapp business api", "pesan sesi whatsapp api", "whatsapp api multipart form-data"]
published: "2026-10-16"
author: "OmniChannels.ai Editorial Team"
reviewer: "BroadNet Messaging Solutions Team"
readingMinutes: 10
answer: "Dengan API OmniChannels.ai, Anda mengirim setiap pesan WhatsApp melalui satu endpoint, POST https://api.broadnet.me/whatsapp/message/sendbulk, sebagai multipart form data dengan header Authorization: Bearer. Field messageType memilih payload: TEMPLATE untuk template yang disetujui, yang Anda butuhkan untuk memulai percakapan atau menulis di luar jendela 24 jam, dan TEXT, IMAGE, atau INTERACTIVE untuk balasan di dalam jendela tersebut."
takeaways: ["Semua pesan WhatsApp melalui POST /whatsapp/message/sendbulk; messageType (TEMPLATE, TEXT, IMAGE, INTERACTIVE, dan lainnya) menentukan field mana yang wajib.", "Request-nya multipart/form-data, bukan JSON, jadi Python membutuhkan files= dan PHP membutuhkan body berupa array agar tidak ter-encode sebagai url-encoded.", "Di luar jendela layanan pelanggan 24 jam hanya template yang disetujui yang dapat dikirim; di dalamnya Anda dapat mengirim teks bebas, media, dan pesan interaktif.", "Panggilan ini mengantrekan pesan dan langsung mengembalikan batchId; status per penerima tiba di webhook Anda dan dari endpoint bulk-insights.", "API tidak menerima idempotency key, jadi catat setiap pengiriman sebelum Anda melakukannya dan periksa status setelah timeout alih-alih mengirim ulang."]
faq: 
  - q: "Apakah pesan WhatsApp dikirim sebagai JSON atau form data?"
    a: "Form data. Endpoint pengiriman WhatsApp, POST /whatsapp/message/sendbulk, menerima multipart/form-data, dengan setiap field sebagai bagian form. Pengiriman SMS tunggal memakai JSON, yang sering menjadi sumber kebingungan. Di Python kirim field melalui files=, bukan data=, dan di PHP berikan array sebagai body agar cURL membangun multipart."
  - q: "Bisakah saya mengirim pesan WhatsApp tanpa template?"
    a: "Hanya di dalam jendela layanan pelanggan. Saat pelanggan mengirim pesan kepada Anda, jendela 24 jam terbuka dan Anda dapat membalas dengan teks, media, atau pesan interaktif. Di luar jendela itu, atau untuk memulai percakapan, Anda harus mengirim template yang disetujui. API menegakkan aturan Meta, dan pengiriman teks bebas ke jendela yang tertutup akan gagal."
  - q: "Bagaimana saya tahu apakah pesan terkirim?"
    a: "Panggilan pengiriman hanya mengonfirmasi bahwa pesan telah diantrekan dan mengembalikan batchId. Status per penerima bergerak melalui SUBMITTED, SENT, DELIVERED, dan READ, atau berakhir sebagai FAILED atau REJECTED. Status dikirim (push) ke webhook Anda, dan Anda juga dapat membacanya per batch dari endpoint bulk-insights."
  - q: "Bisakah saya mengirim ke banyak penerima dalam satu request?"
    a: "Bisa. destinationNo menerima beberapa nomor yang dipisahkan koma, titik koma, atau baris baru, dan destinationFiles menerima file TXT, CSV, atau XLSX. Untuk template dengan variabel berbeda per penerima, unggah file yang dipersonalisasi dengan satu baris per penerima. Atur allowDuplicate ke false untuk menghapus nomor duplikat."
  - q: "Apakah ada idempotency key untuk mencegah pengiriman ganda?"
    a: "Tidak. API tidak menerima idempotency key. Jika request mengalami timeout, Anda tidak dapat mengetahui dari sisi klien apakah request diterima. Catat setiap pengiriman di database Anda sendiri sebelum memanggil, simpan batchId yang dikembalikan, dan periksa status atau daftar batch sebelum mengulang."
  - q: "Apakah saya perlu opt-in sebelum mengirim?"
    a: "Ya. Kebijakan Meta mensyaratkan setiap penerima telah opt-in untuk menerima pesan dari bisnis Anda, dan template harus disetujui sebelum dipakai. API tidak memeriksa persetujuan untuk Anda. Kumpulkan opt-in di sumbernya, simpan buktinya, dan tekan (suppress) siapa pun yang berhenti berlangganan."
sources: 
  - title: "WhatsApp Business Messaging Policy"
    url: "https://business.whatsapp.com/policy"
  - title: "Pricing on the WhatsApp Business Platform — Meta for Developers"
    url: "https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing"
  - title: "RFC 7578: Returning Values from Forms: multipart/form-data"
    url: "https://www.rfc-editor.org/rfc/rfc7578"
---

# Kirim Pesan WhatsApp via API: Contoh cURL, Node, Python, PHP

Canonical: https://omnichannels.ai/id/blog/send-whatsapp-message-api-examples/
Language: id
Publisher: OmniChannels.ai by BroadNet Technologies

Panduan ini ditujukan bagi developer yang mengintegrasikan pengiriman WhatsApp ke dalam aplikasi. Panduan ini menunjukkan request yang berfungsi dalam cURL, Node.js, Python, dan PHP, menjelaskan perbedaan pesan template dan pesan sesi, serta membahas pelacakan dan error. Setiap endpoint dan field berasal dari [referensi WhatsApp API](/developers/whatsapp/) dan spesifikasi OpenAPI kami.

## Bagaimana cara mengirim pesan WhatsApp melalui API?

Anda melakukan satu `POST` terautentikasi ke `https://api.broadnet.me/whatsapp/message/sendbulk` dengan field form. Field `messageType` memilih jenis pesan, dan responsnya mengonfirmasi bahwa pesan telah diantrekan.

| Item | Nilai |
|---|---|
| Base URL | `https://api.broadnet.me` (tanpa awalan `/v1`) |
| Endpoint | `POST /whatsapp/message/sendbulk` |
| Autentikasi | `Authorization: Bearer $BROADNET_TOKEN` |
| Content type | `multipart/form-data` (diatur otomatis oleh klien HTTP Anda) |
| Respons | Ringkasan batch JSON dengan `batchId` |

Kredensial uji diterbitkan setelah permintaan akses singkat, dan pengiriman uji dikirim ke nomor terverifikasi milik Anda sendiri. Lihat [quickstart](/developers/quickstart/) dan halaman autentikasi. Simpan token hanya di server Anda.

## Template atau pesan sesi: mana yang dikirim?

Kirim template untuk memulai percakapan atau menulis di luar jendela, dan pesan sesi jika pelanggan menulis kepada Anda dalam 24 jam terakhir. Aturan ini ditetapkan oleh Meta, bukan oleh kami.

| | Pesan template | Pesan sesi |
|---|---|---|
| `messageType` | `TEMPLATE` | `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`, `INTERACTIVE`, dan lainnya |
| Kapan diizinkan | Kapan saja, setelah template disetujui | Hanya saat jendela layanan pelanggan terbuka |
| Perlu persetujuan | Ya, oleh Meta | Tidak |
| Penggunaan umum | Pembaruan pesanan, OTP, pengingat, kampanye | Balasan dukungan, jawaban atas pertanyaan |

Meta menagih per pesan terkirim menurut kategori, dan aturannya untuk pesan layanan dan template utilitas (utility) berubah pada 1 Oktober 2026. [Panduan jendela](/id/blog/whatsapp-24-hour-customer-service-window/) kami membahas mekanismenya, dan [halaman harga](/id/whatsapp-pricing/) serta [kalkulator](/id/tools/whatsapp-pricing-calculator/) kami membahas biayanya.

## Bagaimana cara mengirim template yang disetujui?

Atur `messageType=TEMPLATE`, sebutkan nama template dan kode bahasanya, lalu berikan nilai variabel. Buat `batchId=0` untuk memulai batch baru.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F campaignName=order-updates \
  -F destinationNo=971500000000 \
  -F messageType=TEMPLATE \
  -F templateName=order_update \
  -F templateLanguage=en_US \
  -F bodyTemplateVariables=INV-400
```

```js
// Node.js 18+ (fetch dan FormData bawaan)
const form = new FormData();
form.append("batchId", "0");
form.append("campaignName", "order-updates");
form.append("destinationNo", "971500000000");
form.append("messageType", "TEMPLATE");
form.append("templateName", "order_update");
form.append("templateLanguage", "en_US");
form.append("bodyTemplateVariables", "INV-400");

const res = await fetch("https://api.broadnet.me/whatsapp/message/sendbulk", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.BROADNET_TOKEN}` }, // fetch mengatur boundary multipart
  body: form,
});
console.log(await res.json());
```

```python
import os
import requests

fields = {
    "batchId": "0",
    "campaignName": "order-updates",
    "destinationNo": "971500000000",
    "messageType": "TEMPLATE",
    "templateName": "order_update",
    "templateLanguage": "en_US",
    "bodyTemplateVariables": "INV-400",
}
# Kirim setiap field sebagai bagian multipart (files=), bukan url-encoded (data=).
r = requests.post(
    "https://api.broadnet.me/whatsapp/message/sendbulk",
    headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
    files={k: (None, v) for k, v in fields.items()},
    timeout=30,
)
print(r.json())
```

```php
<?php
$ch = curl_init("https://api.broadnet.me/whatsapp/message/sendbulk");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BROADNET_TOKEN")],
    // Array (bukan string) membuat cURL mengirim multipart/form-data.
    CURLOPT_POSTFIELDS => [
        "batchId" => "0",
        "campaignName" => "order-updates",
        "destinationNo" => "971500000000",
        "messageType" => "TEMPLATE",
        "templateName" => "order_update",
        "templateLanguage" => "en_US",
        "bodyTemplateVariables" => "INV-400",
    ],
]);
echo curl_exec($ch);
curl_close($ch);
```

Tujuan berupa kode negara ditambah nomor, hanya angka. Panggilan ini langsung kembali dengan ringkasan batch:

```json
{
  "totalFiles": 1,
  "totalSuccess": 1,
  "totalFailed": 0,
  "totalDuplicates": 0,
  "batchId": "2609091405000000001",
  "fileResults": [
    {
      "fileName": "direct",
      "totalRecords": 1,
      "successCount": 1,
      "failedRecipients": [],
      "duplicateRecipients": []
    }
  ],
  "message": "Messages queued for delivery"
}
```

Simpan `batchId` di samping catatan Anda sendiri. "Diantrekan" bukan "terkirim".

## Field request mana yang paling penting?

Field mana yang wajib bergantung pada `messageType`. Berikut yang umum.

| Field | Wajib | Deskripsi |
|---|---|---|
| `messageType` | Ya | `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `DOCUMENT`, `STICKER`, `LOCATION`, `CONTACTS`, `TEMPLATE`, `INTERACTIVE`, `REACTION`, atau `FLOW` |
| `destinationNo` atau `destinationFiles` | Salah satu dari keduanya | Nomor yang dipisahkan koma, titik koma, atau baris baru; atau file TXT, CSV, atau XLSX |
| `batchId` | Tidak | `0` memulai batch baru |
| `campaignName` | Tidak | Label Anda untuk pelaporan |
| `allowDuplicate` | Tidak | `false` menghapus penerima duplikat |
| `templateName`, `templateLanguage` | Untuk `TEMPLATE` | Nama template yang disetujui dan kode bahasanya, seperti `en_US` atau `ar` |
| `bodyTemplateVariables` | Sesuai template | Nilai variabel body; parameter bernama diberikan sebagai JSON |
| `buttonsTemplateVariable` | Sesuai template | Nilai tombol dinamis: akhiran URL, kode kupon, atau kode OTP |
| `content` | Untuk `TEXT` | Isi teks, hingga 4.096 karakter, atau caption untuk media |
| `mediaUrl` | Sesuai tipe | URL HTTPS publik dari media |
| `interactivePayload` | Untuk `INTERACTIVE` | Objek interaktif Meta sebagai string JSON |

Penjadwalan memakai `scheduled`, `scheduleTime`, dan `gmt`, dan `delay` mengatur laju pengiriman. Daftar lengkapnya ada di [referensi WhatsApp API](/developers/whatsapp/).

## Bagaimana mengirim template dengan tombol atau OTP?

Tambahkan nilai tombol di `buttonsTemplateVariable`. Untuk template autentikasi dengan tombol salin kode, berikan kode itu di field body maupun field tombol.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=TEMPLATE \
  -F templateName=login_code \
  -F templateLanguage=en_US \
  -F bodyTemplateVariables=482913 \
  -F buttonsTemplateVariable=482913
```

Di sini `login_code` adalah placeholder untuk template autentikasi Anda sendiri yang sudah disetujui. Untuk beberapa tombol, `buttonsTemplateVariable` menerima array JSON. Buat kode di server Anda, simpan dalam bentuk hash dengan masa berlaku, dan jangan pernah mencatatnya di log. [Panduan OTP](/id/blog/otp-authentication-messages-guide/) kami membahas pengiriman one-tap, zero-tap, dan salin kode.

## Bagaimana membalas di dalam jendela layanan pelanggan?

Gunakan tipe pesan sesi. Balasan teks biasa membutuhkan `messageType=TEXT` dan `content`.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=TEXT \
  -F campaignName=support \
  -F "content=Your order has shipped. Reply here if you need anything."
```

Untuk tombol balasan, kirim `messageType=INTERACTIVE` dan `interactivePayload` yang mengikuti format pesan interaktif Meta.

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=INTERACTIVE \
  -F campaignName=support \
  -F 'interactivePayload={"type":"button","body":{"text":"Confirm your delivery slot?"},"action":{"buttons":[{"type":"reply","reply":{"id":"slot_ok","title":"Confirm"}},{"type":"reply","reply":{"id":"slot_change","title":"Change"}}]}}'
```

Untuk menandai pesan masuk sebagai sudah dibaca, dan secara opsional menampilkan indikator mengetik, panggil `POST /whatsapp/message/read` dengan JSON:

```bash
curl -X POST https://api.broadnet.me/whatsapp/message/read \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "messageId": "wamid.HBgMOTcxNTAwMDAwMDAwFQIAEhgg", "typing": true }'
```

Lihat panduan kami tentang pesan interaktif untuk arahan desain.

## Bagaimana melacak pengiriman?

Status dikirim (push) ke webhook yang dikonfigurasi di akun Anda dan juga dapat dibaca per batch. Setiap penerima bergerak melalui `SUBMITTED`, `SENT`, `DELIVERED`, dan `READ`, atau berakhir sebagai `FAILED` atau `REJECTED` beserta alasannya.

```bash
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&status=FAILED&page=0&size=50" \
  -H "Authorization: Bearer $BROADNET_TOKEN"
```

`batch_Id` wajib; `status`, `destination`, `search`, `page`, dan `size` opsional, dan `page` dimulai dari nol. Gunakan polling sebagai cadangan untuk event yang terlewat, dan bangun penerima Anda seperti dijelaskan di [panduan webhook](/id/blog/whatsapp-webhooks-guide/) kami. Batch juga dapat dijeda, dilanjutkan, dan dibatalkan dengan endpoint batch dalam referensi.

## Error apa yang harus ditangani?

Baca status HTTP terlebih dahulu, lalu body error. API memunculkan error dalam body JSON lima field, dan kegagalan khusus WhatsApp dapat berasal dari Meta.

| Status | Arti | Tindakan |
|---|---|---|
| `400` | Error validasi, seperti field khusus tipe yang hilang atau tujuan tidak valid | Perbaiki request; jangan ulangi tanpa perubahan |
| `401` / `403` | Token hilang atau tidak valid, atau operasi tidak diizinkan | Perbaiki token atau minta kami mengaktifkan operasinya |
| `404` | Template atau sender tidak ditemukan | Periksa nama dan bahasa template |
| `413` | File yang diunggah terlalu besar | Perkecil file |
| `429` | Batas laju | Tunggu `Retry-After` |
| `502` | Meta menolak panggilan; `message` memuat kode dan teks error Meta | Baca; ulangi hanya jika bersifat sementara |
| `500`, `503`, `504` | Masalah server atau upstream | Ulangi dengan exponential backoff dan jitter |

Panduan kode error kami menjelaskan kode Meta yang umum. Karena tidak ada idempotency key, ikuti langkah perlindungan duplikasi di [Error dan batas](/developers/errors-and-limits/): tulis catatan pending terlebih dahulu, simpan `batchId` yang dikembalikan, dan setelah timeout periksa status sebelum mengirim lagi.

## Contoh kasus: layanan notifikasi pesanan

"Souq Rafiq" (fiktif), pengecer di UEA, mengirim template `order_update` saat pesanan dikirim.

1. Layanan pesanan menulis baris dengan kunci `order-8842-shipped` berstatus `pending`.
2. Layanan memanggil `sendbulk` dengan template dan nomor pesanan sebagai `bodyTemplateVariables`.
3. Pada `200`, layanan menyimpan `batchId` dan menandai baris `accepted`.
4. Handler webhook memperbarui baris menjadi `DELIVERED` atau `READ`, atau `FAILED` beserta alasannya.
5. Jika pelanggan membalas, alat dukungan memakai pesan sesi `TEXT` di dalam jendela.
6. Job malam hari membaca `bulk-insights` untuk batch kemarin dan merekonsiliasi status yang hilang.

## Apa saja kesalahan umum?

- **Mengirim JSON.** Endpoint pengiriman WhatsApp memakai multipart; `Content-Type: application/json` tidak akan berfungsi.
- **`data=` di Python** alih-alih `files=`, yang meng-encode form sebagai url-encoded.
- **Teks bebas di luar jendela**, yang gagal. Gunakan template.
- **Kode bahasa yang salah**, seperti `en` alih-alih `en_US` milik template.
- **Menganggap `200` sebagai terkirim.** Artinya diantrekan.
- **Pengulangan buta** setelah timeout, yang dapat mengirim pesan dua kali kepada pelanggan.
- **Token di kode sisi klien.** Panggil API hanya dari backend Anda.
- **Batasan:** detail payload bervariasi menurut template. Konfirmasikan tata letak variabel untuk kampanye personal pertama Anda bersama tim kami.

## Bagaimana OmniChannels.ai membantu

BroadNet adalah Meta Business Solution Provider resmi, dan onboarding ke WhatsApp Business API memakai Meta Embedded Signup; tim kami menyiapkan registrasi bersama Anda, sementara persetujuan tetap di tangan Meta. REST API didokumentasikan dengan spesifikasi OpenAPI 3.1 dan SDK TypeScript, `@broadnet/cpaas-sdk`, dan status pengiriman dinormalisasi di semua kanal. Mulailah dengan [dokumentasi WhatsApp API](/developers/whatsapp/) atau [quickstart](/developers/quickstart/).
