Webhook Whatsapp Business
Pengantar
Status pengiriman dan data Inbox dapat diambil dengan menggunakan fitur URL Callback.
Pengaturan URL Callback dapat diatur di menu Account > Account Settings > WhatsApp Business Webhook di app.nusasms.com.
URL Callback akan dipanggil dengan metode HTTP POST dengan tipe konten JSON (aplikasi/json).
Data callback akan dikirim ke URL callback maksimal 3 kali, jika callback gagal (tidak merespons atau memberikan respons HTTP non 2XX).
Pemanggilan callback juga berhenti setelah menerima respons sukses (dengan kode status HTTP 2XX).
Status Pengiriman Pesan
Status pengiriman adalah untuk perubahan pembaruan status pesan yang dikirim oleh Gateway Anda.
Status yang tersedia:
submittedsentdeliveredread: (jika pengaturan read receipt penerima diaktifkan)failed
Callback status pengiriman mungkin dikirim dalam urutan yang tidak teratur.
Hal ini biasanya terjadi karena beberapa pembaruan status dikumpulkan dalam waktu penundaan yang sangat singkat di sistem kami.
Contoh: read dikirim sebelum delivered
Harap perhatikan urutan urutan status ideal:
- Sukses:
submitted>sent>delivered>read - Gagal:
failedatausubmitted>failed
{
"type": "status",
"status": {
"message_id": "MESSAGE_ID",
"status": "read",
"timestamp": 1766125960,
"gateway_id": "GATEWAY_ID",
"recipient": "628xxxxxxx"
}
}
Pesan Masul (Inbox)
Setelah pengaturan Webhook Whatsapp Business, anda akan menerima pesan masuk (inbox) yang diterima oleh Gateway anda.
The message payload is the data that has been received by the gateway.
{
"type": "inbox",
"inbox": {
"message_id": "MESSAGE_ID",
"timestamp": "1766124507",
"gateway": {
"id": "GATEWAY_ID",
"number": "628xxxxxxx"
},
"sender": {
"number": "628yyyyyyyyy",
"name": "Nama Akun Whatsapp Sender"
},
"message": {
"type": "text",
"text": {
"body": "Teks inbox"
}
}
}
}
Contoh Kode Webhook-nya
Contoh kode di bawah berfungsi sebagai referensi, bukan layanan pengambilan data webhook yang lengkap. Gunakan mereka sebagai dasar untuk memperluas dan menyesuaikan sesuai kebutuhan spesifik Anda.
Golang
Ini adalah contoh aplikasi web Golang sederhana untuk mengambil data webhook menggunakan net/http.
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"time"
)
// Gateway represents the gateway information in the inbox payload
type Gateway struct {
ID string `json:"id"`
Number string `json:"number"`
}
// Sender represents the sender information in the inbox payload
type Sender struct {
Number string `json:"number"`
Name string `json:"name"`
}
// MessageText represents the text message content
type MessageText struct {
Body string `json:"body"`
}
// Message represents the message details in the inbox payload
type Message struct {
Type string `json:"type"`
Text MessageText `json:"text,omitempty"`
}
// Status represents the nested status object in the status payload
type Status struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
Timestamp string `json:"timestamp"`
GatewayID string `json:"gateway_id,omitempty"`
Gateway *Gateway `json:"gateway,omitempty"`
Recipient string `json:"recipient,omitempty"`
}
// Inbox represents the inbox payload structure
type Inbox struct {
MessageID string `json:"message_id"`
Timestamp string `json:"timestamp"`
Gateway Gateway `json:"gateway"`
Sender Sender `json:"sender"`
Message Message `json:"message"`
}
// WebhookPayload represents the full JSON structure
type WebhookPayload struct {
Type string `json:"type"`
Status *Status `json:"status,omitempty"`
Inbox *Inbox `json:"inbox,omitempty"`
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
// Ensure only POST requests are accepted
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
// Limit request body size to prevent potential DoS
r.Body = http.MaxBytesReader(w, r.Body, 1048576) // 1MB max
// Read the request body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusBadRequest)
return
}
defer r.Body.Close()
// Parse the JSON
var payload WebhookPayload
err = json.Unmarshal(body, &payload)
if err != nil {
http.Error(w, fmt.Sprintf("Error parsing JSON: %v", err), http.StatusBadRequest)
return
}
// Process the webhook based on type
processWebhook(payload)
// Respond with success
w.WriteHeader(http.StatusOK)
w.Write([]byte("Webhook received successfully"))
}
func processWebhook(payload WebhookPayload) {
fmt.Printf("Received Webhook Type: %s\n", payload.Type)
switch payload.Type {
case "status":
processStatusWebhook(payload.Status)
case "inbox":
processInboxWebhook(payload.Inbox)
default:
fmt.Printf("Unknown webhook type: %s\n", payload.Type)
}
}
func processStatusWebhook(status *Status) {
// Process the data
return nil
}
func processInboxWebhook(inbox *Inbox) {
// Process the data
return nil
}
func main() {
// Set up routes
http.HandleFunc("/webhook", handleWebhook)
// Start the server
port := 8080
fmt.Printf("Server starting on port %d...\n", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}
PHP
header('Content-Type: application/json');
// Read raw POST body
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
// Helper to send a JSON response
function respond(array $payload, int $code = 200): void
{
http_response_code($code);
echo json_encode($payload);
exit;
}
// Status payload
if (isset($data['type']) && $data['type'] === 'status' && isset($data['status'])) {
$status = $data['status'];
// Basic validation (you can expand as needed)
$required = ['message_id', 'status', 'timestamp', 'gateway_id', 'recipient'];
foreach ($required as $field) {
if (!isset($status[$field])) {
respond(['error' => "Missing field $field in status payload"], 400);
}
}
// Process the data here
respond(['result' => 'status processed']);
}
// Inbox payload
if (isset($data['type']) && $data['type'] === 'inbox' && isset($data['inbox'])) {
$inbox = $data['inbox'];
// Required top‑level fields
$requiredTop = ['message_id', 'timestamp', 'gateway', 'sender', 'message'];
foreach ($requiredTop as $field) {
if (!isset($inbox[$field])) {
respond(['error' => "Missing field $field in inbox payload"], 400);
}
}
// Process the data here
respond(['result' => 'inbox processed']);
}
// -----------------------------------------------------------------
// If we reach here, the payload type is unsupported
// -----------------------------------------------------------------
respond(['error' => 'Unsupported payload type'], 400);
?>
Python
Berikut adalah contoh aplikasi web Rest API sederhana menggunakan Python FastAPI
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import Literal, Union
app = FastAPI()
class Payload(BaseModel):
type: Literal["inbox", "status"]
inbox: dict | None = None
status: dict | None = None
# ---------- Endpoints ----------
@app.post("/webhook")
async def receive_payload(payload: Payload):
"""
Accepts either a `status` or an `inbox` payload.
"""
if payload.type == 'inbox':
# Process the webhook data here
return {"result": "inbox processed"}
elif payload.type == 'status'
# Process the webhook data here
return {"result": "status processed"}
raise HTTPException(status_code=400, detail="Unsupported payload type")