Webhook
Terima notifikasi otomatis ketika ada pesan masuk, status berubah, atau percakapan baru di whatsGo.
Membuat Webhook
Register webhook untuk mulai menerima event secara real-time. Setiap webhook hanya bisa memiliki satu URL, tetapi kamu bisa mendaftarkan beberapa webhook untuk URL yang berbeda.
Endpoint
POST {base_url}/v1/webhooks
Request Body
| Field | Tipe | Wajib | Deskripsi |
|---|---|---|---|
name |
string | Ya | Nama label untuk webhook (e.g. "Produksi CRM"). |
url |
string | Ya | URL endpoint kamu yang akan menerima payload. Harus HTTPS. |
events |
array | Ya | Daftar event yang ingin di-subscribe (e.g. ["message.inbound", "message.sent"]). |
secret |
string | Tidak | Secret key untuk verifikasi signature HMAC-SHA256. Jika tidak disediakan, sistem akan generate otomatis. |
Contoh Request
curl -X POST "https://whats.sahabatmobile.com/external-api/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Produksi CRM",
"url": "https://crm.example.com/webhooks/whatsapp",
"events": [
"message.inbound",
"message.sent",
"message.delivered",
"message.read",
"conversation.opened"
]
}'
Contoh Response
{
"success": true,
"message": "Webhook berhasil dibuat.",
"data": {
"id": 1,
"name": "Produksi CRM",
"url": "https://crm.example.com/webhooks/whatsapp",
"events": [
"message.inbound",
"message.sent",
"message.delivered",
"message.read",
"conversation.opened"
],
"secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"is_active": true,
"created_at": "2025-07-16T10:00:00.000000Z"
}
}
Field secret hanya dikembalikan saat pembuatan webhook. Simpan secret ini di tempat yang aman. Jika kamu kehilangannya, buat ulang webhook atau gunakan endpoint update untuk mengatur secret baru.
Daftar Event
whatsGo mendukung beberapa kategori event yang dikirim ke webhook kamu:
| Event | Deskripsi |
|---|---|
message.inbound |
Pesan baru diterima dari pengguna WhatsApp. |
message.sent |
Pesan berhasil dikirim dari whatsGo. |
message.delivered |
Pesan berhasil diterima di perangkat tujuan. |
message.read |
Pesan telah dibaca oleh penerima. |
message.failed |
Pesan gagal dikirim karena error tertentu. |
contact.created |
Kontak baru terdaftar di workspace. |
contact.updated |
Data kontak diperbarui. |
conversation.opened |
Percakapan baru dibuka oleh pengguna. |
conversation.assigned |
Percakapan ditugaskan ke agent. |
conversation.resolved |
Percakapan ditandai selesai. |
Headers yang Dikirim whatsGo
Setiap request webhook menyertakan header berikut untuk membantu kamu memproses dan memverifikasi payload:
| Header | Deskripsi |
|---|---|
X-WhatsGo-Event |
Tipe event (e.g. message.inbound). |
X-WhatsGo-Timestamp |
Unix timestamp saat event dibuat (dalam detik). |
X-WhatsGo-Delivery-Id |
ID unik pengiriman webhook untuk deduplikasi. |
X-WhatsGo-Signature |
Signature HMAC-SHA256 untuk memverifikasi keaslian payload (format: sha256=...). |
Format Payload
Semua webhook payload dikirim sebagai JSON dengan struktur top-level berikut:
Struktur Top-Level
{
"id": "evt_a1b2c3d4e5f6",
"event": "message.inbound",
"timestamp": 1752662400,
"data": {
// ... field spesifik event
}
}
| Field | Tipe | Deskripsi |
|---|---|---|
id |
string | ID unik event. |
event |
string | Tipe event yang dikirim. |
timestamp |
integer | Unix timestamp saat event terjadi (detik). |
data |
object | Payload data yang relevan dengan event. |
Payload: message.inbound
{
"id": "evt_m1n2b3o4u5n6d",
"event": "message.inbound",
"timestamp": 1752662400,
"data": {
"message_id": "msg_abc123def456",
"from": "6281234567890",
"to": "6289876543210",
"type": "text",
"body": "Halo, saya ingin bertanya tentang pesanan saya.",
"media_url": null,
"timestamp": 1752662400,
"conversation_id": "conv_xyz789"
}
}
| Field | Tipe | Deskripsi |
|---|---|---|
message_id |
string | ID unik pesan. |
from |
string | Nomor telepon pengirim dalam format internasional. |
to |
string | Nomor telepon tujuan (saluran WhatsApp kamu). |
type |
string | Jenis pesan: text, image, audio, video, document, location, sticker. |
body |
string|null | Teks isi pesan. null untuk pesan media tanpa caption. |
media_url |
string|null | URL file media (berlaku untuk tipe image, audio, video, document). Expired setelah 24 jam. |
timestamp |
integer | Unix timestamp saat pesan diterima. |
conversation_id |
string | ID percakapan terkait. |
Payload: contact.created
{
"id": "evt_c1r2e3a4t5e6d",
"event": "contact.created",
"timestamp": 1752662400,
"data": {
"contact_id": 42,
"phone": "6285512345678",
"name": "Rina Wati",
"email": null,
"tags": ["leads"],
"custom_fields": {
"source": "website"
},
"created_at": "2025-07-16T10:00:00.000000Z"
}
}
Payload: conversation.opened
{
"id": "evt_o1p2e3n4e5d6",
"event": "conversation.opened",
"timestamp": 1752662400,
"data": {
"conversation_id": "conv_xyz789",
"contact": {
"id": 42,
"phone": "6285512345678",
"name": "Rina Wati"
},
"channel": "whatsapp",
"status": "open",
"created_at": "2025-07-16T10:00:00.000000Z"
}
}
Payload: conversation.assigned
{
"id": "evt_a1s2s3i4g5n6",
"event": "conversation.assigned",
"timestamp": 1752662400,
"data": {
"conversation_id": "conv_xyz789",
"contact": {
"id": 42,
"phone": "6285512345678",
"name": "Rina Wati"
},
"agent": {
"id": 7,
"name": "Agent Budi",
"email": "budi@example.com"
},
"assigned_at": "2025-07-16T10:05:00.000000Z"
}
}
Verifikasi Signature
whatsGo menggunakan HMAC-SHA256 untuk menandatangani setiap payload webhook. Kamu harus memverifikasi signature ini untuk memastikan request benar-benar berasal dari whatsGo.
Cara Kerja
Signature dihitung dari body payload JSON menggunakan secret key yang kamu atur saat membuat webhook. Format header signature adalah:
X-WhatsGo-Signature: sha256=<hex_digest>
Mengapa Menggunakan Timestamp?
Timestamp dalam header X-WhatsGo-Timestamp memungkinkan kamu mencegah replay attack. Bandingkan timestamp dengan waktu server kamu — jika perbedaan lebih dari 5 menit, tolak request tersebut.
Node.js + Express
const crypto = require('crypto');
const express = require('express');
const app = express();
const WEBHOOK_SECRET = 'whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6';
app.post('/webhooks/whatsapp', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-whatsgo-signature'];
const timestamp = req.headers['x-whatsgo-timestamp'];
const payload = req.body.toString();
// Cek timestamp (tolak jika lebih dari 5 menit)
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (age > 300) {
return res.status(400).json({ error: 'Timestamp terlalu lama' });
}
// Hitung signature
const signedPayload = `${timestamp}.${payload}`;
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(signedPayload)
.digest('hex');
// Bandingkan secara aman (timing-safe)
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).json({ error: 'Signature tidak valid' });
}
// Signature valid, proses payload
const event = JSON.parse(payload);
console.log(`Event diterima: ${event.event}`);
res.status(200).json({ received: true });
});
app.listen(3000);
Python + Flask
import hmac
import hashlib
import time
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = 'whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6'
@app.route('/webhooks/whatsapp', methods=['POST'])
def webhook():
signature = request.headers.get('X-WhatsGo-Signature', '')
timestamp = request.headers.get('X-WhatsGo-Timestamp', '')
payload = request.data.decode('utf-8')
# Cek timestamp (tolak jika lebih dari 5 menit)
age = int(time.time()) - int(timestamp)
if age > 300:
return jsonify({'error': 'Timestamp terlalu lama'}), 400
# Hitung signature
signed_payload = f'{timestamp}.{payload}'
expected = 'sha256=' + hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
signed_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Bandingkan secara aman
if not hmac.compare_digest(signature, expected):
return jsonify({'error': 'Signature tidak valid'}), 401
# Signature valid, proses payload
event = request.get_json()
print(f'Event diterima: {event["event"]}')
return jsonify({'received': True}), 200
Praktik Terbaik
- Selalu verifikasi signature sebelum memproses payload. Jangan pernah mempercayai request tanpa validasi.
- Cek timestamp untuk mencegah replay attack. Tolak request dengan timestamp lebih dari 5 menit yang lalu.
- Response cepat — kirim response 200 dalam 5 detik. whatsGo akan melakukan retry jika endpoint kamu timeout atau return error.
- Proses secara async — terima payload, kirim 200, lalu proses di background queue untuk menghindari timeout.
- Handle retry — whatsGo melakukan retry exponential backoff sampai 5 kali. Gunakan
X-WhatsGo-Delivery-Iduntuk deduplikasi. - Gunakan HTTPS — URL webhook harus menggunakan HTTPS untuk keamanan. HTTP tidak didukung.
- Monitor endpoint — pantau uptime dan latensi endpoint webhook kamu. whatsGo akan menonaktifkan webhook yang terus-menerus gagal.
- Logging — log semua event yang diterima untuk debugging dan audit trail.
whatsGo melakukan retry dengan interval: 10 detik, 60 detik, 5 menit, 30 menit, 2 jam. Webhook akan dinonaktifkan otomatis jika gagal selama 7 hari berturut-turut.