प्रोडक्ट गाइड
इनकमिंग संदेश वेबहुक (लिस्नर)
लिस्नर आपके किसी WhatsApp नंबर को Unibox से जोड़ता है और आपके सर्वर पर इनकमिंग मैसेज वेबहुक भेज सकता है। डैशबोर्ड में लिस्नर इनकमिंग ट्रिगर्स पेज पर मैनेज किए जाते हैं। किसी नंबर के लिस्नर बनते ही उसकी चैट Unibox इनबॉक्स में सिंक होने लगती हैं, और अगर आप वेबहुक URL जोड़ते हैं, तो Wbiztool हर नया संदेश आपके सर्वर पर भेजता है। बातचीत को CRM में दर्ज करने, अपनी टीम को अलर्ट करने या ऑटो-रिप्लाई बनाने के लिए वेबहुक का उपयोग करें।
शुरू करने से पहले#
- Unibox ऐड-ऑन। इसकी कीमत हर WhatsApp नंबर के लिए $20/महीना या $200/साल है, और आप जितने लिस्नर रख सकते हैं, वह ऐड-ऑन की मात्रा के बराबर है। इसे डैशबोर्ड के अंदर बिलिंग और प्लान्स पर उपलब्ध Add-ons में खरीदें। इसके बिना भी इनकमिंग ट्रिगर्स पेज खुलता है, लेकिन नया लिस्नर जोड़ें पर क्लिक करने पर Unibox Add-on Required (Unibox ऐड-ऑन ज़रूरी) दिखता है, जिसमें Subscribe to Unibox Add-on (Unibox ऐड-ऑन सब्सक्राइब करें) बटन होता है।
- WhatsApp सेटिंग्स पर एक कनेक्टेड WhatsApp नंबर। सिर्फ वही कनेक्टेड नंबर जोड़े जा सकते हैं जो अभी लिस्नर नहीं हैं।
- आपको वर्कस्पेस का Owner या Editor होना चाहिए।
- वेबहुक के लिए: एक पब्लिक URL (
httpsका उपयोग करें), 100 या उससे कम अक्षरों का, जो JSON बॉडी के साथPOSTरिक्वेस्ट स्वीकार करता हो।
लिस्नर जोड़ें#
इनकमिंग ट्रिगर्स खोलें
साइडबार में Unibox खोलें और इनकमिंग ट्रिगर्स पर क्लिक करें, या इनकमिंग ट्रिगर्स पर जाएं।
नया लिस्नर शुरू करें
नया लिस्नर जोड़ें कार्ड पर क्लिक करें।
नंबर चुनें
WhatsApp नंबर चुनें में नंबर चुनें। अगर सूची में कोई उपलब्ध WhatsApp नंबर नहीं लिखा है, तो हर कनेक्टेड नंबर पहले से लिस्नर है, या कोई नंबर कनेक्ट नहीं है।
वेबहुक जोड़ें (वैकल्पिक)
अपना Webhook URL (वैकल्पिक) डालें। URL टाइप करते ही Webhook Events (वेबहुक इवेंट) दिखता है: Incoming Messages (आने वाले संदेश), Outgoing Messages (जाने वाले संदेश) या दोनों पर टिक रहने दें। URL आप बाद में भी जोड़ या बदल सकते हैं।
सेव करें
लिस्नर जोड़ें पर क्लिक करें। लिस्नर Active (सक्रिय) स्टेटस के साथ एक कार्ड के रूप में दिखता है। अगर आपने URL डाला है, तो उसके लिए एक वेबहुक सीक्रेट बन जाता है।
लिस्नर मैनेज करें#
हर कार्ड पर नंबर, उसका स्टेटस, Webhook URL: (या कॉन्फ़िगर नहीं), अंतिम गतिविधि: (नंबर पर संदेश आखिरी बार कब जांचे गए, या कभी नहीं) और Webhook Secret: दिखता है, जो आंख वाले बटन पर क्लिक करने तक छिपा रहता है।
कार्ड पर ⋮ मेनू खोलकर आप ये कर सकते हैं:
| कार्य | क्या होता है |
|---|---|
| संपादित करें | वेबहुक URL, वेबहुक इवेंट या सीक्रेट बदलें। नंबर नहीं बदला जा सकता। |
| अक्षम करें / सक्षम करें | अक्षम करने पर लिस्नर Inactive (निष्क्रिय) हो जाता है: नंबर का इनबॉक्स में सिंक रुक जाता है और कोई वेबहुक नहीं भेजा जाता। सक्षम करने पर यह फिर से Active हो जाता है। |
| हटाएं | कन्फर्म करने के बाद लिस्नर हटा देता है। इनबॉक्स में पहले से मौजूद बातचीत बनी रहती हैं। बाद में वही नंबर फिर से जोड़ने पर लिस्नर वापस आ जाता है। अगर दोबारा जोड़ते समय आप वेबहुक URL डालते हैं, तो लिस्नर को नया सीक्रेट नहीं मिलता, बल्कि उसका पिछला वेबहुक सीक्रेट (अगर था) बना रहता है। |
लिस्नर स्टेटस#
| स्टेटस | मतलब |
|---|---|
| Active (सक्रिय) | नंबर कनेक्टेड रहने तक संदेश सिंक होते हैं और वेबहुक भेजे जाते हैं। |
| Pending (पेंडिंग) | लिस्नर बनाते समय नंबर कनेक्टेड नहीं था, जैसे जब Zapier ने इसे बनाया। नंबर कनेक्ट होने के बाद सक्षम करें पर क्लिक करें। |
| Inactive (निष्क्रिय) | अक्षम। कुछ सिंक नहीं होता और कोई वेबहुक नहीं भेजा जाता। |
आंकड़े#
| कार्ड | क्या दिखाता है |
|---|---|
| सक्रिय लिस्नर | पेज के सभी लिस्नर, अक्षम किए गए भी। |
| उपलब्ध नंबर | कनेक्टेड नंबर जो अभी लिस्नर नहीं हैं। जिन नंबरों का लिस्नर आपने डिलीट किया है, वे यहां अब भी इस्तेमाल हुए गिने जाते हैं, इसलिए यह असल में जोड़े जा सकने वाले नंबरों से कम दिखा सकता है। |
| कुल सीमा | आपका Unibox ऐड-ऑन कितने लिस्नर की अनुमति देता है। |
| आज के मैसेज | अभी ट्रैक नहीं होता; हमेशा 0 दिखाता है। |
वेबहुक URL या इवेंट बदलें#
लिस्नर खोलें
कार्ड पर ⋮ पर क्लिक करें, फिर संपादित करें पर।
सेटिंग्स अपडेट करें
Webhook URL (वैकल्पिक) और Webhook Events बदलें। इस नंबर के वेबहुक बंद करने के लिए URL खाली कर दें: इसका सीक्रेट भी हट जाता है, और URL फिर से जोड़ने पर नया सीक्रेट बनता है।
सेव करें
Update Listener (लिस्नर अपडेट करें) पर क्लिक करें।
सीक्रेट दोबारा बनाएं#
Edit Listener (लिस्नर एडिट करें) में Webhook Secret के पास वाले रिफ्रेश बटन पर क्लिक करें और कन्फर्म करें। नया सीक्रेट तुरंत सेव हो जाता है, भले ही आप इसके बाद Update Listener पर क्लिक किए बिना डायलॉग बंद कर दें, और उसके बाद से रिक्वेस्ट इसी से साइन होती हैं। अपने सर्वर पर तुरंत नया सीक्रेट डालें।
वेबहुक कैसे डिलीवर होते हैं#
नंबर सिंक होने पर मिले हर नए संदेश के लिए Wbiztool आपके URL पर एक POST रिक्वेस्ट भेजता है। सिंक हर कुछ मिनट में होता है, जब तक नंबर कनेक्टेड है और संदेश भेजने में व्यस्त नहीं है।
- इवेंट: लोग आपके नंबर पर जो संदेश भेजते हैं उनके लिए
message_received, और उससे भेजे गए संदेशों (फोन, कैंपेन या API से) के लिएmessage_sent। सिर्फ वही इवेंट भेजे जाते हैं जो Webhook Events में टिक हैं। - Unibox इनबॉक्स से दिए गए जवाब आमतौर पर
message_sentट्रिगर नहीं करते, क्योंकि सिंक चलने तक इनबॉक्स में वे पहले से होते हैं। - रिस्पॉन्स: 8 सेकंड के अंदर HTTP
200के साथ जवाब दें। कोई भी दूसरा रिस्पॉन्स या टाइमआउट फेल डिलीवरी माना जाता है। - कोई रीट्राई नहीं: हर संदेश एक ही बार भेजा जाता है। अगर आपका सर्वर डाउन है, तो वह वेबहुक खो जाता है।
- क्रम: रिक्वेस्ट अलग-अलग भेजी जाती हैं और आगे-पीछे पहुंच सकती हैं। अगर क्रम मायने रखता है, तो
message.timestampसे सॉर्ट करें।
हेडर#
| हेडर | वैल्यू |
|---|---|
Content-Type | application/json |
X-Wbiztool-Event | message_received या message_sent |
X-Wbiztool-Timestamp | वेबहुक कब भेजा गया, ISO 8601 UTC में। बॉडी के timestamp जैसा ही। |
X-Wbiztool-Webhook-Id | लिस्नर का ID। बॉडी के webhook_id जैसा ही। |
X-Wbiztool-Signature | sha256= और उसके बाद सिग्नेचर। जब भी लिस्नर का सीक्रेट हो तब भेजा जाता है, और URL सेट होने पर सीक्रेट हमेशा होता है। |
पेलोड#
{
"event": "message_received",
"timestamp": "2026-09-16T10:31:12.482913+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected]_3EB0C1A2B3D4E5F60718",
"type": "chat",
"content": "Hi, is my order #4821 out for delivery?",
"from": "919876543210",
"from_name": "Aman",
"to": "919812345678",
"timestamp": "2026-09-16T10:29:58+00:00",
"whatsapp_timestamp": 1789554598,
"direction": "incoming",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "919876543210",
"name": "Aman",
"is_group": false,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
}
}{
"event": "message_sent",
"timestamp": "2026-09-16T10:33:40.117205+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected]_3EB0F9E8D7C6B5A40312",
"type": "chat",
"content": "Yes, it will reach you today by 6 PM.",
"from": "919876543210",
"from_name": "Aman",
"to": "919812345678",
"timestamp": "2026-09-16T10:31:04+00:00",
"whatsapp_timestamp": 1789554664,
"direction": "outgoing",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "919876543210",
"name": "Aman",
"is_group": false,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
}
}{
"event": "message_received",
"timestamp": "2026-09-16T10:36:02.904311+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected]_3EB0A7B6C5D4E3F20109",
"type": "image",
"content": "📸 Image",
"from": "919876543210",
"from_name": "Aman",
"to": "919812345678",
"timestamp": "2026-09-16T10:34:51+00:00",
"whatsapp_timestamp": 1789554891,
"direction": "incoming",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0,
"media": {
"filename": "",
"mimetype": "image/jpeg",
"size": 245760
}
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "919876543210",
"name": "Aman",
"is_group": false,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
}
}{
"event": "message_received",
"timestamp": "2026-09-16T10:40:15.330187+00:00",
"webhook_id": 42,
"whatsapp_client_id": "678",
"whatsapp_phone": "919812345678",
"message": {
"id": "[email protected][email protected]",
"type": "chat",
"content": "Is the store open on Sunday?",
"from": "120363041234567890",
"from_name": "Acme Loyalty Club",
"to": "919812345678",
"timestamp": "2026-09-16T10:39:02+00:00",
"whatsapp_timestamp": 1789555142,
"direction": "incoming",
"status": "pending",
"is_forwarded": false,
"forwarding_score": 0
},
"contact": {
"whatsapp_id": "[email protected]",
"phone": "120363041234567890",
"name": "Acme Loyalty Club",
"is_group": true,
"is_business": false
},
"organisation": {
"id": "10314",
"name": "Acme Stores"
},
"group": {
"name": "Acme Loyalty Club"
}
}ऊपर दिए नंबर, ID और नाम उदाहरण हैं।
टॉप-लेवल फील्ड#
| फील्ड | टाइप | विवरण |
|---|---|---|
event | string | message_received या message_sent। |
timestamp | string | वेबहुक कब भेजा गया (ISO 8601, UTC)। |
webhook_id | integer | लिस्नर का ID। |
whatsapp_client_id | string | आपके WhatsApp नंबर का ID, जैसा WhatsApp सेटिंग्स पर दिखता है। |
whatsapp_phone | string | आपका WhatsApp नंबर। |
message | object | संदेश। नीचे देखें। |
contact | object | वह व्यक्ति या ग्रुप जिसके साथ बातचीत है। नीचे देखें। |
organisation | object | आपके वर्कस्पेस का id (string) और name। |
group | object | सिर्फ ग्रुप चैट के लिए: name, ग्रुप का नाम। |
message फील्ड#
| फील्ड | टाइप | विवरण |
|---|---|---|
id | string | संदेश के लिए WhatsApp का ID। डुप्लीकेट को अनदेखा करने के लिए इसका उपयोग करें। |
type | string | टेक्स्ट के लिए chat। बाकी के लिए WhatsApp का टाइप, जैसे image, video, audio, ptt (वॉइस नोट), document, sticker या location। |
content | string | chat संदेशों के लिए टेक्स्ट। मीडिया के लिए इसकी जगह एक लेबल: 📸 Image, 🎥 Video, 🎵 Audio, 🎤 Voice Message, 😊 Sticker, Document: और उसके बाद डॉक्यूमेंट का टेक्स्ट (टेक्स्ट न होने पर Document), या बाकी किसी भी चीज़ के लिए title case में टाइप का नाम, जैसे Location। कैप्शन शामिल नहीं होते। |
from | string | हमेशा संपर्क का नंबर (या ग्रुप का ID), दोनों दिशाओं में। |
from_name | string | संपर्क या ग्रुप का नाम। खाली हो सकता है। |
to | string | हमेशा आपका WhatsApp नंबर, दोनों दिशाओं में। |
timestamp | string | संदेश WhatsApp पर कब भेजा गया (ISO 8601, UTC)। |
whatsapp_timestamp | integer | वही समय, सेकंड में Unix टाइमस्टैम्प के रूप में। |
direction | string | incoming या outgoing। दिशा जानने के लिए from और to नहीं, इसका उपयोग करें। |
status | string | अभी हमेशा pending। डिलीवरी या पढ़े जाने के स्टेटस के लिए इस पर भरोसा न करें। |
is_forwarded | boolean | संदेश फॉरवर्ड किया गया था या नहीं। |
forwarding_score | integer | यह कितनी बार फॉरवर्ड हुआ। |
media | object | मीडिया संदेशों के लिए, जब जानकारी उपलब्ध हो: filename, mimetype और बाइट में size। फाइल खुद शामिल नहीं होती। |
quoted_message_id | string | सिर्फ तब, जब संदेश किसी दूसरे संदेश का जवाब हो। |
contact फील्ड#
| फील्ड | टाइप | विवरण |
|---|---|---|
whatsapp_id | string | WhatsApp ID, जैसे किसी व्यक्ति के लिए [email protected] या ग्रुप के लिए …@g.us। |
phone | string | + के बिना नंबर, या ग्रुप के लिए ग्रुप का ID। |
name | string | संपर्क के लिए Wbiztool में दर्ज नाम, या ग्रुप का नाम। खाली हो सकता है। |
is_group | boolean | ग्रुप चैट के लिए true। |
is_business | boolean | WhatsApp Business अकाउंट के लिए true, जब यह पता हो। |
सिग्नेचर वेरिफाई करें#
हर रिक्वेस्ट आपके लिस्नर के सीक्रेट से HMAC-SHA256 का उपयोग करके साइन की जाती है। सिग्नेचर रिक्वेस्ट की रॉ बॉडी पर, ठीक वैसी ही जैसी मिली है, कैलकुलेट किया जाता है, और X-Wbiztool-Signature में sha256= के साथ लोअरकेस hex डाइजेस्ट के रूप में भेजा जाता है।
JSON पार्स करने से पहले हमेशा रॉ बाइट्स से सिग्नेचर कैलकुलेट करें। बॉडी को पार्स करके फिर से एनकोड करने से वह बदल जाती है (जैसे, गैर-अंग्रेज़ी अक्षर और इमोजी \uXXXX के रूप में एस्केप होकर आते हैं), और सिग्नेचर मेल नहीं खाएगा।
// Express: keep the raw body for this route
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.WBIZTOOL_WEBHOOK_SECRET;
app.post("/wbiztool/webhook", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
const received = req.get("X-Wbiztool-Signature") || "";
const valid =
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send("Invalid signature");
const data = JSON.parse(req.body.toString("utf8"));
if (data.event === "message_received") {
console.log(`New message from ${data.contact.phone}: ${data.message.content}`);
}
res.sendStatus(200); // reply quickly; do slow work in the background
});
app.listen(3000);# Flask
import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["WBIZTOOL_WEBHOOK_SECRET"].encode()
@app.post("/wbiztool/webhook")
def wbiztool_webhook():
raw_body = request.get_data() # raw bytes, before parsing
expected = "sha256=" + hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
received = request.headers.get("X-Wbiztool-Signature", "")
if not hmac.compare_digest(expected, received):
abort(401)
data = json.loads(raw_body)
if data["event"] == "message_received":
print(f"New message from {data['contact']['phone']}: {data['message']['content']}")
return "", 200<?php
$secret = getenv('WBIZTOOL_WEBHOOK_SECRET');
$rawBody = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
$received = $_SERVER['HTTP_X_WBIZTOOL_SIGNATURE'] ?? '';
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit('Invalid signature');
}
$data = json_decode($rawBody, true);
if ($data['event'] === 'message_received') {
error_log('New message from ' . $data['contact']['phone'] . ': ' . $data['message']['content']);
}
http_response_code(200);सिग्नेचर में टाइमस्टैम्प हेडर शामिल नहीं होता, इसलिए यह किसी रिक्वेस्ट को दोबारा भेजे जाने (replay) से नहीं बचाता। अगर यह मायने रखता है, तो प्रोसेस किया गया हर message.id सेव करें और दोहराए गए को अनदेखा करें।
समस्या निवारण#
| संदेश या समस्या | क्या करें |
|---|---|
| नया लिस्नर जोड़ें पर क्लिक करने पर Unibox Add-on Required (Unibox ऐड-ऑन ज़रूरी) दिखता है | आपके वर्कस्पेस में Unibox ऐड-ऑन नहीं है। इसे डैशबोर्ड के अंदर बिलिंग और प्लान्स पर उपलब्ध Add-ons में खरीदें। |
You have reached your unibox numbers limit | कोई ऐसा लिस्नर डिलीट करें जिसकी अब ज़रूरत नहीं है, या ऐड-ऑन की मात्रा बढ़ाएं। |
| कोई उपलब्ध WhatsApp नंबर नहीं | कोई दूसरा नंबर कनेक्ट करें, या वह नंबर पहले से लिस्नर है। |
This WhatsApp number is already a listener | इसके बजाय मौजूदा कार्ड एडिट करें। |
Invalid WhatsApp client | नंबर डिसकनेक्ट हो गया है। उसे WhatsApp सेटिंग्स पर फिर से कनेक्ट करें और पेज रीलोड करें। |
सेव करते समय value too long वाली एरर | वेबहुक URL 100 अक्षरों से लंबा है। छोटे URL का उपयोग करें। |
| कोई वेबहुक नहीं आता | जांचें कि लिस्नर Active है, नंबर कनेक्टेड है, इवेंट टाइप टिक है, और आपका URL मान्य सर्टिफिकेट वाला पब्लिक https है। संदेश सिर्फ अगले सिंक के बाद, कुछ मिनट बाद, भेजे जाते हैं। |
| कुछ वेबहुक नहीं आए | आपके सर्वर ने 200 के अलावा कुछ और लौटाया, 8 सेकंड से ज़्यादा समय लिया या उस तक पहुंचा नहीं जा सका। फेल डिलीवरी दोबारा नहीं भेजी जातीं। |
| सिग्नेचर मेल नहीं खाता | रॉ बॉडी का उपयोग करें, दोबारा एनकोड किए गए JSON का नहीं, और मौजूदा सीक्रेट का। सीक्रेट दोबारा बनाने या Zapier कनेक्ट करने से वह बदल जाता है। |
| अंतिम गतिविधि: में कभी नहीं लिखा है | नंबर अभी जांचा नहीं गया है। उसका कनेक्टेड होना और संदेश भेजने में व्यस्त न होना ज़रूरी है। |
