Bearer-token route
Use this server-side route when your integration can send Authorization: Bearer w91_public_token_here. The JSON body must include companyUid.
Create CRM complaints from public API integrations, customer forms, Flow Builder custom API nodes, and tokenless company-URL submissions.
Use CRM Complaint Creation APIs to create company-scoped complaint tickets through the canonical Whats91 public API v2 routes, with bearer-token authentication or a tokenless company-UID URL for controlled embedded intake workflows.
Create CRM complaint tickets in the dedicated Whats91 CRM database while keeping public API authentication in the main Whats91 backend. Complaint creation is company-scoped and can automatically link complaints to existing CRM customers when email or phone values match.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v2/crm/complaints | Create a CRM complaint with bearer-token authentication. |
| POST | /api/v2/crm/companies/{companyUid}/complaints | Create a CRM complaint through a company-UID URL without bearer-token authentication. |
Use this server-side route when your integration can send Authorization: Bearer w91_public_token_here. The JSON body must include companyUid.
Use this tokenless company-URL route for controlled customer forms or Flow Builder submissions where the company UID is embedded in the endpoint URL.
The bearer-token route requires a global public API token. Number-scoped public API tokens are rejected with TOKEN_SCOPE_NOT_ALLOWED because CRM complaints are company-scoped resources, not sender-scoped resources.
Authorization: Bearer w91_public_token_hereFor POST compatibility, authToken, auth_token, or token can also be sent in the JSON body. The bearer token takes precedence when both header and body tokens are present.
For the company-URL route, do not send an Authorization header. The companyUid in the URL selects the target CRM company. If companyUid is also present in the request body, the URL companyUid remains the source of truth.
/api/v2/crm/complaints| Parameter | Type | Required | Description |
|---|---|---|---|
Authorization | header | Required | Bearer w91_public_token_here. Must be a global customer-level public API token. |
Content-Type | header | Required | Use application/json. |
companyUid | string | Required | CRM company UID because the bearer-token URL is static. |
complaint.fields | object | Required | Complaint field object. Include ComplaintTitle and Description or supported aliases. |
{
"companyUid": "crmco_abc",
"complaint": {
"fields": {
"ComplaintTitle": "WhatsApp API is not working",
"Description": "Customer reports API failures since morning.",
"CustomerName": "Dev Test",
"Email": "person@example.com",
"MobilePhone": "919999999999",
"CategoryUid": "crmccat_general",
"PriorityUid": "crmcp_low",
"ExternalReferenceId": "flow-ticket-1001"
}
}
}curl -X POST "https://graph.whats91.com/api/v2/crm/complaints" \
-H "Authorization: Bearer w91_public_token_here" \
-H "Content-Type: application/json" \
-d '{
"companyUid": "crmco_abc",
"complaint": {
"fields": {
"ComplaintTitle": "WhatsApp API is not working",
"Description": "Customer reports API failures since morning.",
"CustomerName": "Dev Test",
"Email": "person@example.com",
"MobilePhone": "919999999999",
"PriorityUid": "crmcp_low",
"ExternalReferenceId": "flow-ticket-1001"
}
}
}'{
"success": true,
"message": "CRM complaint created",
"data": {
"complaint": {
"complaintUid": "crmc_abc",
"uid": "crmc_abc",
"complaintNumber": "CMP-00001",
"complaintTitle": "WhatsApp API is not working",
"description": "Customer reports API failures since morning.",
"submittedEmail": "person@example.com",
"submittedMobileNumber": "919999999999",
"intakeMethod": "api",
"source": {
"sourceUid": "crmcs_api",
"uid": "crmcs_api",
"name": "API"
},
"status": {
"statusUid": "crmcst_open",
"uid": "crmcst_open",
"name": "Open"
},
"recordStatus": "active",
"createdAt": "2026-06-16T08:30:00.000Z",
"updatedAt": "2026-06-16T08:30:00.000Z"
}
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}Use the company-URL route for tokenless submissions when the integration cannot pass an Authorization header. The request body can omit companyUid because the URL value is authoritative.
/api/v2/crm/companies/{companyUid}/complaints| Parameter | Type | Required | Description |
|---|---|---|---|
Content-Type | header | Required | Use application/json. |
companyUid | path | Required | CRM company UID in the URL. This value is the source of truth. |
complaint.fields | object | Required | Complaint field object. Include title and description fields. |
{
"complaint": {
"fields": {
"Subject": "Billing issue",
"Message": "Invoice total does not match the order.",
"Phone": "+91 99999 99999"
}
}
}curl -X POST "https://graph.whats91.com/api/v2/crm/companies/crmco_abc/complaints" \
-H "Content-Type: application/json" \
-d '{
"complaint": {
"fields": {
"Subject": "Billing issue",
"Message": "Invoice total does not match the order.",
"Phone": "+91 99999 99999"
}
}
}'{
"success": true,
"message": "CRM complaint created",
"data": {
"complaint": {
"complaintUid": "crmc_abc",
"uid": "crmc_abc",
"complaintNumber": "CMP-00001",
"complaintTitle": "Billing issue",
"description": "Invoice total does not match the order.",
"submittedMobileNumber": "9999999999",
"intakeMethod": "api",
"recordStatus": "active"
}
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}Warning
The company-URL complaint route is tokenless by design. Use it only for controlled complaint intake workflows where URL-based access is acceptable.
The request body must contain a complaint.fields object. ComplaintTitle and Description are required, though Subject or Title can be used for the title and Message or Issue can be used for the description. Unsupported fields are rejected with VALIDATION_FAILED.
| Field | CRM mapping |
|---|---|
| ComplaintTitle / Subject / Title | complaintTitle |
| Description / Message / Issue | description |
| CustomerName / Name | submittedCustomerName |
| Company / CompanyName | submittedCompanyName |
| submittedEmail | |
| MobilePhone / MobileNumber / Phone | submittedMobileNumber |
| Address | submittedAddress |
| SourceUid / ComplaintSourceUid | sourceUid |
| StatusUid | statusUid |
| PriorityUid | priorityUid |
| CategoryUid | categoryUid |
| QueueUid | queueUid |
| AssigneePrincipalKey | assigneePrincipalKey |
| ExternalReferenceType | externalReferenceType |
| ExternalReferenceId | externalReferenceId |
| RelatedOrderReference | relatedOrderReference |
| NextFollowUpAt | nextFollowUpAt |
Note
If ExternalReferenceId is provided without ExternalReferenceType, the type defaults to public_api. Public complaints are saved with intakeMethod: "api".
Note
Internal database ids, Whats91 user ids, tokens, encrypted values, and raw private payloads are never returned in public CRM complaint responses.
| HTTP | Error code | Cause |
|---|---|---|
| 400 | VALIDATION_FAILED | Missing companyUid, missing complaint.fields, missing title/description, unsupported field, or CRM complaint validation failure. |
| 401 | MISSING_AUTH_TOKEN | Bearer-token route only: no bearer token or compatible token body field was provided. |
| 401 | INVALID_AUTH_TOKEN | Bearer-token route only: the public API token is invalid. |
| 403 | TOKEN_SCOPE_NOT_ALLOWED | Bearer-token route only: a number-scoped public API token was used. |
| 403 | CUSTOMER_TOKEN_REQUIRED | Bearer-token route only: the token does not belong to a customer account. |
| 403 | FORBIDDEN | Company-URL route only: the CRM company is inactive or unavailable for public access. |
| 404 | NOT_FOUND | The CRM company or referenced CRM setup row was not found. |
{
"success": false,
"message": "Complaint title and description are required",
"error_code": "VALIDATION_FAILED",
"details": {},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}Use these examples as starting points for server-side implementations.
curl -X POST "https://graph.whats91.com/api/v2/crm/complaints" \
-H "Authorization: Bearer w91_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"companyUid": "crmco_abc",
"complaint": {
"fields": {
"ComplaintTitle": "WhatsApp API is not working",
"Description": "Customer reports API failures since morning.",
"CustomerName": "Dev Test",
"Email": "person@example.com",
"MobilePhone": "919999999999",
"CategoryUid": "crmccat_general",
"PriorityUid": "crmcp_low",
"ExternalReferenceId": "flow-ticket-1001"
}
}
}'const response = await fetch("https://graph.whats91.com/api/v2/crm/complaints", {
method: "POST",
headers: {
"Authorization": "Bearer w91_live_xxx",
"Content-Type": "application/json"
},
body: JSON.stringify({
"companyUid": "crmco_abc",
"complaint": {
"fields": {
"ComplaintTitle": "WhatsApp API is not working",
"Description": "Customer reports API failures since morning.",
"CustomerName": "Dev Test",
"Email": "person@example.com",
"MobilePhone": "919999999999",
"CategoryUid": "crmccat_general",
"PriorityUid": "crmcp_low",
"ExternalReferenceId": "flow-ticket-1001"
}
}
})
});
const data = await response.json();
console.log(data);$ch = curl_init("https://graph.whats91.com/api/v2/crm/complaints");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer w91_live_xxx",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode([
"companyUid" => "crmco_abc",
"complaint" => [
"fields" => [
"ComplaintTitle" => "WhatsApp API is not working",
"Description" => "Customer reports API failures since morning.",
"CustomerName" => "Dev Test",
"Email" => "person@example.com",
"MobilePhone" => "919999999999",
"CategoryUid" => "crmccat_general",
"PriorityUid" => "crmcp_low",
"ExternalReferenceId" => "flow-ticket-1001"
]
]
])
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;import requests
response = requests.request(
"POST",
"https://graph.whats91.com/api/v2/crm/complaints",
headers={
"Authorization": "Bearer w91_live_xxx",
"Content-Type": "application/json",
},
json={
"companyUid": "crmco_abc",
"complaint": {
"fields": {
"ComplaintTitle": "WhatsApp API is not working",
"Description": "Customer reports API failures since morning.",
"CustomerName": "Dev Test",
"Email": "person@example.com",
"MobilePhone": "919999999999",
"CategoryUid": "crmccat_general",
"PriorityUid": "crmcp_low",
"ExternalReferenceId": "flow-ticket-1001"
}
}
}
)
print(response.json())using System.Text;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer w91_live_xxx");
var request = new HttpRequestMessage(HttpMethod.Post, "https://graph.whats91.com/api/v2/crm/complaints");
request.Content = new StringContent(
"""
{
"companyUid": "crmco_abc",
"complaint": {
"fields": {
"ComplaintTitle": "WhatsApp API is not working",
"Description": "Customer reports API failures since morning.",
"CustomerName": "Dev Test",
"Email": "person@example.com",
"MobilePhone": "919999999999",
"CategoryUid": "crmccat_general",
"PriorityUid": "crmcp_low",
"ExternalReferenceId": "flow-ticket-1001"
}
}
}
""",
Encoding.UTF8,
"application/json"
);
var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());Use POST /api/v2/crm/complaints when your server can send a bearer token. Use POST /api/v2/crm/companies/{companyUid}/complaints only when the integration cannot send an Authorization header and the CRM company UID is embedded in the URL.
complaint.fields must include a complaint title and description. Supported title aliases include ComplaintTitle, Subject, and Title. Supported description aliases include Description, Message, and Issue.
Yes. When Email, MobilePhone, MobileNumber, or Phone is present, CRM normalizes contact values and attempts to link the complaint to an existing customer or contact in the selected company.
Create CRM leads from public API integrations, Flow Builder custom API nodes, and tokenless company-URL submissions.
Practical webhook receiver examples and use cases for CRM, delivery tracking, and template operations.
List conversation report records for the resolved WhatsApp sender.
List message reports with pagination, date filters, status filters, and sender scoping.