Create or Update Lead
Create a new lead or update an existing one, identified by its primary phone number. This is an upsert by customer_phone — set create_customer_if_customer_phone_not_found: true to allow the call to create a lead when no match exists.
This endpoint is the main entry point for syncing leads from an external system (your website form, marketplace, or another CRM) into Superfone.
Requires the x-api-key header. See Overview for details.
HTTP Request
POST /enterprise/api/lead
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
customer_phone | string | Yes | Primary phone number used to look up (and, if missing, create) the lead. E.164 format. |
create_customer_if_customer_phone_not_found | boolean | No | If true, a new lead is created when customer_phone doesn't match any existing lead. Default: false (returns 400 Customer not found). |
first_name | string | No | Lead's first name. |
last_name | string | No | Lead's last name. |
email | string[] | No | One or more email addresses. |
website | string | No | Lead's website. |
address | object | No | Address object. See Address object. |
city | string | No | City name. |
business_name | string | No | Lead's business or company name. |
additional_info | string | No | Free-form notes / additional info on the lead. |
deal_value | number | No | Estimated deal value. |
source | string | No | Free-form source string (e.g. "website-form", "marketplace-x"). |
source_type | string | No | One of the predefined source types. See List source types. |
assignee_user_phone | string | null | No | Phone (E.164) of a Superfone team member to assign the lead to. The user must already be part of your account. Pass null to unassign. |
lead_stage_name | string | null | No | Name of an existing lead stage in your account. Pass null to clear. |
lead_group_name | string | null | No | Name of an existing lead group in your account. Pass null to clear. |
add_labels | string[] | No | Names of existing labels to attach. Cannot be combined with remove_labels in the same request. |
remove_labels | string[] | No | Names of existing labels to detach. Cannot be combined with add_labels in the same request. |
add_products | string[] | No | Names of existing products to attach to the lead. |
add_phones | string[] | No | Secondary phone numbers to attach to the lead, in addition to customer_phone. See add_phones behavior. |
customer_note | string | No | A note to add to the lead's timeline. |
Address object
| Field | Type | Description |
|---|---|---|
text | string | null | Free-form address text |
additional | string | null | Apartment / unit / additional line |
initials | string | null | Address initials (used for display) |
latitude | number | null | Latitude |
longitude | number | null | Longitude |
add_phones behavior
add_phones is how you attach additional / secondary phone numbers to a lead — for example, a customer's office line in addition to their mobile number. It never replaces customer_phone.
Format
Each entry should be in E.164 format (international, with + prefix). Examples: +918000000001, +14155552671. An invalid number anywhere in the array fails the whole request with 400 Invalid phone number..
Deduplication
- Duplicates within the
add_phonesarray are silently collapsed. - A phone that already exists on the same lead (matched via
customer_phone) is silently skipped — no error, no duplicate row. - A phone that already belongs to a different lead in your account fails the request with
400 Phone number {phone} in add_phones is already present in different customer .... To move a phone between leads, remove it from the other lead first (via the dashboard).
Existing vs. new leads
- When
customer_phonematches an existing lead, surviving entries inadd_phonesare appended to that lead's existing phone list. - When the lead is being created in this request (
create_customer_if_customer_phone_not_found: trueand no match), the new lead starts withcustomer_phone, thenadd_phonesentries are appended.
Example
{
"customer_phone": "+918000000001",
"add_phones": ["+918000000002", "+918000000003"]
}
After this request the lead has three phones: +918000000001 (primary, from customer_phone) plus +918000000002 and +918000000003 as secondary.
Notes on behavior
customer_phoneis treated as the lead's primary phone. If it doesn't match any existing lead andcreate_customer_if_customer_phone_not_foundistrue, a new lead is created with this phone as its only phone.- For
add_labels/remove_labels/add_products: unknown names are silently ignored. To discover valid names, call List labels and List products. - For
lead_stage_nameandlead_group_name: the name must match an existing item exactly. Unknown names return404. If multiple items share the name, the request returns400— keep your stage/group names unique.
Try it
Code Examples
- cURL
- JavaScript
- TypeScript
- Python
curl -X POST https://prod-api.superfone.co.in/superfone/enterprise/api/lead \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"customer_phone": "+918000000001",
"create_customer_if_customer_phone_not_found": true,
"first_name": "Asha",
"last_name": "Kumar",
"email": ["asha@example.com"],
"business_name": "Acme Pvt Ltd",
"city": "Bengaluru",
"deal_value": 50000,
"source": "website-form",
"source_type": "OTHERS",
"lead_stage_name": "New Lead",
"lead_group_name": "Inbound",
"add_labels": ["Hot", "Demo Requested"],
"add_products": ["Starter Plan"],
"assignee_user_phone": "+919876543210",
"add_phones": ["+918000000002"],
"customer_note": "Asked for a callback after 6 PM."
}'
const response = await fetch(
'https://prod-api.superfone.co.in/superfone/enterprise/api/lead',
{
method: 'POST',
headers: {
'x-api-key': process.env.SF_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
customer_phone: '+918000000001',
create_customer_if_customer_phone_not_found: true,
first_name: 'Asha',
last_name: 'Kumar',
email: ['asha@example.com'],
business_name: 'Acme Pvt Ltd',
city: 'Bengaluru',
deal_value: 50000,
source: 'website-form',
source_type: 'OTHERS',
lead_stage_name: 'New Lead',
lead_group_name: 'Inbound',
add_labels: ['Hot', 'Demo Requested'],
add_products: ['Starter Plan'],
assignee_user_phone: '+919876543210',
add_phones: ['+918000000002'],
customer_note: 'Asked for a callback after 6 PM.'
})
}
);
const result = await response.json();
console.log(result);
interface UpsertLeadRequest {
customer_phone: string;
create_customer_if_customer_phone_not_found?: boolean;
first_name?: string;
last_name?: string;
email?: string[];
website?: string;
address?: {
text?: string | null;
additional?: string | null;
initials?: string | null;
latitude?: number | null;
longitude?: number | null;
};
city?: string;
business_name?: string;
additional_info?: string;
deal_value?: number;
source?: string;
source_type?:
| 'CSV_UPLOAD'
| 'FACEBOOK_INTEGRATION'
| 'PHONE_CONTACT'
| 'WHATSAPP_MESSAGE'
| 'WHATSAPP_INTEGRATION'
| 'PABBLY'
| 'OTHERS';
assignee_user_phone?: string | null;
lead_stage_name?: string | null;
lead_group_name?: string | null;
add_labels?: string[];
remove_labels?: string[];
add_products?: string[];
add_phones?: string[];
customer_note?: string;
}
const payload: UpsertLeadRequest = {
customer_phone: '+918000000001',
create_customer_if_customer_phone_not_found: true,
first_name: 'Asha',
last_name: 'Kumar',
email: ['asha@example.com'],
source: 'website-form',
source_type: 'OTHERS',
lead_stage_name: 'New Lead',
add_labels: ['Hot']
};
const response = await fetch(
'https://prod-api.superfone.co.in/superfone/enterprise/api/lead',
{
method: 'POST',
headers: {
'x-api-key': process.env.SF_API_KEY!,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}
);
const result: { data: null; message: string } = await response.json();
console.log(result);
import os
import requests
url = 'https://prod-api.superfone.co.in/superfone/enterprise/api/lead'
headers = {
'x-api-key': os.environ['SF_API_KEY'],
'Content-Type': 'application/json'
}
payload = {
'customer_phone': '+918000000001',
'create_customer_if_customer_phone_not_found': True,
'first_name': 'Asha',
'last_name': 'Kumar',
'email': ['asha@example.com'],
'business_name': 'Acme Pvt Ltd',
'city': 'Bengaluru',
'deal_value': 50000,
'source': 'website-form',
'source_type': 'OTHERS',
'lead_stage_name': 'New Lead',
'lead_group_name': 'Inbound',
'add_labels': ['Hot', 'Demo Requested'],
'add_products': ['Starter Plan'],
'assignee_user_phone': '+919876543210',
'add_phones': ['+918000000002'],
'customer_note': 'Asked for a callback after 6 PM.'
}
response = requests.post(url, headers=headers, json=payload)
print(response.status_code, response.json())
Success Response
Status Code: 200 OK
{
"data": null,
"message": "success"
}
The endpoint returns an empty data field on success. If you need the full lead record after writing, follow up with Get Lead.
Error Responses
| Status | Message | When it occurs |
|---|---|---|
400 | Customer phone is required to identify the record to update | customer_phone missing from body |
400 | Invalid phone number. | customer_phone or any value in add_phones is not a valid E.164 number |
400 | Customer not found. | customer_phone doesn't match any lead and create_customer_if_customer_phone_not_found was not true |
400 | Customer phone exists in multiple contact | customer_phone appears as a primary phone on more than one lead in your account |
400 | Phone number {phone} in add_phones is already present in different customer ... | A phone in add_phones already belongs to another lead |
400 | add_labels and remove_labels cannot be used in the same request | Both fields were provided |
400 | Assignee user's phone is invalid | assignee_user_phone is not a valid E.164 number |
400 | Assignee user is not part of your team | The user with that phone exists but is not an active member of your account |
400 | Multiple lead stages found with this name. Provide unique | More than one lead stage matches lead_stage_name |
400 | Multiple lead groups found with this name. Provide unique | More than one lead group matches lead_group_name |
401 | UnAuthorized, Please Provide Valid API Key | Missing or invalid x-api-key |
404 | Assignee user not found | No user exists with the phone given in assignee_user_phone |
404 | Lead stage name not found | lead_stage_name doesn't match any stage in your account |
404 | Lead group name not found | lead_group_name doesn't match any group in your account |
Example error
{
"message": "Lead stage name not found"
}
Related Endpoints
- Get Lead — Read a lead back by phone after upserting
- List labels — Discover label names to use in
add_labels/remove_labels - List lead stages — Discover names for
lead_stage_name - List lead groups — Discover names for
lead_group_name - List products — Discover names for
add_products - List source types — Discover valid
source_typevalues