Connect your tools.
Keep customer context.
Integrate calling agents, automation workflows and your own software with AI CRM Nepal. Every supported external endpoint, its parameters and examples in one place.
Authentication & setup
Sign in to the CRM and open Automation with a profile that has automation permission. Generate a workspace Bearer token, copy it to your server's secret storage and enable API lead intake. Send Authorization: Bearer YOUR_WORKSPACE_TOKEN and Content-Type: application/json. All six integration endpoints use POST with a JSON body, including reads. Tokens are workspace-scoped; do not send a workspace_id to switch tenants. Revoking or regenerating invalidates the old token. Intake, lead-details and task-create require intake to be enabled. The three update endpoints validate an active token but currently do not require that toggle to be on; revoke the token to stop every integration.
Base URL: https://www.aicrmnepal.com
Authorization: Bearer YOUR_WORKSPACE_TOKEN
Content-Type: application/jsonOpen your workspace Automation settings Four ways to find one lead
For lead-details, lead-note, lead-pipeline and lead-assignee, at least one of crm_id, supabase_id, accounting_id or phone is compulsory. The same references optionally link task-create to a lead. Send only one identifier per request. If multiple are supplied, every provided value is validated, then lookup priority is CRM ID > Supabase ID > Accounting ID > phone. An ID that is not found never falls back to the phone. IDs are generated by the CRM and cannot be changed through these APIs. Always use IDs returned by your own workspace.
crm_idsupabase_idaccounting_idphoneEndpoint reference
All operations below use POST and the workspace Bearer token. Required fields are marked explicitly. A lead reference is conditionally required for the four read/update endpoints; it is optional when creating a task.
| Endpoint | Operation | Success | Limit / minute |
|---|---|---|---|
POST /api/automation/intake | Create a lead | 201 | 60 |
POST /api/automation/lead-details | Read a lead and its notes | 200 | 120 |
POST /api/automation/lead-note | Add a lead note | 200 | 120 |
POST /api/automation/lead-pipeline | Update the lead stage | 200 | 120 |
POST /api/automation/lead-assignee | Change the lead assignee | 200 | 120 |
POST /api/automation/task-create | Create a task | 201 | 120 |
/api/automation/intakeCreate a lead
Create a new lead with generated CRM, Supabase and Accounting identifiers. Existing phone numbers return 409; this endpoint does not upsert or update existing leads.
| Parameter | Type | Requirement | Description / default |
|---|---|---|---|
name | string | Required | Lead name, 2–200 characters after trimming. |
phone | string | Required | Required mobile number, 5–32 input characters and at least 7 digits; normalized before duplicate checking. |
email | string | Optional | Valid email address; empty string is accepted and stored as null. |
business_name | string | Optional | Business name. Default: empty string. |
pan_vat_number | string | Optional | PAN/VAT number. Default: null. |
lead_date | date | Optional | Valid YYYY-MM-DD. Omitted or empty: today's Nepal date. |
earned_amount | number | Optional | NPR amount, 0–1,000,000,000,000. Default: 0. Numeric strings are also coerced. |
expected_budget | number | Optional | NPR amount, 0–999,999,999,999.99. Default: 0. Numeric strings are also coerced. |
source | string | Optional | Lead source. Default: Automation API. |
assignee_email | string | Optional | Email of an active member in this workspace. Alias: assigneeEmail. |
pipeline_stage | string | Optional | Configured stage label or value; omit for the default stage. |
pipeline_option_id | string | Optional | Configured stage option ID. If both ID and stage are sent, the server searches for either match; send one for clarity. |
interested_in | string | Optional | Configured interest label. |
interested_option_id | string | Optional | Configured interest option ID; omit both interest fields for no selection. |
city_district | string | Optional | Configured city/district label. Alias: city. |
city_option_id | string | Optional | Configured city option ID; omit both city fields for no selection. |
next_follow_up_at | date-time | Optional | Valid date/time. Prefer ISO 8601 with +05:45 or Z. Omitted/empty: null. Dates without an offset are interpreted in Asia/Kathmandu. |
notes | string | Optional | Initial note. Default: empty string. |
Request example
curl --request POST 'https://www.aicrmnepal.com/api/automation/intake' \
--header "Authorization: Bearer $CRM_AUTOMATION_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"name": "Example Customer",
"phone": "+9779800000000",
"source": "Calling agent",
"notes": "Requested a product walkthrough."
}'Successful response · 201
{ lead: Lead, identifiers: { crm_id, supabase_id, accounting_id } }- Get the workspace's configured stage, interest, city and member values from its CRM settings; there is no Bearer-token options-discovery endpoint.
- Without an assignee email, the configured default assignment is used. Invalid option/member values return 400.
- Lead-plan capacity returns 402; a suspended workspace returns 403. Intake note history is processed asynchronously; do not assume the initial note is immediately in the paginated activity history.
/api/automation/lead-detailsRead a lead and its notes
Look up one lead in the token's workspace using any one of the four reference types. Read current fields quickly or page through conversation history.
Required: at least one lead reference: phone, crm_id, supabase_id or accounting_id.
| Parameter | Type | Requirement | Description / default |
|---|---|---|---|
crm_id | string | One required | Stable CRM ID: CRM- followed by 12 digits. Alias: crmId. |
supabase_id | UUID | One required | Lead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId. |
accounting_id | string | One required | Stable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID. |
phone | string | One required | Mobile number. Aliases: lead_number, leadNumber. Nepal 10-digit numbers starting with 9 normalize to +977; formatted numbers normalize to digits with a + prefix. At least 7 digits. |
include_whatsapp_enquiries | boolean | Optional | Default false. Include private WordPress WhatsApp enquiry page/title/time with independent pagination; normal Bearer and lead authorization required. |
whatsapp_enquiries_cursor | string | Optional | Opaque whatsapp_enquiries.next_cursor, independent of notes/form pagination. |
whatsapp_enquiries_limit | integer | Optional | Page size 1–50, default20. |
include_form_submissions | boolean | Optional | Default false. Include private form inquiry answers as form_submissions with independent pagination; lead access and normal Bearer authentication required. |
form_submissions_limit | integer | Optional | Form submissions per page:1–50, default20. Independent from notes_limit. |
form_submissions_cursor | string | Optional | Opaque form_submissions.next_cursor from the same actor/workspace/lead. Follow until form_submissions.has_more is false; do not use note cursors. |
include_notes | boolean | Optional | Default: true. false skips note-history fetching; notes is empty and has_more is false, but lead.latest_note remains available. |
notes_limit | integer | Optional | 1–100, default 50. Numeric strings are coerced. This is a page size, not the total note count. |
notes_before | date-time | Optional | Optional ISO timestamp with offset from the previous next_cursor.created_at. Example: 2026-10-11T04:00:00Z. |
notes_before_id | UUID | Optional | Use next_cursor.id with notes_before for stable pagination when timestamps are equal. On its own it does not filter notes. |
Request example
curl --request POST 'https://www.aicrmnepal.com/api/automation/lead-details' \
--header "Authorization: Bearer $CRM_AUTOMATION_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"phone": "+9779800000000",
"include_notes": true,
"notes_limit": 100
}'Successful response · 200
{ lead: Lead, notes: Note[], notes_pagination: { included, limit, has_more, next_cursor }, form_submissions?: { submissions, has_more, next_cursor } }- History contains note activities only, not every audit or stage-change activity. Order: newest created_at first, then descending note UUID.
- To read all notes, keep requesting with BOTH returned cursor fields until has_more is false. include_notes:true alone returns only the first page.
- Successful responses are private, no-store. The documentation's sample IDs/numbers are illustrative and are not live customer records.
/api/automation/lead-noteAdd a lead note
Append a note activity to the lead's history and replace the lead's latest-note summary.
Required: at least one lead reference: phone, crm_id, supabase_id or accounting_id.
| Parameter | Type | Requirement | Description / default |
|---|---|---|---|
crm_id | string | One required | Stable CRM ID: CRM- followed by 12 digits. Alias: crmId. |
supabase_id | UUID | One required | Lead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId. |
accounting_id | string | One required | Stable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID. |
phone | string | One required | Mobile number. Aliases: lead_number, leadNumber. Nepal 10-digit numbers starting with 9 normalize to +977; formatted numbers normalize to digits with a + prefix. At least 7 digits. |
note | string | Required | Non-empty after trimming. Alias: notes. No explicit maximum is enforced by this route; keep notes concise. |
Request example
curl --request POST 'https://www.aicrmnepal.com/api/automation/lead-note' \
--header "Authorization: Bearer $CRM_AUTOMATION_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"crm_id": "CRM-000000000001",
"note": "Customer requested a callback tomorrow."
}'Successful response · 200
{ lead: Lead }- A successful response waits for note-history creation. Repeating the same request appends another activity; there is no idempotency key or automatic duplicate suppression.
/api/automation/lead-pipelineUpdate the lead stage
Set a configured stage and derive the lead status from that stage. This existing API works with lead stages; it does not require a separate pipeline-board feature.
Required: at least one lead reference: phone, crm_id, supabase_id or accounting_id.
| Parameter | Type | Requirement | Description / default |
|---|---|---|---|
crm_id | string | One required | Stable CRM ID: CRM- followed by 12 digits. Alias: crmId. |
supabase_id | UUID | One required | Lead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId. |
accounting_id | string | One required | Stable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID. |
phone | string | One required | Mobile number. Aliases: lead_number, leadNumber. Nepal 10-digit numbers starting with 9 normalize to +977; formatted numbers normalize to digits with a + prefix. At least 7 digits. |
pipeline_option_id | string | One selector required | One stage selector is required. Configured stage option ID. Alias: pipelineOptionId. |
pipeline_stage | string | One selector required | Configured stage ID, label or value. Alias: pipelineStage. |
pipeline_label | string | One selector required | Configured stage display label. Alias: pipelineLabel. |
Request example
curl --request POST 'https://www.aicrmnepal.com/api/automation/lead-pipeline' \
--header "Authorization: Bearer $CRM_AUTOMATION_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"accounting_id": "ACC-000000000001",
"pipeline_stage": "contacted"
}'Successful response · 200
{ lead: Lead, changeActivities?: Activity[], unchanged?: true }- Provide at least one stage selector. A matching option ID takes priority; if absent/invalid the server tries the stage, then label when stage is omitted. Use one valid selector to avoid ambiguity.
- The same selection returns unchanged:true. Other selections may trigger the configured stage-change webhook.
/api/automation/lead-assigneeChange the lead assignee
Assign one active team member by email, or reset to the configured default assignment.
Required: at least one lead reference: phone, crm_id, supabase_id or accounting_id.
| Parameter | Type | Requirement | Description / default |
|---|---|---|---|
crm_id | string | One required | Stable CRM ID: CRM- followed by 12 digits. Alias: crmId. |
supabase_id | UUID | One required | Lead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId. |
accounting_id | string | One required | Stable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID. |
phone | string | One required | Mobile number. Aliases: lead_number, leadNumber. Nepal 10-digit numbers starting with 9 normalize to +977; formatted numbers normalize to digits with a + prefix. At least 7 digits. |
assignee_email | string | Optional | Optional active member email. Alias: assigneeEmail. Omitted or empty resets the lead to its configured default assignment; it does not preserve the current assignee. |
Request example
curl --request POST 'https://www.aicrmnepal.com/api/automation/lead-assignee' \
--header "Authorization: Bearer $CRM_AUTOMATION_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"supabase_id": "00000000-0000-4000-8000-000000000001",
"assignee_email": "member@example.com"
}'Successful response · 200
{ lead: Lead, changeActivities?: Activity[], unchanged?: true }- An unknown or inactive member returns 400. Same assignment returns unchanged:true. The endpoint selects one member; it is not a multi-assignee write API.
/api/automation/task-createCreate a task
Create a task, optionally linked to a lead by phone or any of its three identifiers.
| Parameter | Type | Requirement | Description / default |
|---|---|---|---|
title | string | Required | Task title, 1–200 characters after trimming. |
description | string | Optional | Task details. Default: empty string. |
assignee_email | string | Optional | Optional valid active member email. Alias: assigneeEmail. Omitted: token owner's profile ID. Empty string is invalid. |
priority | enum | Optional | low, medium or high. Default: medium. |
due_at | date-time | Optional | Optional valid date/timestamp. Alias: deadline. Omitted/empty: no deadline. Prefer an explicit offset; offset-free values use Asia/Kathmandu. |
crm_id | string | Optional | Stable CRM ID: CRM- followed by 12 digits. Alias: crmId. |
supabase_id | UUID | Optional | Lead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId. |
accounting_id | string | Optional | Stable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID. |
phone | string | Optional | Mobile number. Aliases: lead_number, leadNumber. Nepal 10-digit numbers starting with 9 normalize to +977; formatted numbers normalize to digits with a + prefix. At least 7 digits. Task inputs allow up to 64 characters. |
Request example
curl --request POST 'https://www.aicrmnepal.com/api/automation/task-create' \
--header "Authorization: Bearer $CRM_AUTOMATION_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"title": "Call customer back",
"priority": "high",
"due_at": "2026-10-11T11:00:00+05:45",
"phone": "+9779800000000"
}'Successful response · 201
{ task: Task, lead_reference: { crm_id, supabase_id, accounting_id } | null }- Lead reference fields are all optional here. If one is supplied, it must resolve in the workspace or the request fails; omitted: unlinked task.
- Task CRM IDs must already be uppercase. Accounting IDs are normalized to uppercase. Supabase/lead ID aliases must be UUIDs.
- Task response includes the generated task ID, workspace_id, assigned_to, created_by, title, description, status, priority, due_at, lead_id and timestamps. Default new status: open. This external API does not update task status.
Give your calling agent every note
include_notes: true includes one page, up to 100 notes. Fetch subsequent pages with both cursor fields until has_more is false. This example fails on HTTP errors so the agent does not silently use incomplete history.
// Node.js backend. Keep the workspace token in a secret environment variable.
const reference = { phone: "+9779800000000" }; // Or one of the three IDs.
const allNotes = [];
let cursor = null;
do {
const response = await fetch("https://www.aicrmnepal.com/api/automation/lead-details", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.CRM_AUTOMATION_TOKEN,
"Content-Type": "application/json" },
body: JSON.stringify({ ...reference, include_notes: true, notes_limit: 100,
...(cursor ? { notes_before: cursor.created_at, notes_before_id: cursor.id } : {}) })
});
const result = await response.json();
if (!response.ok) throw new Error(result.error || "CRM lookup failed");
allNotes.push(...result.notes);
cursor = result.notes_pagination.has_more ? result.notes_pagination.next_cursor : null;
} while (cursor);
// allNotes is newest-first. Reverse a copy for chronological agent context.
const chronologicalNotes = [...allNotes].reverse();Illustrative lookup response
This abbreviated example shows the identifiers and note cursor; the actual lead object contains the fields described below.
{
"lead": {
"id": "00000000-0000-4000-8000-000000000001",
"crm_id": "CRM-000000000001",
"supabase_id": "00000000-0000-4000-8000-000000000001",
"accounting_id": "ACC-000000000001",
"name": "Example Customer",
"phone": "+9779800000000",
"email": null,
"pipeline_stage": "contacted",
"latest_note": "Requested a callback."
},
"notes": [
{
"id": "00000000-0000-4000-8000-000000000002",
"content": "Requested a callback.",
"created_at": "2026-10-11T04:00:00Z",
"author": null
}
],
"notes_pagination": {
"included": true,
"limit": 100,
"has_more": false,
"next_cursor": null
}
}Response objects
Lead details return id, crm_id, supabase_id, accounting_id, name, phone, email, business_name, pan_vat_number, lead_date, next_follow_up_at, expected_budget, earned_amount, source, status, pipeline_stage, pipeline_option_id, pipeline_label, interested_option_id, interested_in, city_option_id, city_district, assignee_ids, assignee_labels, assignee, creator, latest_note, created_at and updated_at. Other lead-write endpoints return the lead snapshot with pipeline_display_label, interested_label, city_label and notes names rather than the details endpoint's display aliases. Nullable fields may be null. Notes contain id, content, created_at and author (id, full_name, email, role); author may be null.
Rate limits & errors
Request JSON is limited to 64 KiB (65,536 UTF-8 bytes), including chunked uploads. Oversized bodies return 413. Each workspace has a separate 60-second window per endpoint: intake 60 requests, each other integration endpoint 120. Read X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix seconds) and X-RateLimit-Policy when present; some early validation errors omit them. 429 includes Retry-After in seconds. Errors are JSON with error and sometimes fields, message or retryAfterSeconds. 400: invalid payload/options/member; 401: missing, invalid, revoked token or disabled required intake; 402: intake lead-plan limit; 403: intake workspace suspended; 404: lead not found in this workspace; 409: duplicate intake phone; 429: rate limit; 500: server failure. Retry 429 after Retry-After and transient server failures with bounded backoff. After an ambiguous write/network timeout, reconcile before retrying: note/task writes are not idempotent and can duplicate.
Calling agents & accounting sync
Before a call, use lead-details with include_notes:true and drain every note page using its two-part cursor. Use include_notes:false for a faster current-field read when history is unnecessary. After the call, add the outcome through lead-note and create a follow-up task. Keep CRM, Supabase and Accounting IDs in your integration's mapping table. accounting_id is our internal ACC identifier; mapping to an external accounting customer ID is your integration's responsibility. Do not claim an accounting provider is already connected. Only the six documented operations are supported with workspace Bearer tokens: there is no external lead-list, arbitrary-field edit, delete, task-list or project API yet.
Webhooks: events & delivery
In Automation, configure separate publicly reachable HTTPS URLs and enable the events you need: lead.created, lead.pipeline_changed, lead.assignee_changed and lead.follow_up_due. Standard event payloads have event, triggered_at, profile_id, workspace_id and lead. Stage-change adds previous_pipeline_stage and pipeline_stage. Assignee-change adds previous_assignees and assignees, and is skipped for default-only assignment. Follow-up-due adds event_id and scheduled_for. Standard lead webhook snapshots omit status/status_option_id/status_display_label; due-event snapshots currently include the stored lead fields. Creation, stage and assignment events attempt delivery up to three times with a 5-second timeout and 800/1600ms delays for retryable failures. Due events use a worker with up to five attempts and scheduled backoff; delivery timing depends on that worker. A successful API write is not confirmation that its webhook was delivered. Return 2xx promptly, process work asynchronously, and deduplicate due events using event_id.
Verify webhook signatures
Read X-AI-CRM-Timestamp, X-AI-CRM-Signature, X-AI-CRM-Event, X-AI-CRM-Workspace, X-AI-CRM-Delivery-Attempt and X-AI-CRM-Delivery-ID. The signature is sha256= followed by HMAC-SHA256 using the workspace's current automation token over timestamp + '.' + the exact raw JSON body. Do not parse/re-serialize the body before verification. Use a timing-safe comparison, reject missing/invalid signatures, verify expected workspace/event and reject stale timestamps (for example a five-minute tolerance). Standard events may have an empty delivery ID; do not treat it as a universal deduplication key. Update the verification secret when rotating the token.
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody must be the exact request body string; read it BEFORE parsing JSON.
function verifyWebhook(rawBody, timestamp, signature, expectedToken) {
if (!timestamp || !signature || !expectedToken) return false;
const age = Math.abs(Date.now() - Date.parse(timestamp));
if (!Number.isFinite(age) || age > 5 * 60 * 1000) return false;
const expected = "sha256=" + createHmac("sha256", expectedToken)
.update(timestamp + "." + rawBody).digest("hex");
const actualBytes = Buffer.from(signature);
const expectedBytes = Buffer.from(expected);
return actualBytes.length === expectedBytes.length &&
timingSafeEqual(actualBytes, expectedBytes);
}
// Also verify the expected workspace/event before processing the parsed body.Browser and internal routes
Call integration endpoints from your backend or automation tool; no browser CORS support is promised. Never embed the Bearer token in frontend code, public URLs or AI prompts. /api/automation/settings (GET/PATCH) and /api/automation/token (POST: revoke/regenerate) are authenticated CRM-session management routes with automation permission and same-origin protection for writes. /api/automation/follow-up-dispatch (POST) is an internal worker route with a separate server secret. The application's /api/leads, /api/tasks, /api/projects, /api/analytics, /api/admin and account/settings routes are session/admin APIs, not workspace-token integration endpoints. Use the CRM UI to manage these features; do not substitute a Bearer token for a CRM session.
Ready to connect your workspace?
Generate your token in Automation, choose one lead identifier and start with a read request before enabling writes.