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

# Contacts and Customer CDP

> Create, search, update, merge and read unified customer profiles with account-scoped API keys.

The Contacts API is the system-to-system surface for HueChat customer records.
It uses the same validation, identity locks and event outbox as the dashboard,
while every request remains restricted to the account that issued the API key.

## Before you start

Create a [scoped API key](/api-keys) with `contacts:read` for list, search and
contact calls. Unified profiles, activity, timelines and advanced customer
views also require `deals:read` because they contain deal and task data. Add
`contacts:write` for create, update and merge. A merge also requires the key's
current owner to be an account administrator.

<Warning>
  Use the account id that issued the key. A key from another account receives
  `403`; contact IDs, identifiers and merge aliases from other accounts are never
  resolved.
</Warning>

## Create a contact

Use `identifier` for the stable customer ID from your ERP, CRM or commerce
system. HueChat enforces its uniqueness inside one account.

```bash theme={null}
curl -X POST "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/contacts" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "erp-customer-8842",
    "name": "Sara Alotaibi",
    "email": "sara@example.com",
    "phone_number": "0501234567",
    "phone_country": "SA",
    "additional_attributes": {
      "company_name": "Example Trading",
      "website": "https://example.com",
      "preferred_language": "ar"
    },
    "custom_attributes": {
      "customer_tier": "gold"
    }
  }'
```

Phone numbers are normalized to E.164. Email, identifier and phone conflicts
are rejected instead of silently joining two people. Unknown JSON fields and
oversized bodies are rejected. Contact creation and its `contact.created`
event commit together.

## List and search

List with `GET /api/v2/accounts/{accountId}/contacts`. Use `page`, `per_page`
(maximum 100) and `sort`. Add `q`, or call `/contacts/search?q=…`, to search
name, email, normalized phone, identifier, company, website and domain. Sorting
applies to ordinary lists and cannot be combined with `q`.

```bash theme={null}
curl --get "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/contacts/search" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  --data-urlencode "q=erp-customer-8842" \
  --data-urlencode "per_page=30"
```

Search is fuzzy and ranked; use the returned numeric `id` for later calls.

## Read and update

`GET /contacts/{contactId}` returns the editable record and its `updated_at`
version. `PATCH /contacts/{contactId}` changes only supplied fields. Attribute
objects are merged; an explicit empty email or phone clears that field.
Provider-owned inbox identities are not retargeted by a profile update.
The contact-only create/get surface neither accepts nor reveals inbox
associations; use CDP profile routes for membership-filtered source context.

If the requested ID was removed by a reviewed merge, HueChat resolves its
account-local alias to the kept profile and returns `merged_from_contact_id`.

## Merge duplicate customers

Read both records immediately before merging and send both `updated_at`
values. Choose the kept record as `base_contact_id` and the duplicate as
`mergee_contact_id`.

```bash theme={null}
curl -X POST "https://app.huechat.ai/api/v2/accounts/$ACCOUNT_ID/contacts/merge" \
  -H "Authorization: Bearer $HUECHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base_contact_id": 1042,
    "mergee_contact_id": 1088,
    "confirmed": true,
    "base_updated_at": "2026-09-12T12:00:00Z",
    "mergee_updated_at": "2026-09-12T12:01:00Z",
    "field_choices": {
      "name": "base",
      "email": "mergee",
      "company_name": "mergee"
    }
  }'
```

Each field choice is `base` or `mergee`. Supported keys are `name`, `email`,
`phone_number`, `identifier`, `company_name`, `location`, `city`, `country`,
`country_code`, `notes`, `website` and `domain`.

The merge runs in one transaction and preserves conversations, consent
evidence, labels, attachments, deals, appointments, journeys and memberships.
It refuses stale versions, active delivery work and conflicting enrollments.
Retrying the same completed merge returns the kept ID with `replayed: true`.

## Read the unified CDP profile

Accounts with Customer CDP enabled and both `contacts:read` and `deals:read`
can call:

* `GET /contacts/{contactId}/profile` for the customer, sources, consent
  history, activity, deals, memberships, labels and first timeline page.
* `GET /contacts/{contactId}/timeline?cursor=…` for cursor-paginated messages,
  events, deals and tasks.

Private-inbox rows remain filtered by the API key owner's current membership.
A disabled CDP plan returns `402` without weakening base contact access.

## Synchronize consent evidence

An administrator can call `POST /contacts/{contactId}/consent` with
`channel`, `purpose`, `status`, human-readable `evidence` and the current
`expected_version`. Evidence is bound to the contact's current email or phone;
it does not grant permission to a future address. A stale version returns
`409`. Revoking WhatsApp marketing consent also updates HueChat's durable
broadcast suppression.

## Advanced Customer CDP API

The full CDP surface uses
`/api/v2/accounts/{accountId}/cdp`. Every endpoint re-checks that the API key
owner is still a member of the account. Feature-specific routes return `402`
when Customer CDP, Audiences, Journeys or Membership is not enabled on the
account plan.

### Customer directory and configuration

| Method | Route                                 | Scope                          | Purpose                                                                                    |
| ------ | ------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
| `GET`  | `/cdp/access`                         | `contacts:read`                | Resolve enabled CDP features and management access                                         |
| `GET`  | `/cdp/customers`                      | `contacts:read` + `deals:read` | Filter customers by view, IDs, source, label, company, location and contact-field presence |
| `GET`  | `/cdp/customers/{contactId}/activity` | `contacts:read` + `deals:read` | Read the stable `before_id` activity feed                                                  |
| `GET`  | `/cdp/preferences`                    | `contacts:read`                | Read enabled industry modules                                                              |
| `PUT`  | `/cdp/preferences`                    | `contacts:write`               | Save modules using the current `revision`                                                  |

Preference updates require an administrator. Use the simpler `/contacts`
routes for ordinary synchronization and `/cdp/customers` when you need CDP
views and aggregated customer summaries.

### Audiences

Audiences require the Audiences plan feature. Dynamic audiences support up to
12 validated rules; static audiences support up to 5,000 accessible contact
IDs. A static ID from another account or a private inbox the key owner cannot
access is rejected.

| Method          | Route                                   | Scope                             | Purpose                                          |
| --------------- | --------------------------------------- | --------------------------------- | ------------------------------------------------ |
| `GET`, `POST`   | `/cdp/audiences`                        | `contacts:read`, `contacts:write` | List or create audiences                         |
| `PUT`, `DELETE` | `/cdp/audiences/{audienceId}`           | `contacts:write`                  | Update or delete with the current `revision`     |
| `POST`          | `/cdp/audiences/preview`                | `contacts:read`                   | Preview unsaved conditions without changing data |
| `GET`           | `/cdp/audiences/{audienceId}/customers` | `contacts:read`                   | Recalculate customers from saved rules           |

Create, update and delete require an administrator. Rule fields and operators
are enumerated in the interactive API schema.

### Customer journeys

Journeys require `workflows:read` or `workflows:write` plus the Journeys plan
feature. Draft creation and preview do not send anything. Publishing activates
future native-event enrollment and can cause real WhatsApp sends after HueChat
re-checks consent, suppression, inbox access, the approved template and
provider readiness.

| Method        | Route                                            | Purpose                                                                 |
| ------------- | ------------------------------------------------ | ----------------------------------------------------------------------- |
| `GET`, `POST` | `/cdp/journeys`                                  | List journeys or create a draft                                         |
| `POST`        | `/cdp/journeys/preview`                          | Evaluate a definition against one current customer without side effects |
| `GET`         | `/cdp/journeys/options`                          | List account-scoped deal-stage options                                  |
| `GET`, `PUT`  | `/cdp/journeys/{workflowId}`                     | Read or update a versioned draft                                        |
| `POST`        | `/cdp/journeys/{workflowId}/publish`             | Validate and publish the current version                                |
| `GET`         | `/cdp/journeys/{workflowId}/runs`                | Read visible run and action history                                     |
| `POST`        | `/cdp/journeys/{workflowId}/runs/{runId}/cancel` | Stop a run that is not complete or executing an effect                  |
| `POST`        | `/cdp/journeys/{workflowId}/runs/{runId}/review` | Record review of uncertain delivery without replaying it                |

All journey mutations require `workflows:write` and an administrator.

### Membership programs

Membership routes require the Membership plan feature. Reads use
`contacts:read`; normal writes use `contacts:write` and an administrator.

| Method        | Route                                                                 | Purpose                                                        |
| ------------- | --------------------------------------------------------------------- | -------------------------------------------------------------- |
| `GET`, `POST` | `/cdp/membership/programs`                                            | List or create programs                                        |
| `GET`         | `/cdp/membership/inboxes`                                             | List accessible membership-capable inboxes without credentials |
| `GET`, `PUT`  | `/cdp/membership/programs/{programId}`                                | Read or version-update a program                               |
| `GET`         | `/cdp/membership/programs/{programId}/readiness`                      | Check inbox, provider and synchronized-template readiness      |
| `GET`         | `/cdp/membership/programs/{programId}/activity`                       | Read cursor-paginated program activity                         |
| `GET`, `POST` | `/cdp/membership/programs/{programId}/members`                        | List members or enroll a contact with terms evidence           |
| `POST`        | `/cdp/membership/programs/{programId}/invites`                        | Create a short-lived WhatsApp-verified enrollment URL          |
| `GET`         | `/cdp/membership/members`                                             | List accessible memberships                                    |
| `GET`         | `/cdp/membership/members/{membershipId}`                              | Read membership, ledger, points, benefits and receipts         |
| `POST`        | `/cdp/membership/members/{membershipId}/activity`                     | Apply a replay-safe, versioned membership action               |
| `POST`        | `/cdp/membership/members/{membershipId}/receipts/{receiptId}/fulfill` | Record replay-safe fulfillment evidence                        |
| `POST`        | `/cdp/membership/members/{membershipId}/member-link`                  | Create a short-lived WhatsApp-verified member URL              |

External commerce systems award points with
`POST /cdp/membership/members/{membershipId}/events`. Give that integration a
dedicated key with only `membership:events`; the key owner must still be a
current administrator. `provider + event_id` is account-wide and idempotent,
so a conflicting replay returns `409` instead of awarding points twice.

<Warning>
  Keep separate least-privilege keys for contacts, journeys and membership
  events. HueChat hashes keys at rest, enforces expiry, optional IP allowlists and
  per-key rate limits, and rejects an account ID that does not own the key.
</Warning>

## Deliberately not exposed

Hard delete and bulk delete are not part of this integration API. Customer
history spans conversations, consent evidence and CDP activity; use HueChat's
authenticated privacy workflow when a record must be erased. The dashboard's
CDP onboarding-guide state is also intentionally not an integration endpoint.

See the [Contacts API reference](/api-reference/overview) for request fields
and response schemas.
