Konvensi & Format
Aturan request/response, rate limit, format nomor telepon, pagination, dan inkonsistensi naming yang perlu kamu ketahui.
Halaman ini merangkum aturan umum yang berlaku di semua endpoint API whatsGo. Pastikan kamu membaca bagian ini sebelum mulai mengintegrasikan.
Format request
Semua endpoint menerima JSON. Setiap request wajib menyertakan dua header:
Authorization: Bearer wg_sk_xxxxx
Content-Type: application/json
Body request (untuk method POST / PATCH) selalu berupa JSON yang
valid. Field yang tidak diketahui akan diabaikan tanpa error.
Format response
Setiap response API memakai envelope yang sama. Tidak ada response
"telanjang" — selalu ada success flag.
Response sukses
{
"success": true,
"data": {
"id": "...",
"name": "..."
}
}
Untuk endpoint yang mengembalikan list (GET dengan pagination), struktur tambahan:
{
"success": true,
"data": [
{ "id": "...", "name": "..." }
],
"meta": {
"page": 1,
"per_page": 20,
"total": 142,
"has_more": true
}
}
Response error
{
"success": false,
"error": {
"code": "VALIDATION_INVALID_PHONE",
"message": "Format nomor telepon tidak valid",
"details": {
"field": "to",
"hint": "Gunakan format kode negara + nomor (contoh: 628123456789)"
}
}
}
Field error.details opsional — beberapa error tidak menyertakannya.
Kode HTTP mengikuti makna umum (400 validasi, 401 auth, 404
tidak ditemukan, 409 konflik, 429 rate limit, 5xx server).
Inkonsistensi naming yang perlu kamu tahu
Field di response API whatsGo mencampur camelCase dan snake_case tergantung endpoint. Ini history dari evolusi API — akan distandarkan di v2. Sementara itu, perhatikan polanya:
| Domain | Convention | Contoh |
|---|---|---|
| Pesan WhatsApp | camelCase | messageId, whatsappMessageId, channelId, contentType |
| Kontak | snake_case | custom_fields, created_at, updated_at |
| Webhook payload | camelCase di data, snake_case di top-level |
organization_id (top), messageId (data) |
| Pagination meta | snake_case | per_page, has_more |
Generate type binding (mis. TypeScript interface) per-endpoint —
jangan berasumsi convention global. Atau pakai library transformasi
case di sisi client kamu (mis. humps untuk JavaScript).
Rate limit
Setiap API key memiliki batasan request:
| Batas | Default |
|---|---|
| Per menit | 60 request |
| Per hari | 10.000 request |
Setiap response menyertakan tiga header untuk memantau kuota:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1708123456
Jika kamu melebihi limit, response berikutnya akan mengembalikan
status 429 dengan body:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Terlalu banyak request. Coba lagi setelah 30 detik."
}
}
Strategi mengatasi rate limit
- Baca header
X-RateLimit-Remainingsetiap request — jika rendah (mis. < 5), perlambat pemanggilan. - Jika dapat
429, baca headerRetry-Afteruntuk tahu kapan harus mencoba lagi. - Implementasikan exponential backoff: tunggu 1 s, lalu 2 s, lalu 4 s, dst.
- Untuk bulk operation (mis. kirim ribuan pesan), gunakan queue di sisi server dan dispatch secara bertahap.
- Manfaatkan webhook daripada polling — lebih hemat kuota dan lebih real-time.
Format nomor telepon (E.164)
Semua endpoint yang menerima nomor telepon menggunakan format E.164:
kode negara diikuti nomor tanpa spasi, tanda hubung, atau prefix +.
| Format | Contoh | Status |
|---|---|---|
| E.164 (disarankan) | 6281234567890 |
Diterima |
Prefix + |
+6281234567890 |
Diterima (otomatis di-strip) |
| Nomor lokal | 081234567890 |
Ditolak |
| Dengan spasi / strip | 0812-3456-7890 |
Ditolak |
| Format internasional salah | 62 81234567890 |
Ditolak |
Fungsi konversi nomor lokal ke E.164
function toE164(phone, countryCode = '62') {
// Strip semua karakter non-digit
let digits = phone.replace(/\D/g, '');
// Jika sudah diawali kode negara, kembalikan langsung
if (digits.startsWith(countryCode)) return digits;
// Jika diawali 0, buang 0 lalu prepend kode negara
if (digits.startsWith('0')) {
return countryCode + digits.substring(1);
}
// Fallback: tambahkan kode negara
return countryCode + digits;
}
// Contoh:
toE164('081234567890'); // → '6281234567890'
toE164('+62 812 345 6789'); // → '6281234567890'
toE164('81234567890', '62'); // → '6281234567890'
Pagination
Endpoint yang mengembalikan list mendukung pagination kursor-ofset dengan dua parameter query:
| Parameter | Tipe | Default | Keterangan |
|---|---|---|---|
page |
integer | 1 | Nomor halaman (dimulai dari 1) |
per_page |
integer | 20 | Jumlah item per halaman (maks 100) |
Contoh loop pagination
async function fetchAllContacts() {
const BASE = 'https://whats.sahabatmobile.com/external-api';
const TOKEN = 'wg_sk_xxxxx';
let page = 1;
let all = [];
while (true) {
const res = await fetch(
`${BASE}/contacts?page=${page}&per_page=100`,
{ headers: { Authorization: `Bearer ${TOKEN}` } }
);
const json = await res.json();
all.push(...json.data);
if (!json.meta.has_more) break;
page++;
}
console.log(`Total kontak: ${all.length}`);
}
Menggunakan per_page lebih besar (maks. 100) mengurangi jumlah request.
Namun hindari mengambil terlalu banyak sekaligus jika payload-nya besar —
gunakan streaming atau batch untuk dataset ribuan.
Idempotensi
Beberapa endpoint (terutama kirim pesan) mendukung idempotensi
dengan header Idempotency-Key. Kirim header ini untuk memastikan
request yang sama tidak diproses lebih dari sekali.
POST /external-api/whatsapp/messages
Idempotency-Key: msg-abc-123-def
Content-Type: application/json
{
"to": "6281234567890",
"type": "text",
"text": { "body": "Halo, ini pesan uji." }
}
Aturan idempotensi:
- Key berlaku selama 24 jam sejak request pertama.
- Jika key sudah pernah digunakan, API mengembalikan response yang sama (bukan error).
- Key harus unik per kombinasi endpoint + payload. Jangan pakai key yang sama untuk pesan berbeda.
- Jika tidak ada header
Idempotency-Key, setiap request dianggap baru.