Skip to content
Whats91

Create CRM complaints from public API integrations, customer forms, Flow Builder custom API nodes, and tokenless company-URL submissions.

CRM Complaint Creation

Summary

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.

Prerequisites

  • A Whats91 CRM company UID such as crmco_abc
  • A global customer-level public API token for POST /api/v2/crm/complaints
  • A complaint payload containing complaint.fields with ComplaintTitle and Description

Related documentation

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.

Endpoint Overview

MethodPathPurpose
POST/api/v2/crm/complaintsCreate a CRM complaint with bearer-token authentication.
POST/api/v2/crm/companies/{companyUid}/complaintsCreate a CRM complaint through a company-UID URL without bearer-token authentication.
POST /crm/complaints

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.

POST /companies/{companyUid}/complaints

Tokenless company-URL route

Use this tokenless company-URL route for controlled customer forms or Flow Builder submissions where the company UID is embedded in the endpoint URL.

Authentication

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.

Bearer token header
Authorization: Bearer w91_public_token_here

For 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.

Create Complaint With Bearer Token

POST/api/v2/crm/complaints
ParameterTypeRequiredDescription
AuthorizationheaderRequiredBearer w91_public_token_here. Must be a global customer-level public API token.
Content-TypeheaderRequiredUse application/json.
companyUidstringRequiredCRM company UID because the bearer-token URL is static.
complaint.fieldsobjectRequiredComplaint field object. Include ComplaintTitle and Description or supported aliases.
Bearer-token request body
{
  "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"
    }
  }
}
Create complaint with bearer token
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 Response
{
  "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"
  }
}

Create Complaint With Company UID URL

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.

POST/api/v2/crm/companies/{companyUid}/complaints
ParameterTypeRequiredDescription
Content-TypeheaderRequiredUse application/json.
companyUidpathRequiredCRM company UID in the URL. This value is the source of truth.
complaint.fieldsobjectRequiredComplaint field object. Include title and description fields.
Tokenless request body
{
  "complaint": {
    "fields": {
      "Subject": "Billing issue",
      "Message": "Invoice total does not match the order.",
      "Phone": "+91 99999 99999"
    }
  }
}
Create complaint with company UID URL
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"
      }
    }
  }'
Tokenless success response
{
  "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.

Request Body

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.

Supported Fields

FieldCRM mapping
ComplaintTitle / Subject / TitlecomplaintTitle
Description / Message / Issuedescription
CustomerName / NamesubmittedCustomerName
Company / CompanyNamesubmittedCompanyName
EmailsubmittedEmail
MobilePhone / MobileNumber / PhonesubmittedMobileNumber
AddresssubmittedAddress
SourceUid / ComplaintSourceUidsourceUid
StatusUidstatusUid
PriorityUidpriorityUid
CategoryUidcategoryUid
QueueUidqueueUid
AssigneePrincipalKeyassigneePrincipalKey
ExternalReferenceTypeexternalReferenceType
ExternalReferenceIdexternalReferenceId
RelatedOrderReferencerelatedOrderReference
NextFollowUpAtnextFollowUpAt

Note

If ExternalReferenceId is provided without ExternalReferenceType, the type defaults to public_api. Public complaints are saved with intakeMethod: "api".

Customer Matching

  • CRM attempts to link the complaint to an existing account or contact in the same company when email or phone data is present.
  • Email matching is exact after trimming and lowercasing.
  • Phone values are normalized to digits before matching.
  • Mobile and WhatsApp numbers are treated equally.
  • For India, CRM also matches the last 10 digits so values with or without +91 resolve to the same customer.
  • If no customer is found, the complaint is still created normally.

Response Headers

  • X-Whats91-Request-Id: request id for support and log correlation.
  • X-Whats91-Crm-Complaint-Uid: public CRM complaint UID created by the request.

Note

Internal database ids, Whats91 user ids, tokens, encrypted values, and raw private payloads are never returned in public CRM complaint responses.

Error Responses

HTTPError codeCause
400VALIDATION_FAILEDMissing companyUid, missing complaint.fields, missing title/description, unsupported field, or CRM complaint validation failure.
401MISSING_AUTH_TOKENBearer-token route only: no bearer token or compatible token body field was provided.
401INVALID_AUTH_TOKENBearer-token route only: the public API token is invalid.
403TOKEN_SCOPE_NOT_ALLOWEDBearer-token route only: a number-scoped public API token was used.
403CUSTOMER_TOKEN_REQUIREDBearer-token route only: the token does not belong to a customer account.
403FORBIDDENCompany-URL route only: the CRM company is inactive or unavailable for public access.
404NOT_FOUNDThe CRM company or referenced CRM setup row was not found.
Validation Error Response
{
  "success": false,
  "message": "Complaint title and description are required",
  "error_code": "VALIDATION_FAILED",
  "details": {},
  "metadata": {
    "apiVersion": "v2",
    "requestId": "request-uuid"
  }
}

SDK Examples

Use these examples as starting points for server-side implementations.

cURL
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"
    }
  }
}'
Node.js
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);
PHP
$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;
Python
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())
C#
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());

Frequently Asked Questions

Which CRM complaint endpoint should I use?

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.

Which complaint fields are required?

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.

Can CRM match complaints to existing customers?

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.

Related APIs