Lewati ke konten
API v2

Referensi API KirimPesan.ID

Setiap contoh kode menyesuaikan akun Anda: perangkat, nomor uji, dan API key terisi otomatis, dan tombol Coba sekarang mengirim request sungguhan dari browser.

Contoh memakai nilai placeholder

Memeriksa sesi…

Masuk untuk mengisi otomatis

Pengantar

REST API KirimPesan memungkinkan sistem Anda mengirim pesan WhatsApp, OTP, mengelola perangkat, kontak, dan campaign, serta menerima event lewat webhook. Request & respons memakai JSON UTF-8.

Base URL

https://app.kirimpesan.id/api/v2

Spesifikasi OpenAPI 3.1

/api/v2/openapi.json

Mulai cepat

  1. 1Tautkan nomor WhatsApp di Perangkat.
  2. 2Buat API key di Developer → API Key (atau tombol Buat key untuk docs di panel atas).
  3. 3Kirim pesan pertama — contoh di bawah sudah memakai perangkat, nomor, dan key Anda bila sudah masuk.
curl -X POST "https://app.kirimpesan.id/api/v2/messages" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: order-2041-7c0e2f4a" \
  -H "Content-Type: application/json" \
  -d '{
  "deviceId": 12,
  "to": "6281234567890",
  "content": {
    "type": "text",
    "text": "Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋"
  }
}'

Konvensi

  • Nomor telepon: digit E.164 tanpa “+”, contoh 6281234567890. Nomor lokal 0812… dinormalisasi memakai negara akun Anda.
  • Tanggal: ISO 8601 UTC, contoh 2026-10-12T03:15:22.000Z. ID: integer.
  • List memakai ?page=1&pageSize=20 (maks 100) dan mengembalikan { data, meta: { page, pageSize, total } }.
  • POST /api/v2/messages wajib header Idempotency-Key — retry dengan key & body sama tidak mengirim ganda.
  • Error selalu { "error": { "code", "message", "details"? } }; message berbahasa Indonesia, code stabil untuk logika Anda.
  • API lama (/api/send/…) tetap tersedia dengan format {status, message, data} — lihat bagian API Lama.

Autentikasi

API key & header

Kirim API key sebagai Bearer token. Secret hanya ditampilkan sekali saat dibuat — simpan di variabel lingkungan server, jangan di kode frontend/aplikasi seluler.

Header (API v2)
Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx

API lama memakai parameter secret (query, form, atau JSON) dengan nilai key yang sama. Key yang dicabut langsung ditolak (401 invalid_api_key / "Invalid API secret supplied!").

Izin (scope)

Setiap key punya daftar izin. Endpoint di luar izin ditolak 403 missing_permission dengan details.permission. Key Docs Playground otomatis mendapat izin yang ditandai ✓.

Kirim

  • ✓wa_sendKirim pesan tunggal
  • wa_send_bulkKirim pesan massal
  • ✓otpKirim & verifikasi OTP

Perangkat

  • ✓get_wa_accountsLihat perangkat
  • create_whatsappTautkan perangkat baru
  • ✓validate_wa_phoneCek nomor WhatsApp
  • get_wa_serversLihat server engine

Kontak

  • ✓get_contactsLihat kontak
  • create_contactTambah kontak
  • ✓get_groupsLihat grup kontak
  • create_groupBuat grup kontak
  • get_unsubscribedLihat daftar berhenti langganan

Campaign

  • ✓get_wa_campaignsLihat campaign
  • start_wa_campaignMulai / lanjutkan campaign
  • stop_wa_campaignJeda / hentikan campaign

Baca data

  • ✓get_wa_sentLihat pesan terkirim
  • ✓get_wa_pendingLihat pesan antre
  • ✓get_wa_receivedLihat pesan masuk
  • ✓get_wa_groupsLihat grup WhatsApp
  • get_wa_group_contactsLihat peserta grup WhatsApp
  • ✓get_subscriptionLihat paket & kuota
  • get_creditsLihat saldo kredit
  • get_shortenersLihat shortener

Hapus

  • delete_contactHapus kontak
  • delete_groupHapus grup kontak
  • delete_unsubscribedHapus dari daftar berhenti langganan
  • delete_wa_sentHapus pesan terkirim
  • delete_wa_receivedHapus pesan masuk
  • delete_wa_campaignHapus campaign
  • delete_wa_accountHapus perangkat

Pesan

Kirim pesan tunggal atau massal, lalu pantau statusnya. Contoh mengikuti perangkat, penerima, dan tipe pesan yang Anda pilih.

POST/api/v2/messages
wa_send Mengirim pesan

Kirim satu pesan

Mengantrekan satu pesan ke satu nomor/grup. Wajib header Idempotency-Key — request ulang dengan key & body yang sama mengembalikan hasil yang sama tanpa mengirim ganda (disimpan 24 jam).

ParameterLokasiTipeKeterangan
Idempotency-Key*headerstringString unik per pesan (mis. UUID / ID order), maks 200 karakter.
deviceId*bodyintegerID perangkat pengirim.
to*bodystringNomor tujuan (E.164 tanpa “+”, nomor lokal 08… dinormalisasi) atau JID grup.
content*bodyobjectMessageContent — bentuk ditentukan type. Lihat “Tipe konten”.
prioritybodynormal | highDefault normal. high untuk transaksi/OTP.
skipOptOutbodybooleanLewati daftar opt-out (khusus pesan transaksional). Default false.
curl -X POST "https://app.kirimpesan.id/api/v2/messages" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: order-2041-7c0e2f4a" \
  -H "Content-Type: application/json" \
  -d '{
  "deviceId": 12,
  "to": "6281234567890",
  "content": {
    "type": "text",
    "text": "Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋"
  }
}'
Respons 201
{
  "id": 98231,
  "deviceId": 12,
  "campaignId": null,
  "direction": "outbound",
  "source": "api",
  "peer": "6281234567890",
  "participant": null,
  "type": "text",
  "content": { "type": "text", "text": "Halo! Ini pesan uji…" },
  "status": "queued",
  "statusReason": null,
  "priority": 3,
  "attempts": 0,
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null,
  "failedAt": null,
  "createdAt": "2026-10-12T03:15:22.000Z"
}
Kemungkinan error (5)
  • 400bad_request — header Idempotency-Key tidak ada
  • 400validation_failed — body/content tidak valid
  • 404not_found — perangkat tidak ditemukan
  • 409idempotency_key_reused / idempotency_in_progress
  • 422invalid_phone / quota_exceeded
GET/api/v2/messages/{id}
get_wa_sent

Status pesan + timeline

Status terkini dan riwayat ACK (events). Gunakan webhook message.status agar tidak perlu polling.

ParameterLokasiTipeKeterangan
id*pathintegerID pesan dari respons kirim.
curl -X GET "https://app.kirimpesan.id/api/v2/messages/98231" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "id": 98231,
  "status": "read",
  "sentAt": "2026-10-12T03:15:25.000Z",
  "deliveredAt": "2026-10-12T03:15:26.000Z",
  "readAt": "2026-10-12T03:16:40.000Z",
  "…": "…field Message lainnya",
  "events": [
    { "status": "sent", "error": null, "at": "2026-10-12T03:15:25.000Z" },
    { "status": "delivered", "error": null, "at": "2026-10-12T03:15:26.000Z" },
    { "status": "read", "error": null, "at": "2026-10-12T03:16:40.000Z" }
  ]
}
POST/api/v2/messages/bulk
wa_send_bulk Mengirim pesan

Kirim massal (membuat campaign)

Membuat campaign dari daftar nomor dan/atau grup kontak lalu langsung berjalan (atau terjadwal dengan scheduledAt). Duplikat, nomor tidak valid, dan opt-out otomatis dilewati.

ParameterLokasiTipeKeterangan
name*bodystringNama campaign (3–191 karakter).
deviceIds*bodyarrayID perangkat pengirim (1–50), dirotasi.
rotationbodyround_robin | weighted | sequentialDefault round_robin.
recipientsbodyarrayDaftar nomor (maks 100.000).
groupIdsbodyarrayID grup kontak (maks 200).
content*bodyobjectMessageContent.
scheduledAtbodystringISO 8601. Kosong = kirim sekarang.
curl -X POST "https://app.kirimpesan.id/api/v2/messages/bulk" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Uji dari dokumentasi",
  "deviceIds": [
    12
  ],
  "recipients": [
    "6281234567890"
  ],
  "content": {
    "type": "text",
    "text": "Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋"
  }
}'
Respons 201
{
  "campaign": { "id": 311, "name": "Docs bulk", "status": "running", "totalRecipients": 1, "…": "…" },
  "audience": { "valid": 1, "duplicates": 0, "invalid": 0, "optedOut": 0 }
}

Tipe konten (MessageContent)

Field content ditentukan oleh type. Variabel ({{nama}}, field kustom kontak) dan spintax ({a|b}) dirender di server. Ganti Tipe pesan di panel atas untuk melihat contoh lain — semua contoh kode ikut berubah.

typeFieldCatatan
texttextMendukung variabel {{nama}} dan spintax {a|b}.
image · videomedia.url, caption?URL publik HTTPS.
documentmedia.url, media.mimetype?, media.fileName?, caption?PDF, Excel, Word, dll.
audio · voicemedia.urlvoice dikirim sebagai pesan suara (PTT).
stickermedia.urlWebP 512×512.
locationlatitude, longitude, name?, address?—
contactcontacts[] {name, phone}Maksimal 10 kontak.
buttonstext, buttons[] {kind, label, value}, footer?Maksimal 3 tombol (url/call/reply).
listtext, buttonText, sections[] {title, rows[]}, footer?Maks 10 bagian × 10 baris.
pollquestion, options[], selectableCount?2–12 opsi.
reactionemoji, messageIdReaksi ke pesan tertentu.
content · Teks
{
  "type": "text",
  "text": "Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋"
}

Perangkat

Kelola nomor WhatsApp yang ditautkan dan cek apakah sebuah nomor terdaftar di WhatsApp.

GET/api/v2/devices
get_wa_accounts

Daftar perangkat

Tanpa parameter.

curl -X GET "https://app.kirimpesan.id/api/v2/devices" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
[
  {
    "id": 12,
    "label": "CS Utama",
    "phone": "6281234567890",
    "displayName": "Toko Maju",
    "avatarUrl": null,
    "status": "connected",
    "statusReason": null,
    "receiveChats": true,
    "dailyCap": 1000,
    "lastConnectedAt": "2026-10-12T01:00:00.000Z",
    "createdAt": "2026-09-01T08:00:00.000Z",
    "todaySent": 0,
    "legacyUnique": null
  }
]
POST/api/v2/devices
create_whatsapp Mengubah data

Buat perangkat (QR/pairing)

Membuat perangkat baru (memakai slot paket). Lanjutkan dengan QR atau pairing code.

ParameterLokasiTipeKeterangan
labelbodystringNama perangkat.
methodbodyqr | pairingDefault qr.
phonebodystringWajib untuk metode pairing.
curl -X POST "https://app.kirimpesan.id/api/v2/devices" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Promo 2"
}'
Respons 201
{
  "id": 12,
  "label": "CS Utama",
  "phone": "6281234567890",
  "displayName": "Toko Maju",
  "avatarUrl": null,
  "status": "pending",
  "statusReason": null,
  "receiveChats": true,
  "dailyCap": 1000,
  "lastConnectedAt": "2026-10-12T01:00:00.000Z",
  "createdAt": "2026-09-01T08:00:00.000Z",
  "todaySent": 0,
  "legacyUnique": null
}
GET/api/v2/devices/{id}/qr
create_whatsapp

QR terkini

QR berganti tiap ±20 detik selama perangkat belum tertaut. 404 bila tidak ada QR aktif.

ParameterLokasiTipeKeterangan
id*pathintegerID perangkat.
curl -X GET "https://app.kirimpesan.id/api/v2/devices/12/qr" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{ "qr": "2@AbCdEf…", "expiresAt": "2026-10-12T03:16:00.000Z" }
POST/api/v2/devices/{id}/pairing-code
create_whatsapp Mengubah data

Minta pairing code

Menautkan ulang perangkat dengan kode 8 karakter. Perangkat yang sedang tersambung akan diputus.

ParameterLokasiTipeKeterangan
id*pathintegerID perangkat.
phone*bodystringNomor WhatsApp yang akan ditautkan.
curl -X POST "https://app.kirimpesan.id/api/v2/devices/12/pairing-code" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{}'
Respons 201
{ "deviceId": 12, "status": "pairing", "pairingCode": "K7P29QXA" }
GET/api/v2/devices/{id}/groups
get_wa_groups

Grup WhatsApp perangkat

ParameterLokasiTipeKeterangan
id*pathintegerID perangkat.
curl -X GET "https://app.kirimpesan.id/api/v2/devices/12/groups" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
[{ "id": 7, "gid": "[email protected]", "name": "Reseller Jabar", "participantsCount": 128, "syncedAt": "2026-10-12T01:00:00.000Z" }]
POST/api/v2/numbers/check
validate_wa_phone

Cek nomor terdaftar di WhatsApp

Maksimal 50 nomor per request. Perangkat harus tersambung.

ParameterLokasiTipeKeterangan
deviceId*bodyintegerPerangkat yang dipakai untuk mengecek.
numbers*bodyarrayDaftar nomor (1–50).
curl -X POST "https://app.kirimpesan.id/api/v2/numbers/check" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "deviceId": 12,
  "numbers": [
    "6281234567890"
  ]
}'
Respons 200
[
  { "number": "6281234567890", "exists": true, "jid": "[email protected]" },
  { "number": "6280000000000", "exists": false, "jid": null }
]
Kemungkinan error (1)
  • 422device_not_connected / engine_unavailable

Kontak

CRUD kontak dan grup kontak untuk audiens campaign. Field kustom bisa dipakai sebagai variabel pesan.

GET/api/v2/contacts
get_contacts

Daftar kontak

ParameterLokasiTipeKeterangan
pagequeryintegerHalaman (mulai 1).
pageSizequeryintegerJumlah per halaman, maks 100.
qquerystringKata kunci pencarian.
groupIdqueryintegerFilter anggota grup kontak.
curl -G "https://app.kirimpesan.id/api/v2/contacts" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "pageSize=20"
Respons 200
{
  "data": [{ "id": 1, "phone": "6281234567890", "name": "Rina", "groups": [{ "id": 4, "name": "VIP" }], "customFields": { "kota": "Bandung" } }],
  "meta": { "page": 1, "pageSize": 20, "total": 1 }
}
POST/api/v2/contacts
create_contact

Buat kontak

ParameterLokasiTipeKeterangan
phone*bodystringNomor kontak.
namebodystringNama kontak.
groupIdsbodyarrayID grup kontak.
customFieldsbodyobjectField kustom bebas (dipakai sebagai variabel {{kota}}).
curl -X POST "https://app.kirimpesan.id/api/v2/contacts" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "6281234567890",
  "name": "Rina",
  "customFields": {
    "kota": "Bandung"
  }
}'
Respons 201
{ "id": 1, "phone": "6281234567890", "name": "Rina", "groups": [], "customFields": { "kota": "Bandung" } }
Kemungkinan error (1)
  • 409duplicate_phone — nomor sudah ada
GET/api/v2/contacts/{id}
get_contacts

Detail kontak

ParameterLokasiTipeKeterangan
id*pathintegerID kontak.
curl -X GET "https://app.kirimpesan.id/api/v2/contacts/1" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{ … }
PATCH/api/v2/contacts/{id}
create_contact Mengubah data

Ubah kontak

ParameterLokasiTipeKeterangan
id*pathintegerID kontak.
phonebodystringNomor baru.
namebodystringNama baru.
curl -X PATCH "https://app.kirimpesan.id/api/v2/contacts/1" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Rina Sari"
}'
Respons 200
{ … }
DELETE/api/v2/contacts/{id}
delete_contact Mengubah data

Hapus kontak

ParameterLokasiTipeKeterangan
id*pathintegerID kontak.
curl -X DELETE "https://app.kirimpesan.id/api/v2/contacts/1" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 204
// 204 No Content
GET/api/v2/contact-groups
get_groups

Daftar grup kontak

ParameterLokasiTipeKeterangan
pagequeryintegerHalaman (mulai 1).
pageSizequeryintegerJumlah per halaman, maks 100.
curl -G "https://app.kirimpesan.id/api/v2/contact-groups" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "pageSize=20"
Respons 200
{ … }
POST/api/v2/contact-groups
create_group

Buat grup kontak

ParameterLokasiTipeKeterangan
name*bodystringNama grup.
curl -X POST "https://app.kirimpesan.id/api/v2/contact-groups" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Pelanggan VIP"
}'
Respons 201
{ … }
PATCH/api/v2/contact-groups/{id}
create_group Mengubah data

Ubah grup kontak

ParameterLokasiTipeKeterangan
id*pathintegerID grup.
name*bodystringNama baru.
curl -X PATCH "https://app.kirimpesan.id/api/v2/contact-groups/4" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Pelanggan VIP 2026"
}'
Respons 200
{ … }
DELETE/api/v2/contact-groups/{id}
delete_group Mengubah data

Hapus grup kontak

Default hanya menghapus keanggotaan; kontak tetap ada. deleteOrphans=true ikut menghapus kontak yang tidak punya grup lain.

ParameterLokasiTipeKeterangan
id*pathintegerID grup.
deleteOrphansquerybooleanHapus juga kontak yatim.
curl -X DELETE "https://app.kirimpesan.id/api/v2/contact-groups/4" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 204
// 204 No Content

Campaign

Buat, pantau, dan kendalikan campaign. Untuk kirim massal sederhana gunakan `POST /api/v2/messages/bulk`.

GET/api/v2/campaigns
get_wa_campaigns

Daftar campaign

ParameterLokasiTipeKeterangan
pagequeryintegerHalaman (mulai 1).
pageSizequeryintegerJumlah per halaman, maks 100.
qquerystringKata kunci pencarian.
statusquerydraft | scheduled | running | paused | completed | cancelled | failedFilter status.
curl -G "https://app.kirimpesan.id/api/v2/campaigns" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "pageSize=20"
Respons 200
{ "data": [{ "id": 311, "name": "Flash Sale 10.10", "status": "running", "totalRecipients": 4000 }], "meta": { "page": 1, "pageSize": 20, "total": 1 } }
POST/api/v2/campaigns
wa_send_bulk Mengirim pesan

Buat campaign

Body sama dengan dashboard POST /v1/campaigns: audiens fleksibel, jeda acak, jendela kirim, jadwal.

ParameterLokasiTipeKeterangan
name*bodystringNama campaign (3–191).
deviceIds*bodyarrayID perangkat (1–50).
rotationbodyround_robin | weighted | sequentialDefault round_robin.
audience*bodyobject{ numbers?, groupIds?, waGroups?, fileId?, mapping? }.
content*bodyobjectMessageContent.
delayMinbodyintegerJeda minimum antar pesan (detik, 1–3600).
delayMaxbodyintegerJeda maksimum (≥ delayMin).
sendWindowStartbodystringJam mulai kirim HH:mm (berpasangan dengan sendWindowEnd).
sendWindowEndbodystringJam selesai kirim HH:mm.
skipDuplicates24hbodybooleanLewati nomor yang sudah dikirimi 24 jam terakhir. Default true.
scheduledAtbodystringISO 8601 untuk menjadwalkan.
sendNowbodybooleantrue = langsung jalan. Default false (draft).
curl -X POST "https://app.kirimpesan.id/api/v2/campaigns" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Uji dari dokumentasi",
  "deviceIds": [
    12
  ],
  "audience": {
    "numbers": [
      "6281234567890"
    ]
  },
  "content": {
    "type": "text",
    "text": "Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋"
  },
  "sendNow": true
}'
Respons 201
{ … }
GET/api/v2/campaigns/{id}
get_wa_campaigns

Detail campaign

ParameterLokasiTipeKeterangan
id*pathintegerID campaign.
curl -X GET "https://app.kirimpesan.id/api/v2/campaigns/311" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{ … }
POST/api/v2/campaigns/{id}/pause
stop_wa_campaign Mengubah data

Jeda campaign

ParameterLokasiTipeKeterangan
id*pathintegerID campaign.
curl -X POST "https://app.kirimpesan.id/api/v2/campaigns/311/pause" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{ … }
Kemungkinan error (1)
  • 422invalid_state — status campaign tidak mengizinkan aksi ini
POST/api/v2/campaigns/{id}/resume
start_wa_campaign Mengubah data

Lanjutkan campaign

ParameterLokasiTipeKeterangan
id*pathintegerID campaign.
curl -X POST "https://app.kirimpesan.id/api/v2/campaigns/311/resume" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{ … }
Kemungkinan error (1)
  • 422invalid_state — status campaign tidak mengizinkan aksi ini
POST/api/v2/campaigns/{id}/cancel
stop_wa_campaign Mengubah data

Batalkan campaign

Pesan yang masih antre menjadi cancelled.

ParameterLokasiTipeKeterangan
id*pathintegerID campaign.
curl -X POST "https://app.kirimpesan.id/api/v2/campaigns/311/cancel" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{ … }
Kemungkinan error (1)
  • 422invalid_state — status campaign tidak mengizinkan aksi ini

OTP

Alur: kirim OTP → pengguna memasukkan kode → verifikasi. Kode 6 digit dibuat server, sekali pakai, dengan masa berlaku yang bisa diatur.

POST/api/v2/otp
otp Mengirim pesan

Kirim OTP 6 digit

Server membuat kode 6 digit (CSPRNG) dan mengganti {{otp}} di pesan. Prioritas tertinggi & melewati opt-out. Ada rate limit per nomor.

ParameterLokasiTipeKeterangan
deviceId*bodyintegerID perangkat pengirim.
phone*bodystringNomor tujuan.
messagebodystringWajib memuat {{otp}}. Default "Kode OTP Anda: {{otp}}".
expirebodyintegerMasa berlaku (detik), 1–86400. Default 300.
curl -X POST "https://app.kirimpesan.id/api/v2/otp" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "deviceId": 12,
  "phone": "6281234567890",
  "message": "Kode verifikasi Anda: {{otp}}. Berlaku 5 menit. Jangan berikan ke siapa pun.",
  "expire": 300
}'
Respons 201
{ "messageId": 98232, "phone": "6281234567890", "expiresAt": "2026-10-12T03:20:22.000Z" }
Kemungkinan error (3)
  • 400validation_failed — pesan tidak memuat {{otp}}
  • 429too_many_requests — terlalu banyak OTP ke nomor ini
  • 422invalid_phone / quota_exceeded
POST/api/v2/otp/verify
otp

Verifikasi OTP

Kode hanya bisa dipakai sekali. Gagal → 422 otp_invalid.

ParameterLokasiTipeKeterangan
phone*bodystringNomor yang menerima OTP.
otp*bodystringKode yang dimasukkan pengguna.
curl -X POST "https://app.kirimpesan.id/api/v2/otp/verify" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "6281234567890",
  "otp": "482913"
}'
Respons 200
{ "valid": true, "phone": "6281234567890" }
Kemungkinan error (1)
  • 422otp_invalid — OTP tidak valid atau kedaluwarsa

Webhook

Daftarkan URL HTTPS di Developer → Webhook dan pilih event. Kami mengirim POST JSON dan menganggap sukses bila endpoint Anda membalas 2xx dengan cepat — kerjakan proses berat di latar belakang. Gagal dicoba ulang 8× dengan jeda bertahap (1 mnt → 12 jam, total ±22 jam); setiap pengiriman tercatat dan bisa dikirim ulang.

Event & payload

message.receivedPesan masuk ke salah satu perangkat Anda.
message.statusPerubahan status pesan keluar: sent, delivered, read, failed, …
device.connectedPerangkat berhasil terhubung / tersambung kembali.
device.disconnectedPerangkat terputus (logout, sesi kedaluwarsa, dll.).
campaign.completedSeluruh penerima campaign selesai diproses.

Pesan masuk ke salah satu perangkat Anda.

POST · application/json
User-Agent: KirimPesan-Webhook/2.0
X-KirimPesan-Event: message.received
X-KirimPesan-Delivery: <uuid>
X-KirimPesan-Signature: t=<unix>,v1=<hex>

{
  "id": "f3a1c2d4-5b6e-4f70-8a9b-0c1d2e3f4a5b",
  "event": "message.received",
  "createdAt": "2026-10-12T03:15:22.000Z",
  "data": {
    "id": 120931,
    "deviceId": 12,
    "from": "6281234567890",
    "participant": null,
    "isGroup": false,
    "type": "text",
    "text": "Halo, stok masih ada?",
    "mediaUrl": null,
    "timestamp": "2026-10-12T03:15:22.000Z"
  }
}

Verifikasi signature

Header X-KirimPesan-Signature: t=<unix>,v1=<hex>. Nilai v1 = HMAC-SHA256 dari t + "." + rawBody dengan secret webhook. Tolak bila tidak cocok atau selisih waktu > 5 menit.

import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.KIRIMPESAN_WEBHOOK_SECRET;

// Penting: gunakan raw body, bukan JSON yang sudah di-parse.
app.post("/hooks/wa", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-KirimPesan-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);

  // Tolak jika selisih waktu > 5 menit (mencegah replay)
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);

  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(`${t}.${req.body.toString("utf8")}`)
    .digest("hex");

  const ok =
    typeof parts.v1 === "string" &&
    parts.v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  if (!ok) return res.sendStatus(401);

  const event = JSON.parse(req.body.toString("utf8"));
  // … proses event.event / event.data
  res.sendStatus(200);
});

Mode legacy per webhook mengirim form-encoded seperti sistem lama (secret, type=whatsapp, data[id], data[wid], data[phone], data[message], data[attachment], data[timestamp]) — hanya untuk event pesan masuk.

Uji signature (live)

Tempel body mentah, secret, dan header dari log server Anda untuk memastikan implementasi verifikasi sudah benar.

Spasi & urutan field berpengaruh — gunakan body mentah, bukan JSON yang sudah di-parse ulang.

Isi secret dan header untuk memeriksa.

Dihitung di browser Anda dengan WebCrypto — payload & secret tidak dikirim ke mana pun.

Error & Status

Status pesan

Status v2Status API lamaArti
queuedpendingMasuk antrean, menunggu giliran kirim.
sendingqueuedSedang dikirim ke WhatsApp.
sentsentDiterima server WhatsApp (centang satu).
deliveredsentSampai di perangkat penerima (centang dua).
readsentDibaca penerima (centang biru).
playedsentPesan suara/video diputar.
failedfailedGagal; lihat statusReason.
cancelledfailedDibatalkan sebelum dikirim.
skippedfailedDilewati (opt-out, duplikat, atau nomor tidak valid).

Kode error API v2

Bentuk: { "error": { "code", "message", "details"? } } dengan HTTP status yang sesuai. Kode 5xx & 429 aman di-retry (pakai Idempotency-Key yang sama untuk kirim pesan).

HTTPcodeArti
400validation_failedBody/parameter tidak valid. Detail per field di error.details[] {path, message}.
400bad_requestPermintaan salah bentuk, mis. header Idempotency-Key tidak ada.
401unauthorizedHeader Authorization: Bearer <key> tidak ada.
401invalid_api_keyAPI key salah, dicabut, atau pemiliknya dinonaktifkan.
403missing_permissionIzin key kurang. details.permission berisi izin yang dibutuhkan.
403feature_not_in_planPaket tidak mencakup fitur (mis. api, whatsapp).
404not_foundResource tidak ditemukan di workspace Anda.
409duplicate_phoneNomor kontak sudah ada.
409idempotency_key_reusedIdempotency-Key dipakai dengan body berbeda.
409idempotency_in_progressRequest dengan key yang sama masih diproses.
422invalid_phoneNomor tujuan tidak valid.
422quota_exceededKuota pesan paket habis.
422device_not_connectedPerangkat tidak tersambung.
422otp_invalidOTP salah atau kedaluwarsa.
422invalid_stateStatus campaign tidak mengizinkan aksi (pause/resume/cancel).
422limit_reachedBatas paket tercapai (perangkat, kontak, …).
429too_many_requestsRate limit terlampaui. Tunggu sesuai header Retry-After.
503service_unavailableLayanan sementara tidak tersedia; coba lagi.
500internalKesalahan server. Aman untuk dicoba ulang dengan Idempotency-Key yang sama.

Error API lama

HTTP selalu 200; status ada di body { status, message, data }. Teks message berbahasa Inggris dan tidak pernah berubah (aman dibandingkan string).

statusmessageKapan
400Invalid Parameters!secret tidak dikirim / parameter wajib kurang
401Invalid API secret supplied!secret salah atau sudah dicabut
403This API key doesn't have permission to use this endpoint!izin key kurang
403Subscription has no permission to use WhatsApp services!paket tanpa layanan WhatsApp
403Account is not subscribed to any premium package!tidak berlangganan
400/500Invalid API Endpoint!endpoint tidak dikenal
500Something went wrong!kesalahan server

API Lama (Legacy)

Endpoint lama /api/send|get|create|delete|remote|validate/… (juga /v3.9.7/api/…) berjalan 1:1: semua metode HTTP diterima, secret dari query/form/JSON, HTTP selalu 200, dan teks pesan tidak berubah.

account / unique = Unique ID akun WhatsApp (dari get/wa.accounts). Paginasi memakai offset lama: page < 2 → mulai 0, selain itu page × limit. Integrasi baru sebaiknya memakai API v2.

/api/send/…

POST/api/send/whatsapp
wa_send Mengirim pesan

Kirim pesan WhatsApp

Prioritas: priority integer, default 1 (<1→0, <2→1, lainnya 2). p ≤ 1 dikirim segera, p = 2 masuk antrean. Juga menerima upload media_file / document_file (multipart).

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/send/whatsapp.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
account*formstringUnique ID akun WhatsApp pengirim.
recipient*formstringNomor tujuan format internasional (+62…) atau JID grup …@g.us.
message*formstringTeks pesan / caption.
typeformtext | media | documentDefault text; nilai lain dianggap text.
media_urlformstringURL media (type=media).
media_typeformimage | audio | videoimage | audio | lainnya = video.
document_urlformstringURL dokumen (type=document).
document_typeformpdf | xml | xls | xlsx | doc | docxJenis dokumen.
document_nameformstringNama file dokumen.
priorityformintegerDefault 1.
shortenerformintegerID shortener (tidak tersedia; > 0 → 404).
curl -X POST "https://app.kirimpesan.id/api/send/whatsapp" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "account=UNIQUE_ID_AKUN" \
  --data-urlencode "recipient=+6281234567890" \
  --data-urlencode "message=Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋" \
  --data-urlencode "type=text" \
  --data-urlencode "priority=1"
Respons 200
{
  "status": 200,
  "message": "WhatsApp chat has been sent!",
  "data": { "messageId": 98233 }
}
Kemungkinan error (10)
  • 400Invalid Parameters! Required: recipient, account, message
  • 400Priority parameter must be an integer value!
  • 404WhatsApp account doesn't exist!
  • 500Unable to connect to WhatsApp servers!
  • 500WhatsApp account is disconnected!
  • 403You have reached the maximum number of allowed chats!
  • 400Invalid phone number!
  • 400Chat message is too short! / Chat message is too long!
  • 400You provided an invalid media url! / Please declare the media type! / Invalid media file!
  • 400You provided an invalid document url! / Please declare the document type! / Invalid Document Type! / Invalid Document File!
POST/api/send/whatsapp.random
wa_send Mengirim pesan

Kirim lewat akun acak

Sama dengan send/whatsapp tanpa account; akun tersambung dipilih acak.

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/send/whatsapp.random.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
recipient*formstringNomor tujuan (+62…).
message*formstringTeks pesan / caption.
typeformtext | media | documentDefault text; nilai lain dianggap text.
media_urlformstringURL media (type=media).
media_typeformimage | audio | videoimage | audio | lainnya = video.
document_urlformstringURL dokumen (type=document).
document_typeformpdf | xml | xls | xlsx | doc | docxJenis dokumen.
document_nameformstringNama file dokumen.
priorityformintegerDefault 1.
curl -X POST "https://app.kirimpesan.id/api/send/whatsapp.random" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "recipient=+6281234567890" \
  --data-urlencode "message=Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋" \
  --data-urlencode "type=text" \
  --data-urlencode "priority=1"
Respons 200
{
  "status": 200,
  "message": "WhatsApp chat has been sent!",
  "data": { "messageId": 98234, "account": { "unique": "a1b2c3", "wid": "[email protected]" } }
}
Kemungkinan error (3)
  • 404No WhatsApp accounts available!
  • 503No active WhatsApp account available!
  • 400Invalid Parameters! Required: recipient, message
POST/api/send/whatsapp.bulk
wa_send_bulk Mengirim pesan

Kirim massal (campaign)

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/send/whatsapp.bulk.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
account*formstringUnique ID akun pengirim.
campaign*formstringNama campaign (≥ 3 karakter, kalau tidak 500).
message*formstringTeks pesan.
recipientsformstringNomor dipisah koma.
groupsformstringID grup kontak dipisah koma.
typeformtext | media | documentDefault text; nilai lain dianggap text.
media_urlformstringURL media (type=media).
media_typeformimage | audio | videoimage | audio | lainnya = video.
document_urlformstringURL dokumen (type=document).
document_typeformpdf | xml | xls | xlsx | doc | docxJenis dokumen.
document_nameformstringNama file dokumen.
curl -X POST "https://app.kirimpesan.id/api/send/whatsapp.bulk" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "account=UNIQUE_ID_AKUN" \
  --data-urlencode "campaign=Uji dari dokumentasi" \
  --data-urlencode "message=Halo! Ini pesan uji dari dokumentasi API KirimPesan 👋" \
  --data-urlencode "recipients=6281234567890" \
  --data-urlencode "type=text"
Respons 200
{
  "status": 200,
  "message": "1 chats has been queued for sending!",
  "data": { "campaignId": 311, "messageIds": [98235] }
}
Kemungkinan error (2)
  • 500Campaign name is too short!
  • 500No chats were queued. Please check the sending limit of your package!
POST/api/send/otp
otp Mengirim pesan

Kirim OTP

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/send/otp.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
type*formwhatsappHanya whatsapp (SMS tidak tersedia).
message*formstringWajib memuat {{otp}}.
phone*formstringNomor tujuan.
account*formstringUnique ID akun pengirim.
expireformintegerDetik, default 300.
priorityformintegerDefault 2 (OTP tetap dikirim prioritas tertinggi).
curl -X POST "https://app.kirimpesan.id/api/send/otp" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "type=whatsapp" \
  --data-urlencode "message=Kode verifikasi Anda: {{otp}}. Berlaku 5 menit. Jangan berikan ke siapa pun." \
  --data-urlencode "phone=+6281234567890" \
  --data-urlencode "account=UNIQUE_ID_AKUN" \
  --data-urlencode "expire=300"
Respons 200
{
  "status": 200,
  "message": "OTP has been sent!",
  "data": { "phone": "+6281234567890", "message": "Kode OTP Anda: 482913", "messageId": 98236, "otp": 482913 }
}
Kemungkinan error (1)
  • 429Too many OTP requests for this phone number!

/api/get/…

GET/api/get/otp
otp

Verifikasi OTP

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/otp.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
otp*querystringKode OTP.
curl -G "https://app.kirimpesan.id/api/get/otp" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "otp=482913"
Respons 200
{
  "status": 200,
  "message": "OTP has been verified!",
  "data": false
}
Kemungkinan error (1)
  • 403OTP is invalid or expired!
GET/api/get/wa.message
get_wa_sent / get_wa_received

Detail pesan

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.message.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
id*queryintegerID pesan (messageId).
type*querysent | receivedArah pesan.
curl -G "https://app.kirimpesan.id/api/get/wa.message" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "type=sent"
Respons 200
{
  "status": 200,
  "message": "Message was found!",
  "data": { "id": 98233, "account": "+6281234567890", "status": "sent", "api": true, "recipient": "+6285711223344", "message": "Halo", "attachment": false, "created": 1760238922 }
}
GET/api/get/wa.sent
get_wa_sent

Pesan terkirim

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.sent.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/wa.sent" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "Sent WhatsApp Chats",
  "data": [ … ]
}
GET/api/get/wa.pending
get_wa_pending

Pesan antre

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.pending.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/wa.pending" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "Pending WhatsApp Chats",
  "data": [ … ]
}
GET/api/get/wa.received
get_wa_received

Pesan masuk

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.received.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/wa.received" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "Received WhatsApp Chats",
  "data": [ … ]
}
GET/api/get/wa.campaigns
get_wa_campaigns

Daftar campaign

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.campaigns.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/wa.campaigns" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "WhatsApp Campaigns",
  "data": [ … ]
}
GET/api/get/wa.accounts
get_wa_accounts

Daftar akun WhatsApp

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.accounts.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/wa.accounts" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "WhatsApp Accounts",
  "data": [{ "id": 12, "phone": "+6281234567890", "unique": "a1b2c3d4", "status": "connected", "created": 1756713600 }]
}
GET/api/get/contacts
get_contacts

Daftar kontak

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/contacts.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/contacts" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "Saved Contacts",
  "data": [ … ]
}
GET/api/get/groups
get_groups

Daftar grup kontak

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/groups.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/groups" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "Contact Groups",
  "data": [ … ]
}
GET/api/get/unsubscribed
get_unsubscribed

Daftar berhenti langganan

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/unsubscribed.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
pagequeryintegerDefault 1. Offset lama: page < 2 → 0, selain itu page × limit.
limitqueryintegerDefault 10.
curl -G "https://app.kirimpesan.id/api/get/unsubscribed" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=10"
Respons 200
{
  "status": 200,
  "message": "Unsubscribed Contacts",
  "data": [ … ]
}
GET/api/get/wa.account.status
get_wa_accounts

Status akun

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.account.status.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
unique*querystringUnique ID akun WhatsApp (lihat get/wa.accounts).
curl -G "https://app.kirimpesan.id/api/get/wa.account.status" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "unique=UNIQUE_ID_AKUN"
Respons 200
{
  "status": 200,
  "message": "Account is connected!",
  "data": "connected"
}
GET/api/get/wa.groups
get_wa_groups

Grup WhatsApp akun

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.groups.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
unique*querystringUnique ID akun WhatsApp (lihat get/wa.accounts).
curl -G "https://app.kirimpesan.id/api/get/wa.groups" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "unique=UNIQUE_ID_AKUN"
Respons 200
{
  "status": 200,
  "message": "WhatsApp Groups",
  "data": [{ "gid": "[email protected]", "name": "Reseller Jabar" }]
}
GET/api/get/wa.group.contacts
get_wa_groups

Peserta grup (non-admin)

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.group.contacts.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
unique*querystringUnique ID akun WhatsApp (lihat get/wa.accounts).
gid*querystringID grup …@g.us.
curl -G "https://app.kirimpesan.id/api/get/wa.group.contacts" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "unique=UNIQUE_ID_AKUN"
Respons 200
{
  "status": 200,
  "message": "WhatsApp Group Contacts",
  "data": [{ "phone": "+6281234567890" }]
}
GET/api/get/wa.servers
create_whatsapp

Server WhatsApp

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.servers.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
curl -G "https://app.kirimpesan.id/api/get/wa.servers" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "WhatsApp Servers",
  "data": [{ "id": 1, "name": "Node 1", "status": "online", "available": true }]
}
GET/api/get/subscription
get_subscription

Paket & pemakaian

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/subscription.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
curl -G "https://app.kirimpesan.id/api/get/subscription" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Subscription Package",
  "data": { … }
}
GET/api/get/credits
get_credits

Sisa kredit

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/credits.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
curl -G "https://app.kirimpesan.id/api/get/credits" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Remaining Credits",
  "data": { "credits": 0, "currency": "IDR" }
}
GET/api/get/shorteners
get_shorteners

Shortener

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/shorteners.

ParameterLokasiTipeKeterangan
secret*querystringAPI key (secret) — sama dengan key v2.
curl -G "https://app.kirimpesan.id/api/get/shorteners" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Available Shorteners",
  "data": []
}
GET/api/get/wa.qr

Gambar QR (PNG)

Tanpa secret — memakai token dari create/wa.link. Respons berupa PNG.

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.qr.

ParameterLokasiTipeKeterangan
token*querystringToken dari qrimagelink.
curl -X GET "https://app.kirimpesan.id/api/get/wa.qr"
Respons 200
{ … }
GET/api/get/wa.info

Info akun setelah tertaut

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/get/wa.info.

ParameterLokasiTipeKeterangan
token*querystringToken dari infolink.
curl -X GET "https://app.kirimpesan.id/api/get/wa.info"
Respons 200
{
  "status": 200,
  "message": "WhatsApp Information",
  "data": { "wid": "[email protected]", "unique": "a1b2c3d4" }
}
Kemungkinan error (1)
  • 301Waiting for WhatsApp information!

/api/create/…

POST/api/create/wa.link
create_whatsapp Mengubah data

Tautkan akun baru (QR)

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/create/wa.link.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
sid*formintegerID server dari get/wa.servers.
curl -X POST "https://app.kirimpesan.id/api/create/wa.link" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "sid=1"
Respons 200
{
  "status": 200,
  "message": "WhatsApp QRCode has been created!",
  "data": { "qrstring": "2@…", "qrimagelink": "…/api/get/wa.qr?token=…", "infolink": "…/api/get/wa.info?token=…" }
}
POST/api/create/wa.relink
create_whatsapp Mengubah data

Tautkan ulang akun

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/create/wa.relink.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
sid*formintegerID server.
unique*formstringUnique ID akun WhatsApp (lihat get/wa.accounts).
curl -X POST "https://app.kirimpesan.id/api/create/wa.relink" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "sid=1" \
  --data-urlencode "unique=UNIQUE_ID_AKUN"
Respons 200
{ … }
POST/api/create/contact
create_contact

Buat kontak

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/create/contact.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
name*formstringNama kontak.
phone*formstringNomor kontak.
groups*formstringID grup kontak, dipisah koma.
curl -X POST "https://app.kirimpesan.id/api/create/contact" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "name=Rina" \
  --data-urlencode "phone=+6281234567890" \
  --data-urlencode "groups=1"
Respons 200
{
  "status": 200,
  "message": "Contact has been created!",
  "data": false
}
Kemungkinan error (2)
  • 400Group #x is invalid!
  • 400Phone number already exist in your contact book!
POST/api/create/group
create_group

Buat grup kontak

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/create/group.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
name*formstringNama grup.
curl -X POST "https://app.kirimpesan.id/api/create/group" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "name=Pelanggan VIP"
Respons 200
{
  "status": 200,
  "message": "Contact group has been created!",
  "data": false
}

/api/delete/…

POST/api/delete/contact
delete_contact Mengubah data

Hapus kontak

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/contact.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
id*formintegerID data yang dihapus.
curl -X POST "https://app.kirimpesan.id/api/delete/contact" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Contact has been deleted!",
  "data": false
}
POST/api/delete/group
delete_group Mengubah data

Hapus grup kontak

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/group.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
id*formintegerID data yang dihapus.
curl -X POST "https://app.kirimpesan.id/api/delete/group" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Contact group has been deleted!",
  "data": false
}
POST/api/delete/unsubscribed
delete_unsubscribed Mengubah data

Hapus dari daftar berhenti langganan

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/unsubscribed.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
id*formintegerID data yang dihapus.
curl -X POST "https://app.kirimpesan.id/api/delete/unsubscribed" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Unsubscribed contact has been deleted!",
  "data": false
}
POST/api/delete/wa.sent
delete_wa_sent Mengubah data

Hapus pesan terkirim

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/wa.sent.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
id*formintegerID data yang dihapus.
curl -X POST "https://app.kirimpesan.id/api/delete/wa.sent" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Sent WhatsApp chat has been deleted!",
  "data": false
}
POST/api/delete/wa.received
delete_wa_received Mengubah data

Hapus pesan masuk

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/wa.received.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
id*formintegerID data yang dihapus.
curl -X POST "https://app.kirimpesan.id/api/delete/wa.received" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "Received WhatsApp chat has been deleted!",
  "data": false
}
POST/api/delete/wa.campaign
delete_wa_campaign Mengubah data

Hapus campaign

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/wa.campaign.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
id*formintegerID data yang dihapus.
curl -X POST "https://app.kirimpesan.id/api/delete/wa.campaign" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "status": 200,
  "message": "WhatsApp campaign has been deleted!",
  "data": false
}
POST/api/delete/wa.account
delete_wa_account Mengubah data

Hapus akun WhatsApp

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/delete/wa.account.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
unique*formstringUnique ID akun WhatsApp (lihat get/wa.accounts).
curl -X POST "https://app.kirimpesan.id/api/delete/wa.account" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "unique=UNIQUE_ID_AKUN"
Respons 200
{
  "status": 200,
  "message": "WhatsApp account has been deleted!",
  "data": false
}

/api/remote/…

POST/api/remote/start.chats
start_wa_campaign Mengubah data

Lanjutkan campaign

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/remote/start.chats.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
campaign*formintegerID campaign.
curl -X POST "https://app.kirimpesan.id/api/remote/start.chats" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "campaign=311"
Respons 200
{
  "status": 200,
  "message": "WhatsApp campaign has been resumed!",
  "data": false
}
POST/api/remote/stop.chats
stop_wa_campaign Mengubah data

Jeda campaign

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/remote/stop.chats.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
campaign*formintegerID campaign.
curl -X POST "https://app.kirimpesan.id/api/remote/stop.chats" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "campaign=311"
Respons 200
{
  "status": 200,
  "message": "WhatsApp campaign has been paused!",
  "data": false
}

/api/validate/…

POST/api/validate/whatsapp
validate_wa_phone

Validasi nomor WhatsApp

Kini memeriksa ke WhatsApp sungguhan (dulu selalu “valid”).

HTTP selalu 200 — periksa status di body. Juga tersedia di /v3.9.7/api/validate/whatsapp.

ParameterLokasiTipeKeterangan
secret*formstringAPI key (secret) — sama dengan key v2.
unique*formstringUnique ID akun WhatsApp (lihat get/wa.accounts).
phone*formstringNomor yang dicek.
curl -X POST "https://app.kirimpesan.id/api/validate/whatsapp" \
  --data-urlencode "secret=kp_live_xxxxxxxxxxxxxxxx" \
  --data-urlencode "unique=UNIQUE_ID_AKUN" \
  --data-urlencode "phone=+6281234567890"
Respons 200
{
  "status": 200,
  "message": "WhatsApp phone number is valid!",
  "data": { "jid": "[email protected]", "phone": "+6281234567890" }
}
Kemungkinan error (1)
  • 404WhatsApp phone number does not exist!

Batas & Rate Limit

Batasan agar layanan tetap adil dan stabil, plus endpoint untuk memantau kuota paket.

Rate limit

  • API v2: 60 request/menit per API key (jendela tetap per menit). Setiap respons membawa X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (unix detik).
  • Bila terlampaui: 429 too_many_requests + header Retry-After (detik). Gunakan backoff, jangan retry beruntun.
  • OTP dibatasi per nomor tujuan (v2: 429; API lama: status 429 "Too many OTP requests for this phone number!").
  • Kuota pesan mengikuti paket (wa_send). Habis → 422 quota_exceeded (API lama: 403 "You have reached the maximum number of allowed chats!").
BatasNilai
pageSize list v2maks 100
POST /api/v2/numbers/check1–50 nomor
POST /api/v2/messages/bulk · recipientsmaks 100.000 nomor, 200 grup, 50 perangkat
Idempotency-Keymaks 200 karakter, disimpan 24 jam
Teks pesanmaks 65.536 karakter; caption 4.096
Tombol / list / polling3 tombol · 10×10 baris · 2–12 opsi
Upload file API lamamaks 100 MB, 2 file per request
GET/api/v2/usage
get_subscription

Paket & pemakaian kuota

Tanpa parameter.

curl -X GET "https://app.kirimpesan.id/api/v2/usage" \
  -H "Authorization: Bearer kp_live_xxxxxxxxxxxxxxxx"
Respons 200
{
  "plan": { "id": 3, "name": "Bisnis" },
  "status": "active",
  "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
  "usage": [
    { "metric": "wa_send", "used": 12480, "limit": 50000, "periodStart": "…", "periodEnd": "…" },
    { "metric": "wa_receive", "used": 3021, "limit": 0 },
    { "metric": "wa_account", "used": 3, "limit": 5 },
    { "metric": "contact", "used": 812, "limit": 10000 }
  ]
}

Changelog

2.0.0

2026-10
  • API v2 (/api/v2) dengan autentikasi Bearer, error JSON terstruktur, dan OpenAPI 3.1.
  • Header Idempotency-Key wajib untuk POST /api/v2/messages — mencegah pesan ganda saat retry.
  • Rate limit 60 request/menit per API key dengan header X-RateLimit-*.
  • Webhook JSON bertanda tangan HMAC-SHA256 (X-KirimPesan-Signature) dengan retry hingga ±22 jam.
  • Tipe pesan baru: lokasi, kartu kontak, tombol, list, polling, reaksi.
  • OTP memakai CSPRNG dan rate limit per nomor.

Legacy 3.9.7 (kompatibel)

2026-10
  • Semua endpoint /api/{send|get|create|delete|remote|validate}/… dan /v3.9.7/api/… tetap berjalan 1:1.
  • validate/whatsapp kini benar-benar memeriksa ke WhatsApp (sebelumnya selalu “valid”).
  • send/whatsapp.bulk tidak lagi memakai nomor kontak sebelumnya untuk nomor tidak valid; opt-out & duplikat dibuang.
  • Permission delete/wa.account & delete/wa.campaign diperbaiki (delete_wa_account, delete_wa_campaign).
  • Offset paginasi lama (page < 2 → 0, selain itu page × limit) sengaja dipertahankan.