Lead Intake API
Create leads in your Pay2All CRM from any external website, landing page, or application with a single authenticated request.
A lead created through the API is identical to one captured by a native web form or the chat widget: it enters your pipeline in New status, shows up in the Leads list, can be converted into a Contact, Account and Deal, and fires the same new-lead alert to your agents.
Base URL
All API requests are made to the base URL below and must be sent over HTTPS. Calls made over plain HTTP will fail.
Quick example
curl https://crm.2all.co.in/api/v1/leads \
-H "Authorization: Bearer crm_••••" \
-H "Content-Type: application/json" \
-d '{"name":"Rahul Sharma","phone":"9140929113"}'{
"ok": true,
"lead": {
"id": "clx9a1b2c3d4e5",
"name": "Rahul Sharma",
"status": "NEW"
}
}Authentication
Every request is authenticated with an API key. Send the key in either of these request headers:
AuthorizationheaderSend as Bearer <api_key>. Recommended.
X-API-KeyheaderSend the raw key as the header value. Use whichever your stack makes easier.
write:leads key can create leads in your CRM. Call the API only from your server — never expose the key in browser JavaScript or a mobile app. For public sites, proxy through your backend (see Web form integration).Authenticated request
Authorization: Bearer crm_your_secret_api_keyX-API-Key: crm_your_secret_api_keyGenerating an API key
- Sign in to your CRM and open Developer → API keys.
- Click Create key, name it (e.g. “Website leads”), and select the
write:leadsscope. Addread:leadsif you also want to fetch leads back. - Copy the key immediately — it begins with
crm_and is shown only once. Store it in your server’s environment variables. - Optional: restrict the key to your server’s IP address on the same screen for extra safety.
Scopes
# create leads
write:leads
# read leads back (optional)
read:leadsCreate a lead
Creates a new lead in your organization’s pipeline. Send a JSON body with at least a name; every other field is optional. Requires the write:leads scope.
Request arguments
namestringrequiredLead’s full name. 1–160 characters.
emailstringoptionalA valid email address, if available.
phonestringoptionalContact number. Up to 40 characters.
companystringoptionalCompany name. Carried to the Account when the lead is converted.
sourcestringoptionalWhere the lead came from, e.g. "Website". Defaults to "API". Up to 80 characters.
valueintegeroptionalEstimated deal value in ₹. Whole number ≥ 0. Defaults to 0.
notesstringoptionalAdded as the first note on the lead’s timeline. Up to 2000 characters.
Request
curl -X POST https://crm.2all.co.in/api/v1/leads \
-H "Authorization: Bearer crm_your_secret_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Rahul Sharma",
"email": "rahul@example.com",
"phone": "9140929113",
"company": "Sharma Travels",
"source": "Landing page",
"value": 25000
}'const res = await fetch("https://crm.2all.co.in/api/v1/leads", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.CRM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Rahul Sharma", phone: "9140929113" }),
});
const { lead } = await res.json();$ch = curl_init("https://crm.2all.co.in/api/v1/leads");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("CRM_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(["name" => "Rahul Sharma"]),
]);
$lead = json_decode(curl_exec($ch), true);import os, requests
r = requests.post(
"https://crm.2all.co.in/api/v1/leads",
headers={"Authorization": f"Bearer {os.environ['CRM_API_KEY']}"},
json={"name": "Rahul Sharma", "phone": "9140929113"},
)
lead = r.json()["lead"]{
"name": "Rahul Sharma",
"email": "rahul@example.com",
"phone": "9140929113",
"company": "Sharma Travels",
"source": "Landing page",
"value": 25000,
"notes": "Wants IRCTC API access"
}Response
{
"ok": true,
"lead": {
"id": "clx9a1b2c3d4e5f6",
"name": "Rahul Sharma",
"email": "rahul@example.com",
"phone": "9140929113",
"companyName": "Sharma Travels",
"source": "Landing page",
"status": "NEW",
"value": 25000,
"createdAt": "2026-09-09T10:24:01.500Z"
}
}List leads
Returns your organization’s leads, newest first, using cursor pagination. Requires the read:leads scope.
Query parameters
limitintegeroptionalNumber of leads per page, 1–100. Defaults to 25.
cursorstringoptionalPass the nextCursor from the previous response for the next page. Omit on the first request.
Request
curl "https://crm.2all.co.in/api/v1/leads?limit=25" \
-H "Authorization: Bearer crm_your_secret_api_key"Response
{
"data": [
{ "id": "clx9a1...", "name": "Rahul Sharma", "status": "NEW" }
],
"nextCursor": "clx9a1b2c3d4e5f6"
}Status & errors
The API uses standard HTTP status codes. Errors return a JSON body of the shape { "statusCode": n, "message": "..." }.
OK — the lead was created or listed successfully.
Bad request — validation failed: a missing name, invalid email, or a field over its length limit. The message names the field.
Unauthorized — the API key is missing, malformed, revoked, or wrong.
Forbidden — the key lacks the required scope (write:leads / read:leads), or its account isn’t linked to an organization.
Too many requests — slow down and retry after a short delay.
Error example
{
"statusCode": 400,
"message": "name must be longer than or equal to 1 characters",
"error": "Bad Request"
}{
"statusCode": 403,
"message": "API key is missing required scope(s): write:leads"
}Web form integration
For a public website the safe pattern is: the browser posts to your backend, and your backend attaches the secret key and forwards the request to the CRM. The key never reaches the browser.
The example on the right is a complete drop-in: an HTML form plus a small Node/Express proxy. Set a meaningful source so you can see where each lead came from.
/v1/leads directly from client-side JavaScript with a real key — anyone could read it from the page and create spam leads.<form id="lead">
<input name="name" placeholder="Name" required>
<input name="phone" placeholder="Phone">
<button>Request callback</button>
</form>
<script>
lead.onsubmit = async (e) => {
e.preventDefault();
await fetch("/api/lead", { // your backend
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(new FormData(lead))),
});
lead.innerHTML = "Thanks! We'll be in touch.";
};
</script>// Key lives on the server; the browser never sees it.
app.post("/api/lead", express.json(), async (req, res) => {
const r = await fetch("https://crm.2all.co.in/api/v1/leads", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.CRM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ...req.body, source: "Website form" }),
});
res.status(r.ok ? 200 : 502).json(await r.json());
});Go-live checklist
- Key created with the
write:leadsscope, stored in a server environment variable. - The key is used only from your backend — never shipped to the browser.
- A meaningful
sourceis set so leads are traceable in the CRM. - Non-200 responses are handled and the visitor sees a friendly message on failure.
- Tested end to end — the lead appeared in Leads → New.
You're done
# A POST returns:
{ "ok": true, "lead": { "status": "NEW" } }
# …and the lead shows in Leads → New,
# with your agents notified.https://crm.2all.co.in/api · Manage keys at
crm.2all.co.in/developer