Public API
Last updated: 8 September 2026
Push leads into ScaleNova CRM from any external tool — Pabbly Connect, AiSensy, JustDial (where a custom webhook is available on your plan), a Google Sheets Apps Script, or your own code.
Getting a key
Go to Settings → API Keys in the dashboard (org ADMIN role required) and click Generate. The full key is shown exactly once — copy it immediately, it can't be retrieved again. If you lose it, revoke it and generate a new one.
Ingest / update a lead
POST https://crm.scalenova.co.in/api/public/v1/leads/{your-api-key}
Content-Type: application/jsonThe key is embedded directly in the URL path, not sent as a header — most no-code webhook senders can only POST to a URL and can't set custom headers. Treat the full URL, including the key, as a secret.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | Yes | Lead's name. |
| phone | string | No | Used to match an existing lead. |
| string | No | Must be a valid email if provided. | |
| companyName | string | No | |
| source | string | Yes | Where the lead came from, e.g. "justdial", "pabbly", "website". |
| actionName | string | No | Activity title logged when the same phone number hits again. |
| notes | string | No | |
| customFields | object | No | Keyed by each field's API name (Settings → Lead Fields). Merge semantics on update. |
| customActions | array | No | Logs Custom Actions in the same request: [{ "type": "ACTION_1001", "fields": {...} }]. Max 10. |
Leads created this way always start in the FRESH stage, unassigned. If a lead with the same phone already exists in your organization, the endpoint updates it instead of creating a duplicate and logs a new activity entry.
Example
curl -X POST \
https://crm.scalenova.co.in/api/public/v1/leads/sk_live_xxxxxxxxxxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{
"name": "Rahul Sharma",
"phone": "9876543210",
"email": "rahul.sharma@example.com",
"source": "justdial",
"actionName": "Lead Created",
"notes": "Asked about weekend batch",
"customFields": {
"budget_range": "50k-1L"
}
}'Custom Actions
Log a structured, org-defined activity (Settings → Custom Actions) against the lead in the same request. Each entry in customActions needs a type and a fields object:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | string | Yes | The action's code, prefixed with "ACTION_" — e.g. "ACTION_1001". Find the bare code ("Code 1001") next to the action's name in Settings → Custom Actions. |
| fields | object | No | Keyed by each field's key (shown when you open the action in Settings → Custom Actions). Unknown keys, wrong types, or a missing mandatory field are rejected with a 400. |
Each field's expected value depends on its type:
| Field | Type | Required | Notes |
|---|---|---|---|
| TEXT | string | Rejected if shorter/longer than the field's configured min/max length. | |
| NUMBER | number | Send a JSON number, not a quoted string — e.g. 42, not "42". | |
| DATE | string | Any value JavaScript's Date.parse() accepts, e.g. "2026-07-24". | |
| DROPDOWN | string | Must exactly match one of the field's configured options. |
A field can be omitted or sent as null — that's only rejected if the field is marked mandatory on the definition.
curl -X POST \
https://crm.scalenova.co.in/api/public/v1/leads/sk_live_xxxxxxxxxxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{
"name": "Rahul Sharma",
"phone": "9876543210",
"source": "website",
"customActions": [
{
"type": "ACTION_1001",
"fields": { "course_name": "SUPER_30", "batch": "Weekend" }
}
]
}'A bad field value comes back as a 400, with the offending key(s) named in fieldErrors:
{
"error": "One or more custom field values are invalid.",
"fieldErrors": {
"fieldValues.batch": ["\"Batch\" must be one of: Weekend, Weekday."]
}
}An unknown or archived type code returns a plain 400: { "error": "No active custom action with code 1001." }.
Success response — 201 Created
{
"lead": {
"id": "clx...",
"name": "Rahul Sharma",
"phone": "9876543210",
"source": "justdial",
"stage": "FRESH",
"assignee": null,
"actions": [],
"createdAt": "2026-07-22T...",
"updatedAt": "2026-07-22T..."
}
}Errors
| Field | Type | Required | Notes |
|---|---|---|---|
| 400 | Validation failed — check error/fieldErrors in the response body. | ||
| 401 | The key in the URL is missing, invalid, or has been revoked. | ||
| 429 | Rate limit exceeded for this key — wait and retry. |
Rate limit: 60 requests per minute per key, fixed window.
Triggering a workflow directly
If a Workflow's trigger node is set to API Trigger, fire it directly with the same org API key:
POST https://crm.scalenova.co.in/api/public/v1/workflows/{workflowId}/trigger/{your-api-key}
Content-Type: application/jsonBody: either { "leadId": "..." } for an existing lead, or { "name": "...", "phone": "...", "email": "..." } to match-or-create one. Returns { "ok": true, "leadId": "..." }.
Sending a WhatsApp OTP
If your org has connected a WhatsApp Business Account (Settings → WhatsApp) and has an approved AUTHENTICATION template, send a one-time code to a user's WhatsApp from your own backend using the same org API key:
POST https://crm.scalenova.co.in/api/public/v1/whatsapp/send-otp/{your-api-key}
Content-Type: application/jsonThis is a send-only relay. It does not generate, store, or verify the code — you create the OTP, this endpoint delivers it, and you verify it back in your own system when the user types it in.
| Field | Type | Required | Notes |
|---|---|---|---|
| to | string | Yes | Recipient's WhatsApp number, digits only (E.164 without the leading +), e.g. "919876543210". |
| templateName | string | Yes | Name of your connected, Meta-approved AUTHENTICATION template (Settings → WhatsApp → Templates). |
| languageCode | string | No | Defaults to "en_US". Must match the language the template was approved in. |
| otpCode | string | Yes | The one-time code. Dropped into both the template body's {{1}} placeholder and the mandatory OTP button — you do not build the button component yourself. |
| otpButtonType | string | No | Either "copy_code" (default) or "url". Must match how the template's OTP button was built in WhatsApp Manager — "Copy code" vs "One-tap autofill". Templates created via Settings → WhatsApp → Templates are always Copy code; use "url" only for a One-tap autofill template built directly in Meta's WhatsApp Manager, or Meta rejects the send with error #132018. |
Copy code template (the default):
curl -X POST \
https://crm.scalenova.co.in/api/public/v1/whatsapp/send-otp/sk_live_xxxxxxxxxxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{
"to": "919876543210",
"templateName": "otp_verification",
"languageCode": "en_US",
"otpCode": "482913"
}'One-tap autofill (URL button) template — add otpButtonType:
curl -X POST \
https://crm.scalenova.co.in/api/public/v1/whatsapp/send-otp/sk_live_xxxxxxxxxxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{
"to": "919876543210",
"templateName": "otp_verification",
"languageCode": "en_US",
"otpCode": "482913",
"otpButtonType": "url"
}'Success response — 200 OK:
{ "sent": true, "wabaMessageId": "wamid.HBgL..." }| Field | Type | Required | Notes |
|---|---|---|---|
| 400 | Validation failed, or Meta's Graph API rejected the send (template not approved, wrong language code, wrong button type) — check error in the response body. | ||
| 401 | The key in the URL is missing, invalid, or has been revoked. | ||
| 402 | Org has no active plan, or the plan doesn't include WhatsApp Business / the Public API. | ||
| 429 | Rate limit exceeded for this key — 60 requests per minute, fixed window. |