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 leads from public API integrations, Flow Builder custom API nodes, and tokenless company-URL submissions.
Use CRM Lead Generation APIs to create company-scoped CRM leads through the canonical Whats91 public API v2 routes, with bearer-token authentication or a tokenless company-UID URL for controlled embedded workflows.
Create CRM leads in the dedicated Whats91 CRM database while keeping public API authentication in the main Whats91 backend. Lead creation is company-scoped and can automatically link new leads to existing CRM customers when email or phone values match.
Note
This phase documents Lead Generation only. Complaint Management APIs are intentionally not included here and will be documented in a later CRM phase.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v2/crm/leads | Create a CRM lead with bearer-token authentication. |
| POST | /api/v2/crm/companies/{companyUid}/leads | Create a CRM lead 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 form 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 leads 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 and customer context. If companyUid is also present in the request body, the URL companyUid remains the source of truth.
/api/v2/crm/leads| 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. |
lead.fields | object | Required | Lead field object. Include Email, MobilePhone, or Phone. |
{
"companyUid": "crmco_abc",
"lead": {
"fields": {
"FirstName": "Asha",
"LastName": "Patel",
"Company": "Acme Pvt Ltd",
"Title": "Operations Manager",
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"City": "Indore",
"State": "Madhya Pradesh",
"Country": "India",
"Description": "Needs a CRM follow-up",
"LeadSourceUid": "crmlsrc_web",
"StatusUid": "crmlstat_new",
"Priority": "high",
"ExternalReferenceId": "sf-lead-1"
}
}
}curl -X POST "https://graph.whats91.com/api/v2/crm/leads" \
-H "Authorization: Bearer w91_public_token_here" \
-H "Content-Type: application/json" \
-d '{
"companyUid": "crmco_abc",
"lead": {
"fields": {
"FirstName": "Asha",
"LastName": "Patel",
"Company": "Acme Pvt Ltd",
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"Description": "Needs a CRM follow-up",
"Priority": "high",
"ExternalReferenceId": "sf-lead-1"
}
}
}'{
"success": true,
"message": "CRM lead created",
"data": {
"lead": {
"leadUid": "crml_abc",
"uid": "crml_abc",
"crmCustomerUid": "crmcust_abc",
"crmCustomerContactUid": "crmcontact_abc",
"leadTitle": "Asha Patel",
"firstName": "Asha",
"lastName": "Patel",
"fullName": "Asha Patel",
"prospectCompanyName": "Acme Pvt Ltd",
"email": "asha@example.com",
"mobileNumber": "919999999999",
"priority": "high",
"captureMethod": "api",
"source": {
"sourceUid": "crmlsrc_web",
"uid": "crmlsrc_web",
"name": "Website"
},
"status": {
"statusUid": "crmlstat_new",
"uid": "crmlstat_new",
"name": "New"
},
"recordStatus": "active",
"createdAt": "2026-06-16T08:00:00.000Z",
"updatedAt": "2026-06-16T08:00:00.000Z"
}
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}Use the company-URL route for tokenless form submissions or Flow Builder Custom API nodes where the CRM company UID is already embedded in the endpoint URL.
/api/v2/crm/companies/{companyUid}/leads| 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. |
lead.fields | object | Required | Lead field object. Include Email, MobilePhone, or Phone. |
{
"lead": {
"fields": {
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"Description": "Lead captured from a Whats91 flow"
}
}
}curl -X POST "https://graph.whats91.com/api/v2/crm/companies/crmco_abc/leads" \
-H "Content-Type: application/json" \
-d '{
"lead": {
"fields": {
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"Description": "Lead captured from a Whats91 flow"
}
}
}'{
"success": true,
"message": "CRM lead created",
"data": {
"lead": {
"leadUid": "crml_abc",
"uid": "crml_abc",
"email": "asha@example.com",
"mobileNumber": "919999999999",
"captureMethod": "api",
"recordStatus": "active"
}
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}Warning
The company-URL route is tokenless by design. Only expose URLs for companies and workflows where URL-based lead capture is acceptable.
| Setup | Bearer-token route | Tokenless company-URL route |
|---|---|---|
| Endpoint URL | https://graph.whats91.com/api/v2/crm/leads | https://graph.whats91.com/api/v2/crm/companies/crmco_abc/leads |
| Auth Mode | Bearer token | No auth / URL-based access |
| Bearer Token | {{variables.crm_public_api_token}} or another saved flow variable | Leave blank |
| Payload | Must include companyUid and lead.fields | Should omit companyUid and include lead.fields |
The request body must contain a lead.fields object. Unsupported fields are rejected with VALIDATION_FAILED so lead data is not silently dropped. At least one contact value is required: Email, MobilePhone, or Phone. Name and company fields are optional and are never required for public lead creation.
| Field | CRM mapping | Notes |
|---|---|---|
| LeadTitle | leadTitle | Optional. Defaults from full name when omitted. |
| FirstName | firstName | Optional. |
| LastName | lastName | Optional. |
| Name / FullName | fullName | Optional. If omitted, first and last name are combined. |
| Company | prospectCompanyName | Optional prospect company name. |
| Title | jobTitle | Optional job title. |
| Optional, lowercased by CRM validation. | ||
| MobilePhone | mobileNumber | Optional mobile number. |
| Phone | alternatePhone | Optional phone number. Also used for customer matching when MobilePhone is absent. |
| Website | website | Optional website URL or text. |
| Street / Address | address | Optional address line. |
| City | city | Optional. |
| State | state | Optional. |
| Country | country | Optional. |
| PostalCode / Pincode | pincode | Optional. |
| Description | requirementSummary | Optional notes or requirement summary. |
| LeadSourceUid | leadSourceUid | Optional CRM lead source UID. Omit to use CRM default. |
| StatusUid | statusUid | Optional CRM lead status UID. Omit to use CRM default. |
| OwnerPrincipalKey | ownerPrincipalKey | Optional CRM owner principal key, such as a permitted team member. |
| Priority | priority | Optional: low, medium, high, or urgent. |
| LeadScore | leadScore | Optional integer. |
| EstimatedValue | estimatedValue | Optional numeric value. |
| ExternalReferenceType | externalReferenceType | Optional external system type. |
| ExternalReferenceId | externalReferenceId | Optional external id. If provided without a type, the type defaults to public_api. |
Note
The current CRM lead table does not store Salesforce CurrencyCode or ExpectedClosureDate, so those fields are rejected until the CRM lead schema is extended.
If a match is found, the lead stores the linked CRM customer and, when the match came from a contact row, the linked CRM customer contact. If no match is found, the lead is still created normally without a customer link.
Note
Internal database ids, Whats91 user ids, and private token details are never returned in public CRM lead responses.
| HTTP | Error code | Cause |
|---|---|---|
| 400 | VALIDATION_FAILED | Missing body companyUid on the bearer-token route, missing lead.fields, unsupported field, invalid priority, invalid capture method, or CRM lead 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. |
| 404 | NOT_FOUND | The CRM company or referenced CRM setup row was not found. |
{
"success": false,
"message": "Missing required field: companyUid",
"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/leads" \
-H "Authorization: Bearer w91_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"companyUid": "crmco_abc",
"lead": {
"fields": {
"FirstName": "Asha",
"LastName": "Patel",
"Company": "Acme Pvt Ltd",
"Title": "Operations Manager",
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"City": "Indore",
"State": "Madhya Pradesh",
"Country": "India",
"Description": "Needs a CRM follow-up",
"LeadSourceUid": "crmlsrc_web",
"StatusUid": "crmlstat_new",
"Priority": "high",
"ExternalReferenceId": "sf-lead-1"
}
}
}'const response = await fetch("https://graph.whats91.com/api/v2/crm/leads", {
method: "POST",
headers: {
"Authorization": "Bearer w91_live_xxx",
"Content-Type": "application/json"
},
body: JSON.stringify({
"companyUid": "crmco_abc",
"lead": {
"fields": {
"FirstName": "Asha",
"LastName": "Patel",
"Company": "Acme Pvt Ltd",
"Title": "Operations Manager",
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"City": "Indore",
"State": "Madhya Pradesh",
"Country": "India",
"Description": "Needs a CRM follow-up",
"LeadSourceUid": "crmlsrc_web",
"StatusUid": "crmlstat_new",
"Priority": "high",
"ExternalReferenceId": "sf-lead-1"
}
}
})
});
const data = await response.json();
console.log(data);$ch = curl_init("https://graph.whats91.com/api/v2/crm/leads");
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",
"lead" => [
"fields" => [
"FirstName" => "Asha",
"LastName" => "Patel",
"Company" => "Acme Pvt Ltd",
"Title" => "Operations Manager",
"Email" => "asha@example.com",
"MobilePhone" => "919999999999",
"City" => "Indore",
"State" => "Madhya Pradesh",
"Country" => "India",
"Description" => "Needs a CRM follow-up",
"LeadSourceUid" => "crmlsrc_web",
"StatusUid" => "crmlstat_new",
"Priority" => "high",
"ExternalReferenceId" => "sf-lead-1"
]
]
])
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;import requests
response = requests.request(
"POST",
"https://graph.whats91.com/api/v2/crm/leads",
headers={
"Authorization": "Bearer w91_live_xxx",
"Content-Type": "application/json",
},
json={
"companyUid": "crmco_abc",
"lead": {
"fields": {
"FirstName": "Asha",
"LastName": "Patel",
"Company": "Acme Pvt Ltd",
"Title": "Operations Manager",
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"City": "Indore",
"State": "Madhya Pradesh",
"Country": "India",
"Description": "Needs a CRM follow-up",
"LeadSourceUid": "crmlsrc_web",
"StatusUid": "crmlstat_new",
"Priority": "high",
"ExternalReferenceId": "sf-lead-1"
}
}
}
)
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/leads");
request.Content = new StringContent(
"""
{
"companyUid": "crmco_abc",
"lead": {
"fields": {
"FirstName": "Asha",
"LastName": "Patel",
"Company": "Acme Pvt Ltd",
"Title": "Operations Manager",
"Email": "asha@example.com",
"MobilePhone": "919999999999",
"City": "Indore",
"State": "Madhya Pradesh",
"Country": "India",
"Description": "Needs a CRM follow-up",
"LeadSourceUid": "crmlsrc_web",
"StatusUid": "crmlstat_new",
"Priority": "high",
"ExternalReferenceId": "sf-lead-1"
}
}
}
""",
Encoding.UTF8,
"application/json"
);
var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());Use POST /api/v2/crm/leads when your server can send a bearer token. Use POST /api/v2/crm/companies/{companyUid}/leads only when the integration cannot send an Authorization header and the CRM company UID is embedded in the URL.
companyUid is required in the JSON body for the bearer-token route. On the company-URL route, the companyUid path parameter is authoritative and the request body can omit companyUid.
CRM attempts email and phone matching inside the selected company before saving the lead. If a matching CRM customer or contact is found, the lead stores those CRM links; otherwise the lead is still created without a customer link.
Create CRM complaints from public API integrations, customer forms, Flow Builder custom API nodes, and tokenless company-URL submissions.
Create a new account-wide contact book.
Practical webhook receiver examples and use cases for CRM, delivery tracking, and template operations.
List conversation report records for the resolved WhatsApp sender.