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
Tip integrator

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

  1. Baca header X-RateLimit-Remaining setiap request — jika rendah (mis. < 5), perlambat pemanggilan.
  2. Jika dapat 429, baca header Retry-After untuk tahu kapan harus mencoba lagi.
  3. Implementasikan exponential backoff: tunggu 1 s, lalu 2 s, lalu 4 s, dst.
  4. Untuk bulk operation (mis. kirim ribuan pesan), gunakan queue di sisi server dan dispatch secara bertahap.
  5. 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}`);
}
Performa

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: