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

# Add, List, and Delete Contacts — Zexa Contacts API

> POST /contacts to add recipients, GET /contacts to list and search them, DELETE /contacts/{id} to remove. Build your recipient database via API.

The Contacts API lets you build and maintain your recipient database programmatically. Add contacts individually, organise them into contact lists for use with the Campaigns API, query and filter your contact database, and remove contacts when they are no longer needed. All phone numbers must be in E.164 format to ensure reliable delivery across all channels.

## Add a Contact

```text theme={null}
POST https://api.zexa.ao/v1/contacts
```

### Request Parameters

<ParamField body="name" type="string" required>
  The contact's full name (e.g. `Maria Santos`).
</ParamField>

<ParamField body="phone" type="string" required>
  The contact's phone number in E.164 format. Must include the `+` prefix followed by the country code and subscriber number (e.g. `+244912345678` for an Angolan number).
</ParamField>

<ParamField body="email" type="string">
  The contact's email address. Required if you plan to target this contact via the `email` channel.
</ParamField>

<ParamField body="lists" type="array">
  An array of contact list IDs to assign this contact to upon creation (e.g. `["lst_abc123", "lst_def456"]`). You can also add contacts to lists later via the dashboard.
</ParamField>

### Request Example

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.zexa.ao/v1/contacts \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Maria Santos",
      "phone": "+244912345678",
      "email": "maria@example.com",
      "lists": ["lst_abc123"]
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.zexa.ao/v1/contacts",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json",
      },
      json={
          "name": "Maria Santos",
          "phone": "+244912345678",
          "email": "maria@example.com",
          "lists": ["lst_abc123"],
      },
  )

  print(response.json())
  ```
</CodeGroup>

### Response Fields

<ResponseField name="id" type="string">
  The unique contact identifier, prefixed with `cnt_` (e.g. `cnt_def456`).
</ResponseField>

<ResponseField name="name" type="string">
  The contact's full name as provided in the request.
</ResponseField>

<ResponseField name="phone" type="string">
  The contact's phone number in E.164 format.
</ResponseField>

<ResponseField name="email" type="string">
  The contact's email address, if provided.
</ResponseField>

<ResponseField name="lists" type="array">
  An array of contact list IDs the contact belongs to.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 UTC timestamp of when the contact was created.
</ResponseField>

**Example response (`201 Created`):**

```json theme={null}
{
  "id": "cnt_def456",
  "name": "Maria Santos",
  "phone": "+244912345678",
  "email": "maria@example.com",
  "lists": ["lst_abc123"],
  "created_at": "2026-06-24T10:00:00Z"
}
```

<Tip>
  Phone numbers must be in E.164 format. Use the `+` prefix followed by the country code and local number — for example, `+244` for Angola, `+351` for Portugal, and `+1` for the United States. Submissions in local formats (e.g. `0912345678`) will be rejected with a `422` error.
</Tip>

### Error Scenarios

| Status | Error              | Description                                                    |
| ------ | ------------------ | -------------------------------------------------------------- |
| `400`  | `invalid_request`  | Missing required fields or malformed JSON body                 |
| `401`  | `unauthorized`     | API key is missing or invalid                                  |
| `409`  | `conflict`         | A contact with this phone number already exists on the account |
| `422`  | `validation_error` | `phone` is not in E.164 format, or `email` is malformed        |

***

## List Contacts

Retrieve a paginated list of contacts on your account. Optionally filter by contact list.

```text theme={null}
GET https://api.zexa.ao/v1/contacts
```

### Query Parameters

<ParamField query="list_id" type="string">
  Filter contacts by a specific contact list ID. Returns only contacts belonging to the specified list.
</ParamField>

<ParamField query="page" type="integer">
  Page number (default: `1`).
</ParamField>

<ParamField query="per_page" type="integer">
  Results per page (default: `50`, max: `100`).
</ParamField>

### Request Example

<CodeGroup>
  ```bash curl theme={null}
  curl "https://api.zexa.ao/v1/contacts?list_id=lst_abc123&page=1&per_page=25" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.zexa.ao/v1/contacts",
      params={"list_id": "lst_abc123", "page": 1, "per_page": 25},
      headers={"Authorization": "Bearer YOUR_API_KEY"},
  )

  print(response.json())
  ```
</CodeGroup>

### Response Fields

<ResponseField name="data" type="array">
  An array of contact objects matching the query.

  <Expandable title="contact object fields">
    <ResponseField name="id" type="string">
      Unique contact identifier.
    </ResponseField>

    <ResponseField name="name" type="string">
      The contact's full name.
    </ResponseField>

    <ResponseField name="phone" type="string">
      The contact's phone number in E.164 format.
    </ResponseField>

    <ResponseField name="email" type="string">
      The contact's email address, if set.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO 8601 UTC timestamp of when the contact was created.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object">
  Pagination metadata.

  <Expandable title="meta fields">
    <ResponseField name="total" type="integer">
      Total number of contacts matching the query.
    </ResponseField>

    <ResponseField name="page" type="integer">
      Current page number.
    </ResponseField>

    <ResponseField name="per_page" type="integer">
      Number of results per page.
    </ResponseField>
  </Expandable>
</ResponseField>

**Example response:**

```json theme={null}
{
  "data": [
    {
      "id": "cnt_def456",
      "name": "Maria Santos",
      "phone": "+244912345678",
      "email": "maria@example.com",
      "created_at": "2026-06-24T10:00:00Z"
    },
    {
      "id": "cnt_ghi789",
      "name": "João Ferreira",
      "phone": "+244923456789",
      "email": "joao@example.com",
      "created_at": "2026-06-23T14:30:00Z"
    }
  ],
  "meta": {
    "total": 128,
    "page": 1,
    "per_page": 25
  }
}
```

***

## Delete a Contact

Permanently remove a contact from your account by their ID:

```text theme={null}
DELETE https://api.zexa.ao/v1/contacts/{id}
```

<CodeGroup>
  ```bash curl theme={null}
  curl -X DELETE https://api.zexa.ao/v1/contacts/cnt_def456 \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  response = requests.delete(
      "https://api.zexa.ao/v1/contacts/cnt_def456",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
  )

  print(response.status_code)  # 204 on success
  ```
</CodeGroup>

A successful deletion returns `204 No Content` with an empty response body.

### Error Scenarios

| Status | Error          | Description                                        |
| ------ | -------------- | -------------------------------------------------- |
| `401`  | `unauthorized` | API key is missing or invalid                      |
| `404`  | `not_found`    | No contact with the given ID exists on the account |

<Note>
  Deleting a contact removes them from **all** contact lists they belong to and permanently excludes them from future campaigns. This action cannot be undone. If you simply want to stop targeting a contact in a specific campaign, remove them from the relevant contact list instead.
</Note>
