API v2 (beta)
Webhook
Endpoint webhook ialah URL pada pelayan anda yang dipanggil oleh WbizTool apabila sesuatu berlaku pada mesej: ia dihantar, gagal, dibatalkan atau tamat tempoh. Anda mendapat berita itu serta-merta tanpa meninjau berulang kali, dan setiap permintaan ditandatangani supaya anda boleh menyemak bahawa ia datang daripada WbizTool.
| Operasi | Endpoint | Skop |
|---|---|---|
| Tambah endpoint | POST /api/v2/webhooks | messages:read |
| Senaraikan endpoint | GET /api/v2/webhooks | messages:read |
| Dapatkan endpoint | GET /api/v2/webhooks/{id} | messages:read |
| Buang endpoint | DELETE /api/v2/webhooks/{id} | messages:read |
| Ganti rahsia tandatangan | POST /api/v2/webhooks/{id}/rotate-secret | messages:read |
| Hantar peristiwa ujian | POST /api/v2/webhooks/{id}/test | messages:read |
Endpoint dimiliki oleh ruang kerja dan menerima peristiwa untuk semua mesejnya, tidak kira apa yang menghantarnya: API, papan pemuka, kempen, peringatan atau integrasi. Itulah sebabnya kunci yang dihadkan kepada sebahagian nombor tidak boleh mengurusnya: setiap panggilan menjawab 403 number_not_allowed.
Peristiwa#
| Jenis | Bila |
|---|---|
message.sent | Mesej telah dihantar dari nombor anda. |
message.failed | Mesej tidak dapat dihantar; error.message menyatakan sebabnya. |
message.cancelled | Mesej yang menunggu telah dibatalkan: dengan API, dari papan pemuka, atau kerana nombor atau kempennya dibuang. |
message.expired | Mesej tidak dihantar sebelum expires_at-nya. |
ping | Peristiwa ujian yang anda minta. |
Peristiwa untuk nombor (disambungkan, log keluar) dan untuk mesej masuk akan datang kemudian. Tanda sampai dan dibaca tidak menghantar peristiwa; dapatkan mesej itu untuk melihatnya.
Tambah endpoint#
https://wbiztool.com/api/v2/webhooksBody: JSON (application/json)
Badan
urlstringwajibEndpoint anda: alamat
https://awam, sehingga 500 aksara. Alamathttp://, peribadi dan setempat (sepertilocalhostatau192.168.x.x) ditolak.eventsarray of stringspilihanJenis peristiwa yang diterimanya: mana-mana daripada
message.sent,message.failed,message.cancelleddanmessage.expired. Lalai: keempat-empatnya.pingsentiasa tiba apabila anda memintanya.descriptionstringpilihanNota untuk diri anda sendiri, sehingga 100 aksara, seperti
Order system.
Satu ruang kerja boleh mempunyai sehingga 5 endpoint. Responsnya ialah 201 dengan endpoint dan secret tandatangannya, yang dipaparkan kali ini sahaja. Simpannya di tempat yang boleh dibaca oleh penerima anda, seperti pemboleh ubah persekitaran. Respons kemudian hanya menunjukkan secret_last4; jika anda kehilangan rahsia itu, gantikannya.
curl -X POST https://wbiztool.com/api/v2/webhooks \
-H "Authorization: Bearer $WBIZTOOL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/wbiztool/events",
"events": ["message.sent", "message.failed"],
"description": "Order system"
}'import os
import requests
response = requests.post(
"https://wbiztool.com/api/v2/webhooks",
headers={"Authorization": f"Bearer {os.environ['WBIZTOOL_API_KEY']}"},
json={
"url": "https://example.com/wbiztool/events",
"events": ["message.sent", "message.failed"],
"description": "Order system",
},
timeout=30,
)
endpoint = response.json()
print("Store this secret now:", endpoint["secret"])// Node.js 18+ (built-in fetch). Save as .mjs to use top-level await.
const response = await fetch("https://wbiztool.com/api/v2/webhooks", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.WBIZTOOL_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
url: "https://example.com/wbiztool/events",
events: ["message.sent", "message.failed"],
description: "Order system",
}),
});
const endpoint = await response.json();
console.log("Store this secret now:", endpoint.secret);<?php
$ch = curl_init('https://wbiztool.com/api/v2/webhooks');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('WBIZTOOL_API_KEY'), 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com/wbiztool/events',
'events' => ['message.sent', 'message.failed'],
'description' => 'Order system',
]),
CURLOPT_RETURNTRANSFER => true,
]);
$endpoint = json_decode(curl_exec($ch), true);
curl_close($ch);
echo 'Store this secret now: ' . $endpoint['secret'];Respons (201):
{
"id": 41,
"url": "https://example.com/wbiztool/events",
"description": "Order system",
"events": ["message.sent", "message.failed"],
"secret_last4": "9c1e",
"active": true,
"disabled_reason": null,
"created_at": "2026-10-18T11:20:05.733Z",
"last_delivery": null,
"secret": "3f0b…9c1e"
}
secret yang sebenar ialah 64 aksara heksadesimal. Cubaan semula permintaan ini dengan Idempotency-Key yang sama tidak menambah endpoint kedua, dan responsnya tidak menyertakan rahsia.
Ralat semasa menambah endpoint#
| HTTP | Kod | Bila |
|---|---|---|
400 | validation_failed | url tiada atau terlalu panjang, events kosong atau mempunyai jenis yang tidak dikenali, atau ada medan yang tidak dikenali |
403 | webhook_limit_reached | Ruang kerja sudah mempunyai 5 endpoint |
403 | number_not_allowed | Kunci dihadkan kepada sebahagian nombor |
422 | invalid_webhook_url | url bukan alamat https:// awam |
Senaraikan endpoint#
https://wbiztool.com/api/v2/webhooksEndpoint ruang kerja yang menerima peristiwa, yang tertua dahulu, dalam satu senarai data.
curl https://wbiztool.com/api/v2/webhooks \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"
Senarai ini tidak menyertakan endpoint yang telah berhenti. Pemilik juga boleh melihat endpoint, dan membuangnya, di Tetapan → Kunci API di bawah Endpoint webhook.
Dapatkan endpoint#
https://wbiztool.com/api/v2/webhooks/{id}Satu endpoint. last_delivery menunjukkan keputusan penghantaran terakhirnya. Endpoint yang tidak wujud atau telah dibuang menjawab 404 webhook_not_found; endpoint yang telah berhenti dipaparkan dengan active: false.
Buang endpoint#
https://wbiztool.com/api/v2/webhooks/{id}Tiada apa-apa lagi dihantar kepadanya, termasuk cubaan semula yang masih menunggu. Responsnya ialah {"id": 41, "deleted": true}.
Ganti rahsia tandatangan#
https://wbiztool.com/api/v2/webhooks/{id}/rotate-secretMencipta rahsia baharu untuk endpoint. Responsnya ialah 200 dengan endpoint dan secret baharunya, yang dipaparkan kali ini sahaja. Rahsia lama berhenti serta-merta: peristiwa yang dihantar selepas ini ditandatangani dengan rahsia baharu, termasuk cubaan semula peristiwa terdahulu. Kemas kini penerima anda dengan segera.
curl -X POST https://wbiztool.com/api/v2/webhooks/41/rotate-secret \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"
Hantar peristiwa ujian#
https://wbiztool.com/api/v2/webhooks/{id}/testMemasukkan peristiwa ping ke dalam baris gilir untuk endpoint ini, ditandatangani seperti setiap peristiwa, supaya anda boleh menguji penerima anda. Responsnya ialah 202:
{ "event_id": "evt_8a2f4c1d9b7e6a5f4c3b2a19", "type": "ping", "queued": true }
Endpoint anda menerima:
{
"id": "evt_8a2f4c1d9b7e6a5f4c3b2a19",
"type": "ping",
"created_at": "2026-10-18T11:21:40.002Z",
"data": { "webhook_id": 41 }
}
Payload peristiwa#
Setiap peristiwa ialah POST dengan badan JSON:
{
"id": "evt_c41d7e2a9f3b8c6d5e4f3a21",
"type": "message.sent",
"created_at": "2026-10-18T10:15:09.912Z",
"data": {
"message": {
"id": 9817263,
"status": "sent",
"from": { "id": 101, "phone": "919800000000", "platform": "whatsapp" },
"to": { "phone": "+919876543210", "contact_id": 991 },
"text": "Hi Asha, your order #1042 is ready.",
"media": null,
"send_at": null,
"expires_at": null,
"created_at": "2026-10-18T10:15:02.311Z",
"sent_at": "2026-10-18T10:15:09.870Z",
"error": null
}
}
}
| Medan | Penerangan |
|---|---|
id | ID peristiwa, evt_ diikuti 24 aksara. Peristiwa yang sama mengekalkan ID-nya pada setiap cubaan semula. |
type | Salah satu jenis peristiwa. |
created_at | Bila peristiwa itu berlaku. |
data.message | Bagi peristiwa message.*: Message seperti keadaannya pada masa itu. media.url di dalamnya berfungsi sekurang-kurangnya selama 26 jam. |
data.webhook_id | Bagi ping: ID endpoint. |
Pengepala:
| Pengepala | Nilai |
|---|---|
Content-Type | application/json |
User-Agent | WbizTool-Webhooks/2 |
WbizTool-Event-Id | id peristiwa, sama pada setiap cubaan semula |
WbizTool-Timestamp | Bila cubaan ini dihantar, dalam saat Unix, seperti 1792310409 |
WbizTool-Signature | v1= diikuti HMAC-SHA256 heks bagi <timestamp>.<raw body>, dibuat dengan rahsia endpoint |
Sahkan tandatangan#
Semak setiap permintaan sebelum anda mempercayainya: kira tandatangan daripada badan mentah (sebelum rangka kerja anda menghuraikan JSON), bandingkannya dengan pengepala dalam masa malar, dan tolak cap masa yang berbeza lebih daripada 5 minit, supaya permintaan lama tidak boleh dimainkan semula. Ia skema yang sama seperti tandatangan webhook API v1, dengan rahsia endpoint itu sendiri.
Kemudian gunakan WbizTool-Event-Id untuk membuang ulangan: peristiwa yang sama boleh tiba lebih daripada sekali, contohnya apabila respons anda hilang dan kami mencuba lagi.
import hashlib, hmac, json, os, time
SECRET = os.environ["WBIZTOOL_WEBHOOK_SECRET"]
def verify(secret: str, body: bytes, headers) -> bool:
ts, sig = headers.get("WbizTool-Timestamp", ""), headers.get("WbizTool-Signature", "")
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
return False
mac = "v1=" + hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, sig)
# Flask
from flask import Flask, request
app = Flask(__name__)
seen = set() # use your database in production
@app.post("/wbiztool/events")
def events():
body = request.get_data() # the raw bytes
if not verify(SECRET, body, request.headers):
return "", 401
event_id = request.headers["WbizTool-Event-Id"]
if event_id in seen:
return "", 200 # a repeat: already handled
seen.add(event_id)
event = json.loads(body)
if event["type"] == "message.failed":
message = event["data"]["message"]
print("Failed:", message["id"], message["error"]["message"])
return "", 200Dalam Django, hantar request.body dan request.headers.
const crypto = require("crypto");
const express = require("express");
const SECRET = process.env.WBIZTOOL_WEBHOOK_SECRET;
function verify(secret, rawBody, headers) {
const ts = String(headers["wbiztool-timestamp"] || ""), sig = String(headers["wbiztool-signature"] || "");
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const mac = "v1=" + crypto.createHmac("sha256", secret).update(ts + ".").update(rawBody).digest("hex");
return sig.length === mac.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(mac));
}
const app = express();
const seen = new Set(); // use your database in production
// express.raw keeps the body as a Buffer: the signature covers the exact bytes.
app.post("/wbiztool/events", express.raw({ type: "*/*" }), (req, res) => {
if (!verify(SECRET, req.body, req.headers)) return res.sendStatus(401);
const eventId = req.headers["wbiztool-event-id"];
if (seen.has(eventId)) return res.sendStatus(200); // a repeat: already handled
seen.add(eventId);
const event = JSON.parse(req.body);
if (event.type === "message.failed") {
const message = event.data.message;
console.log("Failed:", message.id, message.error.message);
}
res.sendStatus(200);
});
app.listen(3000);Jawab 2xx dengan cepat, kemudian lakukan kerja yang perlahan selepas itu (contohnya dalam baris gilir).
Cubaan semula#
- Jawab dengan sebarang
2xxdalam masa 10 saat. Selain itu dikira sebagai kegagalan: kod status lain (termasuk4xx), tiada jawapan dalam masa 10 saat, atau tiada sambungan dalam masa 3 saat. Ubah hala diikuti, paling banyak 3 kali, dan setiap satu mesti membawa ke alamat awam. - Penghantaran yang gagal dicuba semula selepas 1, 5 dan 15 minit, kemudian 1, 2, 4, 8 dan 8 jam: 9 cubaan dalam kira-kira 23 jam. Selepas cubaan terakhir, peristiwa itu digugurkan.
- Cubaan semula mempunyai badan dan
WbizTool-Event-Idyang sama, denganWbizTool-Timestampdan tandatangan yang baharu. - Susunan tidak dijamin.
message.sentyang dicuba semula boleh tiba selepas peristiwa yang lebih baharu. Gunakanstatusmesej, atau dapatkan mesej itu, apabila susunan penting.
last_delivery pada endpoint menunjukkan keputusan penghantaran terakhir: state (pending, delivered atau failed), status_code yang dijawab oleh pelayan anda, dan bila peristiwa itu dicipta.
Bila endpoint berhenti#
Endpoint berhenti menerima peristiwa apabila salah satu perkara ini berlaku. Ia kekal dalam GET /webhooks dengan active: false dan disabled_reason, supaya anda boleh melihat sebabnya; ia masih boleh dibaca dan dibuang, tetapi tidak boleh diuji atau diberi rahsia baharu (404).
- Ia telah gagal selama 24 jam. Jika tiada apa-apa berjaya dihantar kepadanya selama 24 jam sementara penghantarannya terus gagal, ia dimatikan dan cubaan semula yang menunggu digugurkan. Tetapan → Kunci API kemudian memaparkannya sebagai Tidak aktif. Betulkan pelayan anda, kemudian tambah endpoint semula.
- Kunci yang menciptanya ditarik balik atau tamat tempoh. Menarik balik kunci menghentikan semua yang disediakannya. Tambah endpoint semula dengan kunci lain.
- Ia dibuang, dengan Buang endpoint atau di Tetapan → Kunci API.
Endpoint yang telah berhenti tidak dikira dalam had 5.
Objek endpoint webhook#
| Medan | Jenis | Penerangan |
|---|---|---|
id | integer | ID endpoint. |
url | string | Tempat peristiwa dihantar. |
description | string | Nota anda; "" apabila tiada. |
events | array | Jenis peristiwa yang diterimanya. |
secret | string | Rahsia tandatangan: hanya dalam respons kepada Tambah endpoint dan Ganti rahsia tandatangan. |
secret_last4 | string | 4 aksara terakhir rahsia, untuk mengetahui rahsia mana yang anda miliki. |
active | boolean | false apabila ia tidak lagi menerima peristiwa – lihat Bila endpoint berhenti. |
disabled_reason | string or null | Sebab ia berhenti (gagal selama 24 jam, atau kunci yang menciptanya ditarik balik atau tamat tempoh); null semasa aktif. |
created_at | string | Bila ia ditambah. |
last_delivery | object or null | Penghantaran terakhir yang dicuba: state, status_code (atau null apabila pelayan anda tidak menjawab) dan created_at. null sebelum yang pertama. |
Berkaitan#
- Mesej: objek Message dalam
data.message - Tandatangan webhook untuk API v1
- Ralat
