Developer documentation

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.

6 integration endpoints · JSON over HTTPS · Verified 11 October 2026

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/json
Open 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_idphone

Endpoint 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.

Supported external integration endpoints
EndpointOperationSuccessLimit / minute
POST /api/automation/intakeCreate a lead20160
POST /api/automation/lead-detailsRead a lead and its notes200120
POST /api/automation/lead-noteAdd a lead note200120
POST /api/automation/lead-pipelineUpdate the lead stage200120
POST /api/automation/lead-assigneeChange the lead assignee200120
POST /api/automation/task-createCreate a task201120
POST/api/automation/intake

Create 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.

JSON request parameters
ParameterTypeRequirementDescription / default
namestringRequiredLead name, 2–200 characters after trimming.
phonestringRequiredRequired mobile number, 5–32 input characters and at least 7 digits; normalized before duplicate checking.
emailstringOptionalValid email address; empty string is accepted and stored as null.
business_namestringOptionalBusiness name. Default: empty string.
pan_vat_numberstringOptionalPAN/VAT number. Default: null.
lead_datedateOptionalValid YYYY-MM-DD. Omitted or empty: today's Nepal date.
earned_amountnumberOptionalNPR amount, 0–1,000,000,000,000. Default: 0. Numeric strings are also coerced.
expected_budgetnumberOptionalNPR amount, 0–999,999,999,999.99. Default: 0. Numeric strings are also coerced.
sourcestringOptionalLead source. Default: Automation API.
assignee_emailstringOptionalEmail of an active member in this workspace. Alias: assigneeEmail.
pipeline_stagestringOptionalConfigured stage label or value; omit for the default stage.
pipeline_option_idstringOptionalConfigured stage option ID. If both ID and stage are sent, the server searches for either match; send one for clarity.
interested_instringOptionalConfigured interest label.
interested_option_idstringOptionalConfigured interest option ID; omit both interest fields for no selection.
city_districtstringOptionalConfigured city/district label. Alias: city.
city_option_idstringOptionalConfigured city option ID; omit both city fields for no selection.
next_follow_up_atdate-timeOptionalValid date/time. Prefer ISO 8601 with +05:45 or Z. Omitted/empty: null. Dates without an offset are interpreted in Asia/Kathmandu.
notesstringOptionalInitial note. Default: empty string.

Request example

cURL / POSIX shell. Replace the token and sample values with your own workspace data.

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.
POST/api/automation/lead-details

Read 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.

JSON request parameters
ParameterTypeRequirementDescription / default
crm_idstringOne requiredStable CRM ID: CRM- followed by 12 digits. Alias: crmId.
supabase_idUUIDOne requiredLead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId.
accounting_idstringOne requiredStable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID.
phonestringOne requiredMobile 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_enquiriesbooleanOptionalDefault false. Include private WordPress WhatsApp enquiry page/title/time with independent pagination; normal Bearer and lead authorization required.
whatsapp_enquiries_cursorstringOptionalOpaque whatsapp_enquiries.next_cursor, independent of notes/form pagination.
whatsapp_enquiries_limitintegerOptionalPage size 1–50, default20.
include_form_submissionsbooleanOptionalDefault false. Include private form inquiry answers as form_submissions with independent pagination; lead access and normal Bearer authentication required.
form_submissions_limitintegerOptionalForm submissions per page:1–50, default20. Independent from notes_limit.
form_submissions_cursorstringOptionalOpaque form_submissions.next_cursor from the same actor/workspace/lead. Follow until form_submissions.has_more is false; do not use note cursors.
include_notesbooleanOptionalDefault: true. false skips note-history fetching; notes is empty and has_more is false, but lead.latest_note remains available.
notes_limitintegerOptional1–100, default 50. Numeric strings are coerced. This is a page size, not the total note count.
notes_beforedate-timeOptionalOptional ISO timestamp with offset from the previous next_cursor.created_at. Example: 2026-10-11T04:00:00Z.
notes_before_idUUIDOptionalUse next_cursor.id with notes_before for stable pagination when timestamps are equal. On its own it does not filter notes.

Request example

cURL / POSIX shell. Replace the token and sample values with your own workspace data.

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.
POST/api/automation/lead-note

Add 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.

JSON request parameters
ParameterTypeRequirementDescription / default
crm_idstringOne requiredStable CRM ID: CRM- followed by 12 digits. Alias: crmId.
supabase_idUUIDOne requiredLead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId.
accounting_idstringOne requiredStable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID.
phonestringOne requiredMobile 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.
notestringRequiredNon-empty after trimming. Alias: notes. No explicit maximum is enforced by this route; keep notes concise.

Request example

cURL / POSIX shell. Replace the token and sample values with your own workspace data.

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.
POST/api/automation/lead-pipeline

Update 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.

JSON request parameters
ParameterTypeRequirementDescription / default
crm_idstringOne requiredStable CRM ID: CRM- followed by 12 digits. Alias: crmId.
supabase_idUUIDOne requiredLead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId.
accounting_idstringOne requiredStable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID.
phonestringOne requiredMobile 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_idstringOne selector requiredOne stage selector is required. Configured stage option ID. Alias: pipelineOptionId.
pipeline_stagestringOne selector requiredConfigured stage ID, label or value. Alias: pipelineStage.
pipeline_labelstringOne selector requiredConfigured stage display label. Alias: pipelineLabel.

Request example

cURL / POSIX shell. Replace the token and sample values with your own workspace data.

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.
POST/api/automation/lead-assignee

Change 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.

JSON request parameters
ParameterTypeRequirementDescription / default
crm_idstringOne requiredStable CRM ID: CRM- followed by 12 digits. Alias: crmId.
supabase_idUUIDOne requiredLead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId.
accounting_idstringOne requiredStable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID.
phonestringOne requiredMobile 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_emailstringOptionalOptional 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 / POSIX shell. Replace the token and sample values with your own workspace data.

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.
POST/api/automation/task-create

Create a task

Create a task, optionally linked to a lead by phone or any of its three identifiers.

JSON request parameters
ParameterTypeRequirementDescription / default
titlestringRequiredTask title, 1–200 characters after trimming.
descriptionstringOptionalTask details. Default: empty string.
assignee_emailstringOptionalOptional valid active member email. Alias: assigneeEmail. Omitted: token owner's profile ID. Empty string is invalid.
priorityenumOptionallow, medium or high. Default: medium.
due_atdate-timeOptionalOptional valid date/timestamp. Alias: deadline. Omitted/empty: no deadline. Prefer an explicit offset; offset-free values use Asia/Kathmandu.
crm_idstringOptionalStable CRM ID: CRM- followed by 12 digits. Alias: crmId.
supabase_idUUIDOptionalLead UUID returned by the CRM. Aliases: supabaseId, lead_id, leadId.
accounting_idstringOptionalStable internal Accounting ID: ACC- followed by 12 digits. Alias: accountingId. This is not an external accounting vendor's customer ID.
phonestringOptionalMobile 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 / POSIX shell. Replace the token and sample values with your own workspace data.

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.