Receive Lead via Webhook
Push a lead into Superfone by POSTing a JSON payload to your integration's webhook URL. The endpoint matches by phone number — if a lead with that phone already exists in your account, it's updated; otherwise a new lead is created and round-robin assigned.
The webhook_uuid in the URL is the credential — there's no separate signature or API key. The request is processed synchronously: the response tells you the outcome (new vs. updated lead, assigned agent) directly. Get the webhook URL from the integration's settings in the Superfone dashboard.
CORS is allowed, so browser calls won't be blocked at the network layer — but the webhook URL should never be shipped to a browser, since knowing it is enough to push leads into your account. Make all requests from your backend. See Overview for the full security model.
HTTP Request
POST /webhook/integration/:webhook_uuid
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
webhook_uuid | string | Yes | The unique identifier issued when you created the integration in the dashboard. |
Required Headers
| Header | Description |
|---|---|
content-type | Must be application/json. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
customer_phone | string | Yes | Lead's phone number (E.164 preferred, e.g. +918000000001). Used to match an existing lead or create a new one. |
first_name | string | No | Lead's first name. Existing leads keep their stored first/last name if the incoming first_name looks like a phone number. |
last_name | string | No | Lead's last name. |
email | string | string[] | No | One or more email addresses. A single string is accepted and converted to a one-element array. |
address | string | object | No | Address. A plain string is stored as { "text": "..." }; an object is stored as-is. See Address object. |
website | string | No | Lead's website. |
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. "facebook-leadgen", "landing-page"). Falls back to the integration's configured source. |
source_type | string | No | One of the predefined source types. See List source types. Falls back to the integration's configured source_type. |
assignee_user_id | number | No | ID of a Superfone team member to assign the lead to. If omitted, the lead is round-robin assigned across the integration's configured assignee_user_ids. |
lead_stage_id | number | No | ID of a lead stage. Falls back to the integration's configured stage. |
lead_group_id | number | No | ID of a lead group. Falls back to the integration's configured group. |
label_ids | number[] | No | IDs of labels to attach. Falls back to the integration's configured labels. |
| (custom field label name) | string | number | No | Any custom field configured on your account — see Custom fields. |
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 |
If you pass a plain string for address, it's stored as { "text": "<your string>" }.
Field-fallback behavior
For lead_stage_id, lead_group_id, label_ids, source, and source_type: if the field is omitted from the payload, the value configured on the integration setting is used. For assignee_user_id: when omitted, the lead is round-robin assigned across the integration's configured assignee_user_ids pool. This lets you keep payloads minimal — only include a field when you want to override the integration's default.
Custom fields
Send a custom field by its configured label/title from your account's custom field settings as a top-level key (e.g. "Course": "MBA"). It's resolved to the right field automatically based on that label's type.
{
"customer_phone": "+918000000001",
"Course": "MBA",
"Budget": 50000
}
Type-specific behavior when resolving a label:
| Label type | Behavior |
|---|---|
| Dropdown | Value is matched against the label's configured allowed values (case/space/underscore-insensitive). No match → the field is left unset. |
| Date / Date-time | Parsed in the org's timezone. An unparseable value → the field is left unset. |
| Numeric | Parsed as an integer. A non-numeric value → the field is left unset. |
| Text (and others) | Used as-is. |
A text-type custom field value that's an object or array is dropped rather than saved.
Try it
Paste your integration's webhook UUID below and click Send (or Copy as cURL to run it from your terminal).
Code Examples
- Bash (cURL)
- JavaScript (Node)
- TypeScript
- Python
#!/usr/bin/env bash
set -euo pipefail
WEBHOOK_UUID="8f4e2a31-2b6d-4c3a-9f1e-7a0b6c2d4e5f"
URL="https://prod-api.superfone.co.in/superfone/webhook/integration/${WEBHOOK_UUID}"
curl -X POST "$URL" \
-H "content-type: application/json" \
--data '{"customer_phone":"+918000000001","first_name":"Asha","last_name":"Kumar","email":"asha@example.com","business_name":"Acme Pvt Ltd","city":"Bengaluru","address":"12 MG Road, Bengaluru","deal_value":50000,"source":"landing-page","additional_info":"Asked for a callback after 6 PM."}'
const WEBHOOK_UUID = process.env.SF_WEBHOOK_UUID;
const URL =
"https://prod-api.superfone.co.in/superfone/webhook/integration/" +
WEBHOOK_UUID;
async function pushLead(payload) {
const res = await fetch(URL, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(payload),
});
return res.json();
}
pushLead({
customer_phone: "+918000000001",
first_name: "Asha",
last_name: "Kumar",
email: "asha@example.com",
business_name: "Acme Pvt Ltd",
city: "Bengaluru",
address: "12 MG Road, Bengaluru",
deal_value: 50000,
source: "landing-page",
additional_info: "Asked for a callback after 6 PM.",
}).then(console.log);
interface WebhookLeadPayload {
customer_phone: string;
first_name?: string;
last_name?: string;
email?: string | string[];
address?:
| string
| {
text?: string | null;
additional?: string | null;
initials?: string | null;
latitude?: number | null;
longitude?: number | null;
};
website?: string;
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_id?: number;
lead_stage_id?: number;
lead_group_id?: number;
label_ids?: number[];
}
interface WebhookResponse {
data: {
is_new_customer: boolean;
customers: unknown[];
agent_name: string;
agent_phone?: string;
agent_superfone?: string;
group_request_id: string;
customer_id?: number;
task_id?: number;
};
message: string;
}
async function pushLead(
payload: WebhookLeadPayload
): Promise<WebhookResponse> {
const url =
"https://prod-api.superfone.co.in/superfone/webhook/integration/" +
process.env.SF_WEBHOOK_UUID!;
const res = await fetch(url, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(payload),
});
if (!res.ok) {
throw new Error(`Webhook failed: ${res.status} ${await res.text()}`);
}
return (await res.json()) as WebhookResponse;
}
import json
import os
import requests
WEBHOOK_UUID = os.environ["SF_WEBHOOK_UUID"]
URL = (
"https://prod-api.superfone.co.in/superfone/webhook/integration/"
+ WEBHOOK_UUID
)
payload = {
"customer_phone": "+918000000001",
"first_name": "Asha",
"last_name": "Kumar",
"email": "asha@example.com",
"business_name": "Acme Pvt Ltd",
"city": "Bengaluru",
"address": "12 MG Road, Bengaluru",
"deal_value": 50000,
"source": "landing-page",
"additional_info": "Asked for a callback after 6 PM.",
}
response = requests.post(
URL,
json=payload,
headers={"content-type": "application/json"},
)
print(response.status_code, response.json())
Success Response
Status Code: 200 OK
{
"data": {
"is_new_customer": true,
"customers": [{ "id": 123456, "first_name": "Asha", "last_name": "Kumar", "..." : "..." }],
"agent_name": "Ravi Shankar",
"agent_phone": "+919000000002",
"agent_superfone": "+918040000000",
"group_request_id": "a1b2c3d4e5f6",
"customer_id": 123456,
"task_id": 789012
},
"message": "success"
}
Response Fields
| Field | Type | Description |
|---|---|---|
data.is_new_customer | boolean | true if this call created a new lead, false if it updated an existing one. |
data.customers | array | The affected lead record(s) — the newly created lead, or the matched existing lead(s). |
data.agent_name | string | Name of the team member the lead was assigned to (empty if unassigned). |
data.agent_phone | string | Assigned agent's phone number. |
data.agent_superfone | string | The Superfone number associated with the account. |
data.group_request_id | string | Server-side trace ID for this request. Quote it in any support request about a specific delivery. |
data.customer_id | number | ID of the lead that was created or updated by this api call. |
data.task_id | number | ID of the task created for this lead, if a task was configured on the integration. Omitted if no task was created. |
message | string | "success" on a 200. |
Error Responses
Errors are returned synchronously — there's no separate log to check after the fact.
| Status | When it occurs |
|---|---|
400 | Request body is empty, or no valid phone number was found in customer_phone. |
404 | webhook_uuid doesn't match a configured integration, no active subscription/CRM addon on the account, or the account's lead storage is full. |
500 | Unexpected server error. Safe to retry. |
Example error
{
"message": "No valid phone numbers found in payload"
}
Retries
There's no idempotency key on this endpoint. If a request times out or you get a 5xx, a retry is safe to send — the endpoint always re-resolves the lead by phone, so retrying an update just re-applies the same values. Avoid firing the same payload multiple times from a source that doesn't dedupe on its own, since a matched follow-up task can be re-created on every call.
Related Endpoints
- Integrations overview — Auth model, synchronous processing, retry guidance
- Customer Management overview — Server-to-server CRM API for richer reads/writes
- Create or Update Lead — Upsert by phone with name-based catalog references (no IDs needed)
- List lead stages, lead groups, labels — Discover IDs to use in
lead_stage_id,lead_group_id,label_ids