curl --request POST \
--url http://localhost:3000/chatwoot/webhook \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "http://localhost:3000/chatwoot/webhook"
payload = {}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({})
};
fetch('http://localhost:3000/chatwoot/webhook', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "3000",
CURLOPT_URL => "http://localhost:3000/chatwoot/webhook",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://localhost:3000/chatwoot/webhook"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://localhost:3000/chatwoot/webhook")
.header("Authorization", "Basic <encoded-value>")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:3000/chatwoot/webhook")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body{
"code": 400,
"message": "field cannot be blank",
"results": null
}Chatwoot webhook endpoint
Receives webhook events from Chatwoot and sends messages to WhatsApp. Configure this URL in your Chatwoot inbox webhook settings.
When CHATWOOT_WEBHOOK_SECRET is set the request must prove knowledge of
it. Recommended: Chatwoot’s own signature, sent by Chatwoot when the API
channel has a webhook secret — X-Chatwoot-Timestamp (unix seconds) and
X-Chatwoot-Signature: sha256=<hex HMAC-SHA256 of "<timestamp>.<raw body>">.
The timestamp must be within CHATWOOT_WEBHOOK_SIGNATURE_TOLERANCE (5m by
default) of the server clock. Also accepted: X-Hub-Signature-256
(HMAC-SHA256 of the raw body), the secret in X-Chatwoot-Webhook-Secret
or X-Gowa-Chatwoot-Secret, or the secret query parameter (discouraged:
the secret then appears in URLs and logs).
The secret that applies is the secret of the device the payload is
routed to: that device config’s webhook_secret when it has one (the
global secret is then not accepted for it), CHATWOOT_WEBHOOK_SECRET
otherwise and when no device resolves. A Chatwoot-signed request that
was already accepted (answered 2xx) inside the tolerance window is
acknowledged with 200 and not acted on again. One that was answered
with any other status is not remembered, so sending the identical
request again is handled.
curl --request POST \
--url http://localhost:3000/chatwoot/webhook \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "http://localhost:3000/chatwoot/webhook"
payload = {}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({})
};
fetch('http://localhost:3000/chatwoot/webhook', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "3000",
CURLOPT_URL => "http://localhost:3000/chatwoot/webhook",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://localhost:3000/chatwoot/webhook"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://localhost:3000/chatwoot/webhook")
.header("Authorization", "Basic <encoded-value>")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:3000/chatwoot/webhook")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body{
"code": 400,
"message": "field cannot be blank",
"results": null
}Authorizations
Basic authentication header of the form Basic <encoded-value>, where <encoded-value> is the base64-encoded string username:password.
Headers
Chatwoot signature: sha256=<hex HMAC-SHA256 of "." keyed with the webhook secret>
Unix time (seconds) the webhook was signed at
Query Parameters
The webhook secret. Discouraged; prefer the signature headers.
Body
Chatwoot webhook payload (see Chatwoot documentation)
Response
Webhook processed
