Skip to content
Whats91

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

CRM Lead Generation

Summary

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.

Prerequisites

  • A Whats91 CRM company UID such as crmco_abc
  • A global customer-level public API token for POST /api/v2/crm/leads
  • A lead payload containing lead.fields with Email, MobilePhone, or Phone

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.

Endpoint Overview

MethodPathPurpose
POST/api/v2/crm/leadsCreate a CRM lead with bearer-token authentication.
POST/api/v2/crm/companies/{companyUid}/leadsCreate a CRM lead through a company-UID URL without bearer-token authentication.
POST /crm/leads

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}/leads

Tokenless company-URL route

Use this tokenless company-URL route for controlled form 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 leads 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 and customer context. If companyUid is also present in the request body, the URL companyUid remains the source of truth.

Create Lead With Bearer Token

POST/api/v2/crm/leads
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.
lead.fieldsobjectRequiredLead field object. Include Email, MobilePhone, or Phone.
Bearer-token request body
{
  "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"
    }
  }
}
Create lead with bearer token
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 Response
{
  "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"
  }
}

Create Lead With Company UID URL

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.

POST/api/v2/crm/companies/{companyUid}/leads
ParameterTypeRequiredDescription
Content-TypeheaderRequiredUse application/json.
companyUidpathRequiredCRM company UID in the URL. This value is the source of truth.
lead.fieldsobjectRequiredLead field object. Include Email, MobilePhone, or Phone.
Tokenless request body
{
  "lead": {
    "fields": {
      "Email": "asha@example.com",
      "MobilePhone": "919999999999",
      "Description": "Lead captured from a Whats91 flow"
    }
  }
}
Create lead with company UID URL
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"
      }
    }
  }'
Tokenless success response
{
  "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.

Flow Builder Custom API Setup

SetupBearer-token routeTokenless company-URL route
Endpoint URLhttps://graph.whats91.com/api/v2/crm/leadshttps://graph.whats91.com/api/v2/crm/companies/crmco_abc/leads
Auth ModeBearer tokenNo auth / URL-based access
Bearer Token{{variables.crm_public_api_token}} or another saved flow variableLeave blank
PayloadMust include companyUid and lead.fieldsShould omit companyUid and include lead.fields

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

FieldCRM mappingNotes
LeadTitleleadTitleOptional. Defaults from full name when omitted.
FirstNamefirstNameOptional.
LastNamelastNameOptional.
Name / FullNamefullNameOptional. If omitted, first and last name are combined.
CompanyprospectCompanyNameOptional prospect company name.
TitlejobTitleOptional job title.
EmailemailOptional, lowercased by CRM validation.
MobilePhonemobileNumberOptional mobile number.
PhonealternatePhoneOptional phone number. Also used for customer matching when MobilePhone is absent.
WebsitewebsiteOptional website URL or text.
Street / AddressaddressOptional address line.
CitycityOptional.
StatestateOptional.
CountrycountryOptional.
PostalCode / PincodepincodeOptional.
DescriptionrequirementSummaryOptional notes or requirement summary.
LeadSourceUidleadSourceUidOptional CRM lead source UID. Omit to use CRM default.
StatusUidstatusUidOptional CRM lead status UID. Omit to use CRM default.
OwnerPrincipalKeyownerPrincipalKeyOptional CRM owner principal key, such as a permitted team member.
PrioritypriorityOptional: low, medium, high, or urgent.
LeadScoreleadScoreOptional integer.
EstimatedValueestimatedValueOptional numeric value.
ExternalReferenceTypeexternalReferenceTypeOptional external system type.
ExternalReferenceIdexternalReferenceIdOptional 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.

Automatic Customer Matching

  • CRM checks existing customer data for the same companyUid after validation and before saving the lead.
  • Email matching is exact after normalizing case and surrounding spaces.
  • Customer contacts and customer primary email are both checked.
  • Phone matching treats MobilePhone and Phone as lead contact numbers.
  • Phone values are normalized to digits before matching, so plus signs, spaces, hyphens, brackets, and dots do not affect matching.
  • Exact digit matches are checked first across customer mobile and WhatsApp fields.
  • For Indian customers, CRM also matches the last 10 digits, so +91 98765 43210 and 9876543210 resolve to the same customer.
  • Mobile and WhatsApp numbers are treated equally for matching.

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.

Error Responses

HTTPError codeCause
400VALIDATION_FAILEDMissing body companyUid on the bearer-token route, missing lead.fields, unsupported field, invalid priority, invalid capture method, or CRM lead 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.
404NOT_FOUNDThe CRM company or referenced CRM setup row was not found.
Validation Error Response
{
  "success": false,
  "message": "Missing required field: companyUid",
  "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/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"
    }
  }
}'
Node.js
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);
PHP
$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;
Python
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())
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/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());

Frequently Asked Questions

Which CRM lead endpoint should I use?

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.

Is companyUid required?

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.

How does CRM link leads to existing customers?

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.

Related APIs