> ## Documentation Index
> Fetch the complete documentation index at: https://developers.huechat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sales CRM

> Manage leads, products, price books and versioned quotes through account-scoped APIs.

The Sales CRM API is the system-to-system surface behind HueChat's Sales
workspace. It combines manually created leads with captures from WhatsApp,
Instagram, Messenger, forms, appointments and advertising, and provides the
account's product catalog, price books and draft quote register. Every record
stays scoped to the account that issued the API key.

## Before you start

Create a scoped API key in **HueChat → Account → Developer API** with
`leads:read` to list Leads, open a Lead and read its history. Add
`leads:write` to create or qualify Leads. Converting a Lead also requires
`deals:write`. Key creation and usage inspection stay inside HueChat and are
not public account-management endpoints.

Both **Deals** and **Sales Leads** must be enabled on the account plan. A key
with valid scopes receives `402` with `code: "plan_disabled"` when either
entitlement is off. This plan check happens before lead data is read or changed.

Products, price books and quotes require **Deals** and **Sales Products &
Quotes**. Use `deals:read` for their list routes and `deals:write` for create,
archive and line-entry routes. The same `402` contract applies before any
tenant data is read.

<Warning>
  Use the account id that issued the key. Leads, contacts, companies, branches,
  conversations and deals from another account are never resolved.
</Warning>

## Create a lead

API keys have no team-member identity, so send `owner_id` for the member who
will own the lead. The signed-in HueChat app can omit it and use its
authenticated member. A lead is never committed without an owner: the lead,
first status receipt and first ownership receipt are one transaction.

```bash theme={null}
curl -X POST "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/leads" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "manual",
    "owner_id": 42,
    "full_name": "Sara Alotaibi",
    "phone": "+966501234567",
    "email": "sara@example.com",
    "company": "Example Trading",
    "rating": "warm",
    "product_interest": "Customer engagement",
    "next_action": "Schedule discovery call",
    "next_action_at": "2026-09-21T09:00:00Z",
    "dedup_key": "crm-import-8842"
  }'
```

`source: "ad"` is intentionally refused. Meta, TikTok, Snap and Google
ingestion own those rows so an API client cannot manufacture a lead that the
Ads workspace reports as a real campaign capture.

## List and filter

Call `GET /api/v2/accounts/{accountId}/leads`. Results are newest first. Use
`page` and `per_page` (maximum 100), plus optional `source`, `status`,
`owner_id`, `branch_id` or `unassigned=true` filters.

```bash theme={null}
curl --get "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/leads" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  --data-urlencode "status=qualified" \
  --data-urlencode "per_page=30"
```

## Change qualification status

Post a status to `/leads/{leadId}/status`. Supported transitions use `new`,
`working`, `qualified`, `disqualified` and `processed`. Include a
`disqualified_reason` when disqualifying, and use `recycle_at` when the lead
should return later.

Do not send `converted` to this endpoint. Conversion owns the deal link and
its idempotency, so it has a dedicated route.

## Convert a qualified Lead

`POST /leads/{leadId}/convert` can link a qualified Lead to an existing Deal,
or atomically create the reviewed Company and Deal selected in the conversion
preview. Send a unique `Idempotency-Key` header. Repeating the same key returns
the original result; reusing it for another Lead returns `409`.

```bash theme={null}
curl -X POST "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/leads/$LEAD_ID/convert" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  -H "Idempotency-Key: lead-conversion-8842" \
  -H "Content-Type: application/json" \
  -d '{
    "deal_id": "b8cb6c91-a805-41f4-ac4f-beb462bb7a31",
    "reason": "Discovery completed"
  }'
```

For a new opportunity, send `create_deal: true`, `deal_name`, amount, currency,
close date and forecast category. `create_company: true` and `company_name`
create the tenant-owned Company in the same transaction. A Lead already
converted by another key, or linked to another Deal, returns `409`.

## Read the audit trail

`GET /leads/{leadId}/history` returns append-only `status_history` and
`ownership_history` arrays, newest first and capped at 200 entries each. Use
these receipts for synchronization and audit; do not infer history from only
the lead's current fields.

## Products and services

Use `GET /sales/products` to list the account catalog and filter it with `q`,
`active`, `page` and `per_page`. Create a product or service with
`POST /sales/products`; `standard_price` is an exact decimal string such as
`"1250.00"`, and `currency` is a three-letter uppercase code. Archive or
restore it with `PATCH /sales/products/{productId}/active`.

SKUs are unique only within the account. Validity dates are civil dates; when
both are present, `valid_until` cannot precede `valid_from`.

## Price books

Create and list account price books at `/sales/price-books`. A price-book entry
is managed at `/sales/price-books/{priceBookId}/entries` and snapshots a list
price for one active product. The book and product must be active, belong to
the same account and use the same currency. Repeating the same product in one
book returns `409`.

Clone a complete book with `POST /sales/price-books/{priceBookId}/clone` and a
unique `name`. The operation is transactional, copies active and archived
entries, and always creates a custom book so it cannot replace the standard
book for that currency.

## The 360-degree Sales record

Sales records do not create a second customer database. A Lead resolves to the
canonical Customer CDP contact, the Company groups that contact's commercial
relationships, and Deals carry the pipeline, tasks, products, quotes,
payments, appointments, forms and permitted conversation activity. Use:

* `GET /sales/overview` for pipeline totals, stage aging and work queues.
* `GET /sales/companies/{companyId}` for hierarchy, contact roles, Deals,
  tasks, notes, files, revenue and history.
* `GET /sales/deals/{dealId}` for the complete opportunity record, including
  line items, roles, quotes, provider-reported payments and linked activity.
* `GET /contacts/{contactId}/profile` and the Customer CDP endpoints for the
  same person's identity, consent, memberships, journey activity and unified
  timeline.

Removing a Company or Deal role never deletes the canonical contact. Every
relationship lookup is account-scoped, and conversation activity is limited
to inboxes the caller is allowed to see.

## Tasks and saved views

`/sales/tasks` is the shared work queue for Leads, Deals, Companies and
contacts. A task can be assigned to a teammate or queue, carry priority,
reminder and outcome fields, and recur daily, weekly or monthly. Completing a
recurring task creates the next occurrence once; retries do not duplicate it.

Saved list or Kanban filters are managed under `/sales/saved-views`. Personal
views remain private to their creator; shared views are still tenant-scoped.

## Versioned quotes and payments

`GET /sales/quotes` returns the quote register and supports `status`,
`approval_status`, `payment`, `deal_id`, `page` and `per_page`. Use
`payment=requested` for the Payment Links queue or `payment=outstanding` for
accepted quotes with a remaining balance. `POST /sales/quotes` creates the draft and all line-item
snapshots in one transaction. Send one to 200 items; the server calculates
subtotal, discounts, tax and total from exact two-decimal amounts.

References to a deal, contact, company or product are accepted only when they
belong to the same account. A product line must use the product currency. A
primary quote must name a deal, and making it primary clears the previous
primary quote for that deal atomically.

<Note>
  Quote lifecycle transitions are explicit. Use
  `PATCH /sales/quotes/{quoteId}/lifecycle` to record sent, viewed, accepted,
  rejected or expired state, and use `/revisions` to create a versioned draft.
  Acceptance requires an idempotency key. This route preserves payment and
  approval state; it cannot be used to overwrite either. An administrator
  decides a pending high-discount approval through
  `PATCH /sales/quotes/{quoteId}/approval` with `approved` or `rejected` plus a
  required audit reason. A payment request records an amount and provider state,
  but HueChat never infers that the provider accepted or settled it.
</Note>

### Attach a quotation PDF

Each quote version can hold one signed or branded PDF. Upload or replace it as
multipart form data, view it inline, download it as an attachment, or remove
it without deleting the quote:

```bash theme={null}
curl -X POST \
  "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/sales/quotes/$QUOTE_ID/pdf" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  -F "file=@quotation.pdf;type=application/pdf"
```

Only a real PDF up to 20 MB is accepted. Replacement is concurrency-safe and
the prior object is cleaned up only after the new tenant-scoped reference is
stored. `GET .../pdf` views it, `GET .../pdf?download=1` downloads it, and
`DELETE .../pdf` removes it. All three actions use the quote's normal
`deals:read` or `deals:write` permission and plan checks.

### Share a private quote

Create or rotate the customer link with
`POST /sales/quotes/{quoteId}/share-link`. The route requires `deals:write`,
returns the raw link only once, and moves a draft quote to `sent`. A pending or
rejected approval, an expired quote, or a superseded version cannot be shared.

The customer credential is stored only as a hash. It is placed in the link's
URL fragment, so it is not sent in HTTP request URLs or access logs, and it
expires on the quote's own expiry date or after 30 days, whichever comes first.
Rotating the link immediately invalidates the earlier one. Opening the link
records the first real `viewed` lifecycle event and exposes only the quotation,
its line items, terms and attached PDF—not the account, API key or other CRM
records.

## Forecasts, reports and settings

`GET /sales/forecast` returns pipeline categories, owner rollups, quotas and
snapshots. Quotas are written through `/sales/forecast/quotas`; snapshots are
created through `/sales/forecast/snapshots`. `GET /sales/reports` supplies the
Sales reporting workspace with conversion, pipeline, revenue and activity
measures without mixing currencies.

`/sales/settings` contains identity and duplicate policy, automatic assignment
and stage rules. API writes require account-level permission. Journey actions
can assign owners or queues, update Leads and Deals, move Deal stages, create
tasks, quotes or payment requests, send approved WhatsApp/form steps, notify a
manager and emit signed events. The same entitlement and tenant checks apply
whether an action starts in the UI or a Customer journey.

The interactive API reference contains every request field, response schema,
status code and filter for this published Sales CRM API surface.
