Skip to main content
Skip to content

API Reference

The CloviCRM API lets you integrate CloviCRM into your own applications and automations. All endpoints are served over HTTPS from https://clovicrm.clovitek.com.

Authentication

Authenticate every request with a Bearer token in the Authorization header. Generate a token from your account settings on the CloviCRM dashboard.

curl https://clovicrm.clovitek.com/api/me \
  -H "Authorization: Bearer $CLOVI_TOKEN"
import requests

resp = requests.get(
    "https://clovicrm.clovitek.com/api/me",
    headers={"Authorization": f"Bearer {token}"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://clovicrm.clovitek.com/api/me", {
  headers: { Authorization: `Bearer ${token}` },
});
const data = await resp.json();
console.log(data);
import axios from "axios";

const { data } = await axios.get(
  "https://clovicrm.clovitek.com/api/me",
  { headers: { Authorization: `Bearer ${token}` } }
);
console.log(data);

Keep your token secret

Treat your API token like a password. Send it only over HTTPS and never commit it to source control — load it from an environment variable instead.

Responses & errors

All responses are JSON. Successful calls return 2xx; client errors return 4xx with a JSON body describing the problem. Common status codes:

Status Meaning
200 Success
400 Bad request — check your parameters
401 Missing or invalid token
404 Resource not found
429 Rate limit exceeded — slow down and retry
500 Server error — retry or contact support

CloviCRM API Reference

Status: Live backend at http://127.0.0.1:8978 (pm2 clovicrm).
Source: /root/clovicrm/server.js
Public domain: clovicrm.clovitek.com


Authentication

All /api/* routes (except the public widget routes and /health) are protected by requireAuth middleware.

Two accepted credential types:

Method Cookie/Header Notes
Platform SSO cl_session cookie Shared CloviTek AI JWT minted by clovitek-sso at port 8991
Standalone session crm_token cookie or Authorization: Bearer <token> Issued by /api/auth/login or /api/auth/register

Both are signed with JWT_SECRET from /root/.api_keys/master.env. Token lifetime is 30 days for standalone sessions. A 401 Unauthorized is returned when neither credential is present or valid.

Note: There is no separate developer/public API key system yet. All routes are application-layer routes consumed by the CloviCRM frontend and trusted internal integrations. A public REST API (Bearer API key) is planned.


Route Sections

  1. Auth Routes
  2. Contacts
  3. Companies
  4. Deals & Pipeline
  5. Tasks (CRM Reminders)
  6. Projects & Project Management
  7. AI Layer
  8. Public Widget API (no auth)
  9. Health

Auth Routes

POST /api/auth/register

Creates a new standalone CloviCRM account. Issues a crm_token cookie on success.

Auth: None required
Body (JSON):

Field Type Required Notes
email string yes Must be a valid email
password string yes Minimum 8 characters
name string no Display name

Example:

curl -s -c cookies.txt -X POST https://clovicrm.clovitek.com/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"secret123","name":"Your Name"}'
Response (201):
{ "success": true, "data": { "email": "you@example.com", "name": "Your Name" } }
Errors: 400 invalid email or short password; 409 email already registered.


POST /api/auth/login

Signs in a standalone account. Issues a crm_token cookie.

Auth: None required
Body (JSON):

Field Type Required
email string yes
password string yes

Example:

curl -s -c cookies.txt -X POST https://clovicrm.clovitek.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"secret123"}'
Response (200):
{ "success": true, "data": { "email": "you@example.com", "name": "Your Name" } }
Errors: 401 invalid credentials.


POST /api/auth/logout

Clears the crm_token cookie.

Auth: None required
Parameters: None
Response:

{ "success": true }


Contacts

Contact kind values: investor | client | lead | partner

GET /api/contacts

Returns all contacts, optionally filtered.

Auth: cl_session or crm_token / Bearer
Query params:

Param Type Notes
kind string Filter by investor, client, lead, or partner
source string Filter by custom.source tag (platform capture source)

Example:

curl -s -b cookies.txt "https://clovicrm.clovitek.com/api/contacts?kind=investor"
Response:
{
  "success": true,
  "data": [
    {
      "id": 1, "name": "Jane Smith", "company": "Acme Inc", "email": "jane@acme.com",
      "kind": "investor", "title": "GP", "owner": "you@example.com",
      "stage": "intro", "status": "active", "tags": [], "custom": {},
      "lastContact": "2026-06-10", "isDemo": false, "avatar": "JS"
    }
  ]
}
Errors: 400 invalid kind value.


POST /api/contacts

Creates a new contact.

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
name string yes
kind string no Default: lead
email string no
company string no
title string no
owner string no Defaults to authenticated user's email
stage string no
status string no Default: active
tags string[] no
custom object no Arbitrary key-value metadata

Example:

curl -s -b cookies.txt -X POST https://clovicrm.clovitek.com/api/contacts \
  -H "Content-Type: application/json" \
  -d '{"name":"Bob Lee","kind":"lead","email":"bob@example.com","company":"StartupCo"}'
Response (201):
{ "success": true, "data": { "id": 42, "name": "Bob Lee", "kind": "lead", "..." : "..." } }
Errors: 400 name is required.


POST /api/contacts/upsert

Idempotent upsert by (kind, email). If a matching record exists, it is updated (with shallow custom merge). If a client kind is submitted and a lead with the same email exists, that lead is promoted to client instead of creating a duplicate.

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
email string yes Lookup key
name string yes
kind string no Default: lead
source string no Stored in custom.source for per-platform views
company, title, owner, stage, status string no
custom object no Shallowly merged with existing custom
is_demo boolean no

Response:

{ "success": true, "created": false, "promoted": false, "data": { "...": "..." } }
- created: true — new record inserted
- promoted: true — lead promoted to client

Errors: 400 email or name missing.


PATCH /api/contacts/:id

Updates one or more fields of an existing contact.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON) — any subset of:

name, email, company, title, owner, stage, status, kind, tags (array), custom (object)

Example:

curl -s -b cookies.txt -X PATCH https://clovicrm.clovitek.com/api/contacts/42 \
  -H "Content-Type: application/json" \
  -d '{"stage":"qualified","status":"active"}'
Response:
{ "success": true, "data": { "id": 42, "stage": "qualified", "...": "..." } }
Errors: 400 no fields provided; 404 contact not found.


DELETE /api/contacts/:id

Deletes a contact.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Response:

{ "success": true }


GET /api/contacts/export

Downloads contacts as a CSV file.

Auth: cl_session or crm_token / Bearer
Query params:

Param Type Notes
kind string Optional filter

Response: Content-Type: text/csv download
Columns: id, name, email, company, kind, title, owner, stage, status, tags (pipe-delimited), lastContact

Errors: 400 invalid kind value.


POST /api/contacts/import

Imports contacts from a CSV. Deduplicates by (kind, email) via upsert. Rows without email are always inserted.

Auth: cl_session or crm_token / Bearer
Content-Type: text/csv (body = raw CSV) or application/json with { "csv": "..." }
CSV columns (case-insensitive): name (required), email, company, kind, title, owner, stage, status, tags (pipe or semicolon separated)

Example:

curl -s -b cookies.txt -X POST https://clovicrm.clovitek.com/api/contacts/import \
  -H "Content-Type: text/csv" \
  --data-binary @contacts.csv
Response:
{
  "success": true,
  "data": { "created": 5, "updated": 2, "skipped": 0, "total": 7, "errors": [] }
}
Errors: 400 no CSV provided, missing header row, or no name column.


GET /api/contacts/:id/activities

Returns the activity log for a contact (most recent first, up to 200 entries).

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Response:

{
  "success": true,
  "data": [
    { "id": 1, "actor": "you@example.com", "type": "note", "payload": { "note": "Called them." }, "at": "2026-06-10T12:00:00Z" }
  ]
}


POST /api/contacts/:id/activities

Appends an activity entry to a contact's log and refreshes last_contact_at.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON):

Field Type Required Notes
type string yes e.g. call, email, meeting, note
actor string no Defaults to authenticated user's email
payload object no Arbitrary metadata

Response (201):

{ "success": true }
Errors: 400 type missing; 404 contact not found.


POST /api/contacts/:id/notes

Convenience shortcut — writes a type:'note' activity and refreshes last_contact_at.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON):

Field Type Required
note or text string yes

Response (201):

{ "success": true }
Errors: 400 note text missing; 404 contact not found.


GET /api/sources

Returns distinct custom.source values with counts. Powers the per-platform source filter dropdown.

Auth: cl_session or crm_token / Bearer
Response:

{
  "success": true,
  "data": [
    { "source": "manuscriptsmith", "n": 14 },
    { "source": "clovilegal", "n": 3 }
  ]
}


Companies

GET /api/companies

Returns all companies with their contact counts.

Auth: cl_session or crm_token / Bearer
Response:

{
  "success": true,
  "data": [
    { "id": 1, "name": "Acme Inc", "domain": "acme.com", "industry": "SaaS", "custom": {}, "contact_count": 3, "isDemo": false, "created_at": "..." }
  ]
}


POST /api/companies

Creates a new company record.

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required
name string yes
domain string no
industry string no
custom object no

Response (201):

{ "success": true, "data": { "id": 5, "name": "Acme Inc", "...": "..." } }
Errors: 400 name required.


PATCH /api/companies/:id

Updates a company.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON) — any subset of: name, domain, industry, custom

Response:

{ "success": true, "data": { "...": "..." } }
Errors: 400 no fields; 404 not found.


DELETE /api/companies/:id

Deletes a company.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true }


GET /api/companies/:id/contacts

Returns all contacts linked to a company.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true, "data": [ { "id": 1, "name": "Jane Smith", "...": "..." } ] }


POST /api/companies/:id/link-contact

Links a contact to a company and syncs the denormalized company label on the contact row.

Auth: cl_session or crm_token / Bearer
Path params: id (company integer)
Body (JSON):

Field Type Required
contact_id integer yes

Response:

{ "success": true, "data": { "id": 42, "company": "Acme Inc", "...": "..." } }
Errors: 400 contact_id required; 404 company or contact not found.


Deals & Pipeline

GET /api/pipeline

Returns the full kanban pipeline: stages with their nested deals.

Auth: cl_session or crm_token / Bearer
Query params:

Param Type Notes
view string raise (investor pipeline) or operate (client pipeline); omit for all stages

Response:

{
  "success": true,
  "data": {
    "stages": [
      {
        "id": "intro", "label": "Intro", "color": "#3b82f6",
        "deals": [
          { "id": 1, "title": "Series A", "value": 500000, "contact": "Jane Smith", "contact_id": 7, "probability": 30, "days": 12 }
        ]
      }
    ]
  }
}


POST /api/pipeline-stages

Idempotent upsert of a pipeline stage by id. Used by provisioning scripts to seed product-specific stages (e.g. manuscriptsmith-trial).

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
id string yes Unique stage identifier
label string yes Display label
color string no Hex color, default #64748b
sort_order integer no Default 0

Response (201):

{ "success": true, "data": { "id": "manuscriptsmith-trial", "label": "Trial", "color": "#64748b", "sort_order": 0 } }
Errors: 400 id or label missing.


POST /api/deals

Creates a new deal and logs a deal_created activity.

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
title string yes
stage string no Pipeline stage id
value number no Default 0
probability number no 0-100, default 0
contact_id integer no

Response (201):

{ "success": true, "data": { "id": 9, "title": "Series A", "stage": "intro", "value": 500000, "...": "..." } }
Errors: 400 title required.


PATCH /api/deals/:id

Updates a deal (including moving it between pipeline stages). A stage_change activity is logged on stage updates.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON) — any subset of: title, stage, value, probability, contact_id

Response:

{ "success": true, "data": { "id": 9, "stage": "term-sheet", "...": "..." } }
Errors: 400 no fields; 404 not found.


GET /api/stats

Returns high-level CRM counts for the dashboard.

Auth: cl_session or crm_token / Bearer
Response:

{
  "success": true,
  "data": { "contacts": 84, "deals": 12, "revenue": 245000.00, "open_tasks": 5 }
}
- revenue = sum of values for deals with stage = 'closed' - deals = count of deals NOT in stage closed - open_tasks = count of type='task' activities where done is false


GET /api/chart/revenue

Returns a 30-day revenue chart dataset. This is deterministic seed data; it does not reflect live customer revenue.

Auth: cl_session or crm_token / Bearer
Response:

{
  "success": true,
  "data": {
    "labels": ["May 13", "May 14", "..."],
    "values": [1200, 800, "..."]
  }
}


Tasks (CRM Reminders)

These are CRM reminder/follow-up tasks stored in the tasks table. They are distinct from project management tasks (see /api/pm-tasks and /api/projects/:id/tasks).

GET /api/tasks

Returns CRM reminder tasks with optional filters.

Auth: cl_session or crm_token / Bearer
Query params:

Param Type Notes
done true or false Filter by completion state
contact_id integer Filter by linked contact
due string overdue (past + not done), today, or upcoming (future)

Response:

{
  "success": true,
  "data": [
    { "id": 1, "title": "Follow up with Jane", "contact_id": 7, "deal_id": null, "due_at": "2026-06-20T09:00:00Z", "done": false, "owner": "you@example.com", "contact_name": "Jane Smith", "created_at": "..." }
  ]
}


POST /api/tasks

Creates a CRM reminder task. Logs a task activity on the linked contact if contact_id is provided.

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
title string yes
contact_id integer no
deal_id integer no
due_at ISO datetime no
done boolean no Default false
owner string no Defaults to authenticated user's email

Response (201):

{ "success": true, "data": { "id": 3, "title": "Follow up with Jane", "...": "..." } }
Errors: 400 title required.


PATCH /api/tasks/:id

Updates a CRM reminder task.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON) — any subset of: title, due_at, done, owner, contact_id, deal_id

Response:

{ "success": true, "data": { "id": 3, "done": true, "...": "..." } }
Errors: 400 no fields; 404 not found.


DELETE /api/tasks/:id

Deletes a CRM reminder task.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true }


Projects & Project Management

CloviCRM includes a full project/task management layer (CloviProject). Database tables are pm_-prefixed to avoid collision with CRM reminder tasks.

GET /api/projects

Lists all projects with task counts.

Auth: cl_session or crm_token / Bearer
Response:

{
  "success": true,
  "data": [
    { "id": 1, "title": "Website Redesign", "description": "", "client_contact_id": 7, "client_name": "Jane Smith", "status": "active", "start_date": null, "deadline": "2026-08-01", "color": "#3d6ae0", "isDemo": false, "created_at": "...", "task_count": 5 }
  ]
}


POST /api/projects

Creates a new project.

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
title string yes
description string no
client_contact_id integer no Links to a CRM contact
status string no Default: active
start_date date no
deadline date no
color string no Hex, default #3d6ae0

Response (201):

{ "success": true, "data": { "id": 2, "title": "Website Redesign", "...": "..." } }
Errors: 400 title required.


GET /api/projects/:id

Returns a full project board payload: project metadata + kanban statuses + milestones + all tasks (with assignees).

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Response:

{
  "success": true,
  "data": {
    "project": { "id": 1, "title": "Website Redesign", "...": "..." },
    "statuses": [ { "id": 1, "label": "To Do", "color": "#64748b", "sort_order": 0, "is_done": false, "project_id": null } ],
    "milestones": [ { "id": 1, "project_id": 1, "title": "Beta Launch", "due_date": "2026-07-01", "sort_order": 0 } ],
    "tasks": [ { "id": 10, "project_id": 1, "title": "Set up hosting", "status_id": 1, "priority": "medium", "assignees": [{"ref":"you@example.com","name":"You"}], "...": "..." } ]
  }
}
Errors: 404 project not found.


PATCH /api/projects/:id

Updates a project.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON) — any subset of: title, description, client_contact_id, status, start_date, deadline, color

Response:

{ "success": true, "data": { "...": "..." } }
Errors: 400 no fields; 404 not found.


DELETE /api/projects/:id

Deletes a project.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true }


GET /api/projects/:id/tasks

Lists all project management tasks for a project (with assignees).

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true, "data": [ { "id": 10, "title": "Set up hosting", "assignees": [], "...": "..." } ] }


POST /api/projects/:id/tasks

Creates a task inside a project.

Auth: cl_session or crm_token / Bearer
Path params: id (project integer)
Body (JSON):

Field Type Required Notes
title string yes
description string no
status_id integer no Links to a pm_task_statuses row (kanban column)
priority string no Default: medium
milestone_id integer no
deadline date no
sort_order integer no Default: 0
context string no e.g. deal, contact
context_id integer no ID of linked context object
assignees array no [{ref, name}] or ["email@..."]

Response (201):

{ "success": true, "data": { "id": 11, "title": "Set up hosting", "assignees": [], "...": "..." } }
Errors: 400 title required; 404 project not found.


PATCH /api/pm-tasks/:id

Updates a project management task. This is also the kanban drag endpoint — update status_id and sort_order to move a card between columns. Replaces the full assignee set if assignees is provided.

Auth: cl_session or crm_token / Bearer
Path params: id (pm_task integer)
Body (JSON) — any subset of: title, description, status_id, priority, milestone_id, deadline, sort_order, context, context_id, assignees

Response:

{ "success": true, "data": { "id": 11, "status_id": 2, "assignees": [], "...": "..." } }
Errors: 404 task not found.


DELETE /api/pm-tasks/:id

Deletes a project management task.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true }


GET /api/task-statuses

Returns kanban column definitions. Org-level columns always included; pass project_id to also include project-specific columns.

Auth: cl_session or crm_token / Bearer
Query params:

Param Type Notes
project_id integer If provided, returns org-level + project-specific columns

Response:

{
  "success": true,
  "data": [ { "id": 1, "label": "To Do", "color": "#64748b", "sort_order": 0, "is_done": false, "project_id": null } ]
}


POST /api/task-statuses

Creates a new kanban column (task status).

Auth: cl_session or crm_token / Bearer
Body (JSON):

Field Type Required Notes
label string yes
project_id integer no null = org-level column
color string no Hex, default #64748b
sort_order integer no Default 0
is_done boolean no Tasks in this column are treated as done

Response (201):

{ "success": true, "data": { "id": 3, "label": "Done", "is_done": true, "...": "..." } }
Errors: 400 label required.


GET /api/projects/:id/milestones

Lists milestones for a project.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true, "data": [ { "id": 1, "project_id": 1, "title": "Beta Launch", "due_date": "2026-07-01", "sort_order": 0 } ] }


POST /api/projects/:id/milestones

Creates a milestone for a project.

Auth: cl_session or crm_token / Bearer
Path params: id (project integer)
Body (JSON):

Field Type Required
title string yes
due_date date no
sort_order integer no

Response (201):

{ "success": true, "data": { "id": 2, "project_id": 1, "title": "Beta Launch", "...": "..." } }
Errors: 400 title required; 404 project not found.


PATCH /api/milestones/:id

Updates a milestone.

Auth: cl_session or crm_token / Bearer
Path params: id (integer)
Body (JSON) — any subset of: title, due_date, sort_order

Response:

{ "success": true, "data": { "...": "..." } }
Errors: 400 no fields; 404 not found.


DELETE /api/milestones/:id

Deletes a milestone.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true }


AI Layer

Routes that call the crm_llm.py bridge (guarded llm_cascade). All AI routes require auth. They return 502 if the AI backend is unavailable rather than failing silently.

POST /api/ai/score-deal/:id

Scores a deal's close likelihood (0-100) using the AI layer and logs the result as an ai_score activity. Does not modify any deal columns.

Auth: cl_session or crm_token / Bearer
Path params: id (deal integer)
Body: None required
Response:

{
  "success": true,
  "deal_id": 9,
  "score": 72,
  "reasoning": "Deal is well-advanced with a warm contact in a buying role.",
  "next_action": "Send the final proposal by Friday.",
  "engine": "claude-3-haiku",
  "fell_back": false
}
Errors: 404 deal not found; 502 AI unavailable or unparseable output.


POST /api/ai/draft-email/:contactId

Drafts a personalized B2B email for a contact and logs an ai_email_draft activity.

Auth: cl_session or crm_token / Bearer
Path params: contactId (integer)
Body (JSON):

Field Type Required Notes
intent string no Default: "a friendly follow-up"

Example:

curl -s -b cookies.txt -X POST https://clovicrm.clovitek.com/api/ai/draft-email/42 \
  -H "Content-Type: application/json" \
  -d '{"intent":"introduce our new pricing plan"}'
Response:
{
  "success": true,
  "contact_id": 42,
  "subject": "Quick thought on your next step",
  "body": "Hi Bob,\n\nI wanted to reach out about...",
  "engine": "claude-3-haiku",
  "fell_back": false
}
Errors: 404 contact not found; 502 AI unavailable.


GET /api/ai/status

Probes whether the AI layer (crm_llm.py / llm_cascade) is reachable.

Auth: cl_session or crm_token / Bearer
Response:

{ "success": true, "ai_available": true, "engine": "claude-3-haiku" }
Or when unavailable:
{ "success": true, "ai_available": false, "engine": null, "detail": "llm bridge failed: ..." }


Public Widget API (no auth)

These endpoints have cors({ origin: '*' }) and require no authentication. They expose read-only pipeline summary data with no contact PII.

GET /api/widget/public/pipeline

Returns a public summary of the deal pipeline (stage labels, deal counts, aggregate values only).

Auth: None
Response:

{
  "success": true,
  "data": {
    "stages": [
      { "id": "intro", "label": "Intro", "color": "#3b82f6", "count": 3, "value": 150000 }
    ],
    "total_deals": 12,
    "total_value": 875000
  }
}


GET /widget/

Renders an embeddable HTML iframe widget displaying the pipeline. No authentication required.

Auth: None (CORS open)
Query params:

Param Type Notes
mode string badge (default) — single-line stat pill; card — per-stage list; panel — horizontal bar chart
theme string dark (default) or light

Example embed:

<iframe src="https://clovicrm.clovitek.com/widget/?mode=card&theme=dark" width="300" height="200" frameborder="0"></iframe>
Response: text/html rendered widget.


Health

GET /health

DB-aware health probe. Returns 503 if the PostgreSQL database is unreachable.

Auth: None
Response (200):

{ "status": "ok", "db": "up", "service": "clovicrm", "ts": "2026-06-16T10:00:00.000Z" }
Response (503):
{ "status": "degraded", "db": "down", "error": "connection refused" }


Route Index

Method Path Auth Category
POST /api/auth/register none Auth
POST /api/auth/login none Auth
POST /api/auth/logout none Auth
GET /api/contacts required Contacts
POST /api/contacts required Contacts
POST /api/contacts/upsert required Contacts
GET /api/contacts/export required Contacts
POST /api/contacts/import required Contacts
PATCH /api/contacts/:id required Contacts
DELETE /api/contacts/:id required Contacts
GET /api/contacts/:id/activities required Contacts
POST /api/contacts/:id/activities required Contacts
POST /api/contacts/:id/notes required Contacts
GET /api/sources required Contacts
GET /api/companies required Companies
POST /api/companies required Companies
PATCH /api/companies/:id required Companies
DELETE /api/companies/:id required Companies
GET /api/companies/:id/contacts required Companies
POST /api/companies/:id/link-contact required Companies
GET /api/pipeline required Deals
POST /api/pipeline-stages required Deals
POST /api/deals required Deals
PATCH /api/deals/:id required Deals
GET /api/stats required Deals
GET /api/chart/revenue required Deals
GET /api/tasks required Tasks
POST /api/tasks required Tasks
PATCH /api/tasks/:id required Tasks
DELETE /api/tasks/:id required Tasks
GET /api/projects required Projects
POST /api/projects required Projects
GET /api/projects/:id required Projects
PATCH /api/projects/:id required Projects
DELETE /api/projects/:id required Projects
GET /api/projects/:id/tasks required Projects
POST /api/projects/:id/tasks required Projects
PATCH /api/pm-tasks/:id required Projects
DELETE /api/pm-tasks/:id required Projects
GET /api/task-statuses required Projects
POST /api/task-statuses required Projects
GET /api/projects/:id/milestones required Projects
POST /api/projects/:id/milestones required Projects
PATCH /api/milestones/:id required Projects
DELETE /api/milestones/:id required Projects
POST /api/ai/score-deal/:id required AI
POST /api/ai/draft-email/:contactId required AI
GET /api/ai/status required AI
GET /api/widget/public/pipeline none Widget
GET /widget/ none Widget
GET /health none Health