# WAAPI — Contact Management API

Manage your contacts and groups programmatically using your vendor API key. Supports full CRUD for contacts, bulk import (up to 50,000 at once), and contact group management.

---

## Base URLs

| Base URL | Description |
|----------|-------------|
| `https://your-api.com/api` | **Backend API** — direct connection to the Node.js backend |
| `https://your-domain.com/api` | **Frontend Proxy** — Next.js server-side proxy (same body format, same token) |

Both URLs accept identical request bodies and return identical responses.

---

## Authentication

Every request must include a valid Bearer token in the `Authorization` header.

```
Authorization: Bearer <your_api_token>
```

**How to get the token:**
1. Go to **WhatsApp Settings → API & Webhook → API Keys**
2. Click **Create Key** and enable `read_contacts` and/or `write_contacts` permissions
3. Copy the token — it is only displayed **once** at creation time

**cURL example:**
```bash
curl https://your-api.com/api/external/contacts/{encodedPhoneId} \
  -H "Authorization: Bearer <your_api_token>"
```

---

## Permissions

| Permission | Grants Access To |
|------------|-----------------|
| `read_contacts` | GET (list, get single, list groups, get group) |
| `write_contacts` | POST, PUT, DELETE (create, update, delete contacts & groups; bulk import) |

A token may hold both permissions simultaneously.

---

## URL Parameter

All contact endpoints include `:encodedPhoneId` — the **Base64-encoded** WhatsApp phone number ID for the connected account. This value is shown on the **API Keys** settings page.

```
/api/external/contacts/{encodedPhoneId}
```

---

## Endpoints Overview

| Method | Path | Permission | Description |
|--------|------|------------|-------------|
| `GET` | `/api/external/contacts/{phoneId}` | `read_contacts` | List contacts |
| `POST` | `/api/external/contacts/{phoneId}` | `write_contacts` | Create a contact |
| `GET` | `/api/external/contacts/{phoneId}/{id}` | `read_contacts` | Get a contact |
| `PUT` | `/api/external/contacts/{phoneId}/{id}` | `write_contacts` | Update a contact |
| `DELETE` | `/api/external/contacts/{phoneId}/{id}` | `write_contacts` | Delete a contact |
| `POST` | `/api/external/contacts/{phoneId}/bulk` | `write_contacts` | Bulk import contacts |
| `GET` | `/api/external/contacts/{phoneId}/groups` | `read_contacts` | List groups |
| `POST` | `/api/external/contacts/{phoneId}/groups` | `write_contacts` | Create a group |
| `GET` | `/api/external/contacts/{phoneId}/groups/{groupId}` | `read_contacts` | Get a group |
| `PUT` | `/api/external/contacts/{phoneId}/groups/{groupId}` | `write_contacts` | Update a group |
| `DELETE` | `/api/external/contacts/{phoneId}/groups/{groupId}` | `write_contacts` | Delete a group |
| `POST` | `/api/external/contacts/{phoneId}/groups/{groupId}/add` | `write_contacts` | Add contacts to group |
| `POST` | `/api/external/contacts/{phoneId}/groups/{groupId}/remove` | `write_contacts` | Remove contacts from group |

---

## Contact Object

```json
{
  "_id": "64f1a2b3c4d5e6f7a8b9c0d1",
  "name": "John Doe",
  "phone": "919876543210",
  "email": "john@example.com",
  "language_code": "en",
  "tags": ["vip", "newsletter"],
  "notes": "Preferred contact via WhatsApp.",
  "opt_out": false,
  "opt_out_marketing": false,
  "reply_bot_enabled": true,
  "custom_fields": {
    "city": "Mumbai",
    "loyalty_tier": "gold"
  },
  "createdAt": "2024-01-15T10:30:00.000Z",
  "updatedAt": "2024-01-20T14:22:00.000Z"
}
```

---

## 1. List Contacts

```http
GET /api/external/contacts/{encodedPhoneId}
Authorization: Bearer <token>
```

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `search` | string | Search by name or phone |
| `tag` | string | Filter by a single tag |
| `opt_out` | boolean | Filter opted-out contacts |
| `page` | number | Page number (default: 1) |
| `limit` | number | Results per page (default: 20, max: 100) |

### Example Request

```bash
curl "https://your-api.com/api/external/contacts/{phoneId}?search=john&tag=vip&page=1&limit=20" \
  -H "Authorization: Bearer <token>"
```

### Response

```json
{
  "success": true,
  "data": {
    "contacts": [
      {
        "_id": "64f1a2b3c4d5e6f7a8b9c0d1",
        "name": "John Doe",
        "phone": "919876543210",
        "email": "john@example.com",
        "tags": ["vip"],
        "opt_out": false,
        "createdAt": "2024-01-15T10:30:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "totalPages": 1
    }
  },
  "message": "Contacts retrieved successfully."
}
```

---

## 2. Get Contact

```http
GET /api/external/contacts/{encodedPhoneId}/{contactId}
Authorization: Bearer <token>
```

### Response

```json
{
  "success": true,
  "data": {
    "contact": { ...contactObject }
  },
  "message": "Contact retrieved successfully."
}
```

---

## 3. Create Contact

```http
POST /api/external/contacts/{encodedPhoneId}
Authorization: Bearer <token>
Content-Type: application/json
```

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | ✅ | Full name |
| `phone` | string | ✅ | Phone in E.164 format (e.g. `+919876543210`) |
| `email` | string | — | Email address |
| `language_code` | string | — | e.g. `en`, `hi`, `ar` |
| `tags` | string[] | — | Array of label strings |
| `notes` | string | — | Free-text notes (max 2000 chars) |
| `opt_out` | boolean | — | Block all outbound messages (default: `false`) |
| `opt_out_marketing` | boolean | — | Exclude from campaigns (default: `false`) |
| `reply_bot_enabled` | boolean | — | Allow chatbot to reply (default: `true`) |
| `custom_fields` | object | — | Key-value map of custom field values |

### Example Request

```bash
curl -X POST https://your-api.com/api/external/contacts/{phoneId} \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Smith",
    "phone": "+919876543210",
    "email": "jane@example.com",
    "language_code": "en",
    "tags": ["customer", "vip"],
    "notes": "High-value customer",
    "reply_bot_enabled": true,
    "custom_fields": {
      "city": "Delhi",
      "loyalty_tier": "platinum"
    }
  }'
```

### Response

```json
{
  "success": true,
  "data": {
    "contact": { ...contactObject }
  },
  "message": "Contact created successfully."
}
```

**Status:** `201 Created`

---

## 4. Update Contact

```http
PUT /api/external/contacts/{encodedPhoneId}/{contactId}
Authorization: Bearer <token>
Content-Type: application/json
```

Send only the fields you want to update.

### Example Request

```bash
curl -X PUT https://your-api.com/api/external/contacts/{phoneId}/{contactId} \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": ["vip", "renewed"],
    "custom_fields": {
      "loyalty_tier": "gold"
    }
  }'
```

### Response

```json
{
  "success": true,
  "data": {
    "contact": { ...updatedContactObject }
  },
  "message": "Contact updated successfully."
}
```

---

## 5. Delete Contact

```http
DELETE /api/external/contacts/{encodedPhoneId}/{contactId}
Authorization: Bearer <token>
```

Deletes the contact and removes them from all groups.

### Response

```json
{
  "success": true,
  "data": null,
  "message": "Contact deleted successfully."
}
```

---

## 6. Bulk Import Contacts

Import or update up to **50,000** contacts in a single request. Contacts are **upserted by phone number** — existing contacts are updated, new ones are created.

```http
POST /api/external/contacts/{encodedPhoneId}/bulk
Authorization: Bearer <token>
Content-Type: application/json
```

### Request Body

```json
{
  "contacts": [
    {
      "name": "John Doe",
      "phone": "+919876543210",
      "email": "john@example.com",
      "language_code": "en",
      "tags": ["newsletter"],
      "groups": ["VIP Customers"],
      "custom_fields": {
        "city": "Mumbai"
      }
    },
    {
      "name": "Jane Smith",
      "phone": "+919812345678"
    }
  ]
}
```

### Contact Fields for Bulk Import

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Full name (or use `first_name` + `last_name`) |
| `first_name` | string | Combined with `last_name` |
| `last_name` | string | Combined with `first_name` |
| `phone` | string | ✅ Required. E.164 format |
| `email` | string | Email address |
| `language_code` | string | e.g. `en`, `hi` |
| `tags` | string[] | Array of tag strings |
| `groups` | string[] | Group names — auto-created if not existing |
| `custom_fields` | object | Key-value custom field values |

### Response

```json
{
  "success": true,
  "data": {
    "created": 150,
    "updated": 42,
    "skipped": 3,
    "errors": []
  },
  "message": "Bulk import complete."
}
```

---

## 7. List Groups

```http
GET /api/external/contacts/{encodedPhoneId}/groups
Authorization: Bearer <token>
```

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `search` | string | Search by group name |
| `page` | number | Page number |
| `limit` | number | Results per page |

### Response

```json
{
  "success": true,
  "data": {
    "groups": [
      {
        "_id": "64f1a2b3c4d5e6f7a8b9c0d2",
        "name": "VIP Customers",
        "description": "High-value customers",
        "contacts": ["id1", "id2"],
        "createdAt": "2024-01-10T08:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
  },
  "message": "Groups retrieved successfully."
}
```

---

## 8. Create Group

```http
POST /api/external/contacts/{encodedPhoneId}/groups
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "name": "Newsletter Subscribers",
  "description": "Contacts subscribed to the weekly newsletter"
}
```

### Response

```json
{
  "success": true,
  "data": {
    "group": {
      "_id": "64f1a2b3c4d5e6f7a8b9c0d3",
      "name": "Newsletter Subscribers",
      "description": "Contacts subscribed to the weekly newsletter",
      "contacts": [],
      "createdAt": "2024-01-20T09:00:00.000Z"
    }
  },
  "message": "Group created successfully."
}
```

**Status:** `201 Created`

---

## 9. Get Group

```http
GET /api/external/contacts/{encodedPhoneId}/groups/{groupId}
Authorization: Bearer <token>
```

Returns the group with its full contact list (populated).

---

## 10. Update Group

```http
PUT /api/external/contacts/{encodedPhoneId}/groups/{groupId}
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "name": "Premium Subscribers",
  "description": "Updated description"
}
```

---

## 11. Delete Group

```http
DELETE /api/external/contacts/{encodedPhoneId}/groups/{groupId}
Authorization: Bearer <token>
```

Deletes the group only — contacts are NOT deleted.

---

## 12. Add Contacts to Group

```http
POST /api/external/contacts/{encodedPhoneId}/groups/{groupId}/add
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "contact_ids": ["64f1a2b3c4d5e6f7a8b9c0d1", "64f1a2b3c4d5e6f7a8b9c0d4"]
}
```

---

## 13. Remove Contacts from Group

```http
POST /api/external/contacts/{encodedPhoneId}/groups/{groupId}/remove
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "contact_ids": ["64f1a2b3c4d5e6f7a8b9c0d1"]
}
```

---

## Error Responses

All errors follow the standard format:

```json
{
  "success": false,
  "message": "Human-readable error description",
  "error": "ERROR_CODE"
}
```

| HTTP Status | Meaning |
|-------------|---------|
| `400` | Bad Request — missing required fields or invalid data |
| `401` | Unauthorized — missing, invalid, or expired Bearer token |
| `403` | Forbidden — token does not have required permission |
| `404` | Not Found — contact or group ID does not exist |
| `409` | Conflict — phone number already exists (on create) |
| `500` | Internal Server Error |

### Common Error Examples

**Missing permission:**
```json
{
  "success": false,
  "message": "Permission denied. This token does not have 'write_contacts' permission."
}
```

**Duplicate phone:**
```json
{
  "success": false,
  "message": "A contact with this phone number already exists."
}
```

**Contact not found:**
```json
{
  "success": false,
  "message": "Contact not found."
}
```

---

## Rate Limits

| Route Type | Limit |
|------------|-------|
| General API | 100 requests / 15 minutes |

---

## Custom Fields

Custom fields must be defined first in the dashboard (**Contacts → Fields tab**) before values can be stored. The `custom_fields` object uses the **field_name** (internal key) as the property name.

**Example:** If you created a field with `field_name: city`, send:
```json
{
  "custom_fields": {
    "city": "Mumbai",
    "loyalty_tier": "gold"
  }
}
```

Unknown field names are silently ignored by the backend.
