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

# Contacts API

> Internal reference for the Contacts API — CRUD, bulk operations, merge candidates, timeline, and customer-360 view.

> **Internal documentation for developers.** This page covers the Contacts API endpoints, bulk operations, merge detection, and the customer-360 data model. Not intended for end users.

***

## Route Map

| Method   | Endpoint                         | Handler File                         | Description                |
| -------- | -------------------------------- | ------------------------------------ | -------------------------- |
| `GET`    | `/api/contacts`                  | `contacts/route.ts`                  | List contacts with filters |
| `POST`   | `/api/contacts`                  | `contacts/route.ts`                  | Create a contact           |
| `GET`    | `/api/contacts/[contactId]`      | `contacts/[contactId]/route.ts`      | Get contact by ID          |
| `PATCH`  | `/api/contacts/[contactId]`      | `contacts/[contactId]/route.ts`      | Update contact             |
| `DELETE` | `/api/contacts/[contactId]`      | `contacts/[contactId]/route.ts`      | Delete contact             |
| `POST`   | `/api/contacts/bulk-delete`      | `contacts/bulk-delete/route.ts`      | Delete multiple contacts   |
| `GET`    | `/api/contacts/merge-candidates` | `contacts/merge-candidates/route.ts` | Find duplicate candidates  |

***

## Auth Pattern

```ts theme={null}
withPermissionRoute(req, {
  anyPermission: ['contacts.read'],  // for GET
  // or
  anyPermission: ['contacts.write'], // for mutations
  authMode: 'lightweight',           // for reads
}, handler)
```

All queries must scope to `req.session.organization.id`.

***

## Contact Object Schema

```ts theme={null}
interface Contact {
  id: string;
  organizationId: string;
  name: string | null;
  email: string | null;
  phone: string | null;
  whatsappPhone: string | null;
  externalId: string | null;
  avatarUrl: string | null;
  metadata: Record<string, unknown>;
  tags: string[];
  createdAt: string;
  updatedAt: string;
}
```

***

## List Contacts (Filters)

```ts theme={null}
// GET /api/contacts
// Query params:
// - search: string (name, email, phone, externalId)
// - page, limit: pagination
// - tag: string (filter by tag)
// - channel: string (contacts with conversations in this channel)
// - dateFrom, dateTo: ISO 8601
```

***

## Bulk Delete

```ts theme={null}
// POST /api/contacts/bulk-delete
// Body: { contactIds: string[] }
// Deletes all contacts + cascades to conversations
// Max batch: 100 IDs per request (to prevent timeout)
```

***

## Merge Candidates

The `merge-candidates` endpoint runs a fuzzy-match algorithm to identify potential duplicate contacts:

```ts theme={null}
// GET /api/contacts/merge-candidates?contactId=xxx
// Returns contacts that match by email, phone, or name similarity
// Uses: Prisma + application-level fuzzy matching
```

**Merge strategy:**

* Primary contact keeps its ID
* Secondary contact's conversations are re-attributed
* Secondary contact is deleted
* **No undo** — warn users before merge

***

## Customer-360 View

The individual contact page aggregates data from multiple sources:

| Source                              | Data                          |
| ----------------------------------- | ----------------------------- |
| `Contact` table                     | Core profile                  |
| `Conversation` table                | All conversations             |
| `Message` table (via conversations) | Message history               |
| `metadata` JSON                     | Custom fields                 |
| Integration webhooks                | CRM/external data (if synced) |

***

## Prisma Indexes Used

| Table     | Index                        | Used by                   |
| --------- | ---------------------------- | ------------------------- |
| `Contact` | `organizationId`             | All list queries          |
| `Contact` | `organizationId, email`      | Merge candidate detection |
| `Contact` | `organizationId, phone`      | Merge candidate detection |
| `Contact` | `organizationId, externalId` | External system sync      |

***

## Known Issues / Gotchas

* **Bulk delete cascade:** Deleting contacts deletes their conversations. This is a destructive operation. Always confirm with the user before calling.
* **Phone normalization:** Phone numbers should be stored in E.164 format (`+5511999990000`). Inconsistent formats cause merge candidates to miss duplicates.
* **`externalId`** is used for CRM sync (e.g., HubSpot, Salesforce). When syncing from external systems, always use `upsert` on `organizationId + externalId`.
* **Merge candidates** uses application-level matching (not DB-level). For large contact lists (100k+), this can be slow. Consider adding a background job for large orgs.
