# Mailing Lists API — Reference

Reference for third-party developers and LLM coding assistants integrating with the **mailing-list management** endpoint — the contact lists used by phone campaigns, email campaigns, IVR whitelists and broadcast tools.

For the full HTML reference (with diagrams and Hebrew explanations) see [mailingListsApiDocs.html](mailingListsApiDocs.html).

---

## Base URL

```
https://api.tlivr.com/mailingListsApi.php
```

All requests **MUST be sent over POST**. Parameters can be sent as `application/x-www-form-urlencoded`, `application/json`, or `multipart/form-data` (when uploading a file).

---

## Authentication

Every request must include an API key.

| Location              | Example                              |
|-----------------------|--------------------------------------|
| POST body (form)      | `apiKey=YOUR_API_KEY&...`            |
| POST body (JSON)      | `{ "apiKey": "YOUR_API_KEY", ... }`  |

The API key identifies the tenant account that owns the mailing list. Treat it like a password — never embed in browser code or public repos.

---

## Overview

A **mailing list** is a named collection of contacts. Each contact entry holds:

- `phone` — the primary phone number.
- `name` — the contact's display name. For backward compatibility you may also send `firstName` / `lastName`; they are combined into the single `name`.
- `email` — optional e-mail address.
- `status` — `0` = active, `1` = blocked. Blocked contacts are excluded from outgoing campaigns.

Mailing lists are addressed by their numeric **list ID** (`mailingList`).

> **These are the same contacts you see in the system.** Every action here reads and writes the exact contacts shown for that list in the account interface — there is no separate copy. All operations are scoped to the account that owns the `apiKey`: you only ever see and change your own account's lists and contacts. The `mailingList` ID must be one of your account's own list IDs (a list ID that isn't yours simply returns nothing rather than another account's data).

The API has two levels:

**The list itself**

- **Get all lists** in the account, with contact counts — `mailingLists`.
- **Create a list**, or rename an existing one — `mailingListEdit`.
- **Delete a list** together with all its contacts — `mailingListDelete`.

**The contacts inside a list**

- **Add / update / replace / delete** contacts in bulk — `uplodePhones`.
- **List** the contacts in a list (with search and pagination) — `phonesList`.
- **Add or update a single contact** — `phoneEdit`.
- **Delete contacts** by phone number — `phonesDelete`.
- **Block / unblock contacts** — `setStatus`.

Every contact-level action requires a `mailingList` ID of an existing list. You get that ID from `mailingListEdit` (on creation) or from `mailingLists`. No interface access is needed to create a list.

> **Dispatching a campaign to a list.** The same ID goes into `campaignRun` as `mailingList`, and the system dials the list's recipients without you sending any numbers — see [Targeting a mailing list](campaignApi.md#targeting-a-mailing-list) in the Campaign API reference.

> **Spelling note.** The action name `uplodePhones` is intentional and case-sensitive — it is preserved for backwards compatibility with existing integrations. Use it exactly as shown.

---

## Lifecycle of a list

A full integration from scratch, without touching the UI:

| Step | Action                        | What happens                                                                                  |
|------|-------------------------------|-----------------------------------------------------------------------------------------------|
| 1    | `mailingListEdit`             | Create the list with a `name`. Returns `mailingList` — the ID used by every later call.       |
| 2    | `uplodePhones`                | Import contacts into that ID.                                                                 |
| 3    | `phonesList` / `setStatus`    | Read, search, block opt-outs — routine maintenance.                                           |
| 4    | `mailingLists`                | Enumerate the account's lists with per-list contact counts.                                   |
| 5    | `mailingListDelete`           | Delete the list along with all of its contacts.                                               |

> **List names are not unique.** Calling `mailingListEdit` without an `id` always creates another list, even if one with the same name exists. An automated "make sure the list exists" step must either persist the returned `mailingList` ID or look the name up via `mailingLists` first — see [Recipe 0a](#0a-get-or-create-a-list-by-name).

---

## Action — `mailingLists`

Return every mailing list in the account, newest first, with contact counts.

### Request

| Param    | Required | Type   | Description                |
|----------|----------|--------|----------------------------|
| `action` | yes      | string | Must be `mailingLists`.    |
| `apiKey` | yes      | string | Tenant API key.            |

No other parameters — the response always covers all of the account's lists.

### Successful response

```json
{
  "status": "Ok",
  "lists": [
    { "id": 1508, "name": "לקוחות מסר", "phonesCount": 1250, "active": 1198, "blocked": 52 },
    { "id": 1506, "name": "מבצע חנוכה", "phonesCount": 311,  "active": 311,  "blocked": 0  }
  ],
  "listsCount": 2
}
```

| Field                  | Description                                                                       |
|------------------------|-------------------------------------------------------------------------------------|
| `lists[].id`           | List ID — this is what you pass as `mailingList` to every contact-level action.    |
| `lists[].name`         | List name as shown in the interface.                                               |
| `lists[].phonesCount`  | Total contacts in the list (active + blocked).                                     |
| `lists[].active`       | Active contacts — the ones an outgoing campaign will reach.                        |
| `lists[].blocked`      | Blocked contacts (`status=1`).                                                     |
| `listsCount`           | Number of lists in the account.                                                    |

Counts are computed at call time across all contacts. On accounts with very large lists this is a comparatively heavy call — use it to discover list IDs, not on every work cycle.

### Example

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=mailingLists" \
  -d "apiKey=YOUR_API_KEY"
```

```python
r = requests.post(URL, data={'action': 'mailingLists', 'apiKey': KEY}).json()
lists = {l['name']: l['id'] for l in r['lists']}
mailing_list = lists.get('לקוחות מסר')   # None if no list by that name
```

---

## Action — `mailingListEdit`

Create a new mailing list, or rename an existing one.

### Request

| Param    | Required | Type    | Description                                                                                       |
|----------|----------|---------|-----------------------------------------------------------------------------------------------------|
| `action` | yes      | string  | Must be `mailingListEdit`.                                                                        |
| `apiKey` | yes      | string  | Tenant API key.                                                                                   |
| `name`   | yes      | string  | List name. Empty or whitespace-only is rejected.                                                  |
| `id`     | no       | integer | ID of an existing list to rename. **Omit it to create a new list.** Also accepted as `mailingList`. |

### Successful response — create

```json
{
  "status": "Ok",
  "note": "רשימת התפוצה נוצרה בהצלחה",
  "mailingList": 2742,
  "id": 2742,
  "name": "לקוחות מסר"
}
```

`mailingList` and `id` carry the same value — the new list ID. Store it: it is the `mailingList` parameter of every other action.

### Successful response — rename

```json
{ "status": "Ok", "note": "שם רשימת התפוצה עודכן בהצלחה", "mailingList": 2742, "id": 2742, "name": "לקוחות מסר 2026" }
```

### Error responses

```json
{ "status": "Error", "note": "חסר שם לרשימת התפוצה" }
{ "status": "Error", "note": "רשימת התפוצה לא נמצאה" }
```

`רשימת התפוצה לא נמצאה` is returned when the `id` does not exist or belongs to another account — nothing is changed.

Renaming never touches contacts. To swap out a list's contents use `uplodePhones` with `insertType=replacing`.

### Example — create

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=mailingListEdit" \
  -d "apiKey=YOUR_API_KEY" \
  --data-urlencode "name=לקוחות מסר"
```

### Example — rename

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=mailingListEdit" \
  -d "apiKey=YOUR_API_KEY" \
  -d "id=2742" \
  --data-urlencode "name=לקוחות מסר 2026"
```

---

## Action — `mailingListDelete`

Delete a mailing list **and every contact inside it**.

> **Destructive and irreversible.** There is no recycle bin. To empty a list while keeping its ID, use `uplodePhones` with `insertType=replacing` and an empty `phones` array instead.

### Request

| Param         | Required | Type    | Description                                          |
|---------------|----------|---------|--------------------------------------------------------|
| `action`      | yes      | string  | Must be `mailingListDelete`.                         |
| `apiKey`      | yes      | string  | Tenant API key.                                      |
| `mailingList` | yes      | integer | ID of the list to delete. Also accepted as `id`.     |

### Successful response

```json
{
  "status": "Ok",
  "note": "רשימת התפוצה נמחקה בהצלחה",
  "deletedNumbers": 1250
}
```

`deletedNumbers` is how many contacts were removed along with the list.

### Error response

```json
{ "status": "Error", "note": "רשימת התפוצה לא נמצאה" }
```

Returned when the ID does not exist or belongs to another account — in which case nothing is deleted.

### Example

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=mailingListDelete" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=2742"
```

---

## Action — `uplodePhones`

Bulk import contacts into a mailing list. Supports four import modes: add-and-update, replace, insert-only, and delete.

### Request

`POST` to `https://api.tlivr.com/mailingListsApi.php`.

| Param         | Required | Type     | Description                                                                                  |
|---------------|----------|----------|----------------------------------------------------------------------------------------------|
| `action`      | yes      | string   | Must be `uplodePhones`.                                                                      |
| `apiKey`      | yes      | string   | Tenant API key.                                                                              |
| `mailingList` | yes      | integer  | Numeric ID of the target mailing list.                                                       |
| `phones`      | yes\*    | array / JSON-string | Array of contact rows (see [Contact row](#contact-row) below). \*Not required when `insertType=delete` is used to clear the entire list. |
| `insertType`  | no       | enum     | Import mode. Default `all`. See [Import modes](#import-modes).                               |
| `name`        | no       | string   | Reserved — display name for the import batch. May be omitted.                                |

#### Contact row

Each entry inside `phones` is an object:

| Field       | Required | Type    | Description                                                |
|-------------|----------|---------|------------------------------------------------------------|
| `phone`     | yes\*    | string  | Phone number. Will be normalized server-side.              |
| `firstName` | no       | string  | First name.                                                |
| `lastName`  | no       | string  | Last name.                                                 |
| `email`     | no       | string  | E-mail address. \*Either `phone` or `email` must be set.   |
| `status`    | no       | integer | `0` = active (default), `1` = blocked.                     |

#### Import modes

| `insertType`  | Behavior                                                                                                              |
|---------------|-----------------------------------------------------------------------------------------------------------------------|
| `all`         | (default) — Add new contacts and update fields of existing ones. Match by `phone`, then by `email`.                    |
| `replacing`   | Empty the list, then insert the new rows. **Destructive** — existing contacts in this list are removed.               |
| `insert`      | Insert only — new contacts are added; rows that already exist (by `phone` / `email`) are skipped silently.            |
| `delete`      | Remove the matching contacts from the list. Rows in `phones` that don't exist are ignored.                            |

### Successful response

HTTP 200, JSON:

```json
{
  "newNumbers":     12,
  "updateNumbers":  4,
  "errorNumbers":   1,
  "deletedNumbers": 0
}
```

| Field             | Description                                                       |
|-------------------|-------------------------------------------------------------------|
| `newNumbers`      | Number of contacts that were inserted.                            |
| `updateNumbers`   | Number of existing contacts that were updated.                    |
| `errorNumbers`    | Number of rows skipped because they had neither `phone` nor `email`. |
| `deletedNumbers`  | Number of contacts removed (relevant for `replacing` and `delete`).|

### Example — cURL (form, `phones` as JSON string)

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=uplodePhones" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=1042" \
  -d "insertType=all" \
  --data-urlencode 'phones=[
    {"phone":"0501234567","firstName":"Avi","lastName":"Cohen","email":"avi@example.com","status":0},
    {"phone":"0507654321","firstName":"Dana","lastName":"Levy","email":"","status":0}
  ]'
```

### Example — PHP

```php
$payload = [
    'action'      => 'uplodePhones',
    'apiKey'      => 'YOUR_API_KEY',
    'mailingList' => 1042,
    'insertType'  => 'all',
    'phones'      => json_encode([
        ['phone' => '0501234567', 'firstName' => 'Avi',  'lastName' => 'Cohen', 'email' => 'avi@example.com',  'status' => 0],
        ['phone' => '0507654321', 'firstName' => 'Dana', 'lastName' => 'Levy',  'email' => '',                 'status' => 0],
    ]),
];

$ch = curl_init('https://api.tlivr.com/mailingListsApi.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
```

### Example — Python (requests, JSON body)

```python
import requests

r = requests.post('https://api.tlivr.com/mailingListsApi.php', json={
    'action':      'uplodePhones',
    'apiKey':      'YOUR_API_KEY',
    'mailingList': 1042,
    'insertType':  'all',
    'phones': [
        {'phone': '0501234567', 'firstName': 'Avi',  'lastName': 'Cohen', 'email': 'avi@example.com', 'status': 0},
        {'phone': '0507654321', 'firstName': 'Dana', 'lastName': 'Levy',  'email': '',                'status': 0},
    ],
})
print(r.json())
```

---

## Action — `phonesList`

Read the contacts inside a mailing list. Supports free-text search and pagination.

### Request

| Param         | Required | Type    | Description                                                                |
|---------------|----------|---------|----------------------------------------------------------------------------|
| `action`      | yes      | string  | Must be `phonesList`.                                                      |
| `apiKey`      | yes      | string  | Tenant API key.                                                            |
| `mailingList` | yes      | integer | Numeric ID of the mailing list.                                            |
| `freeSerch`   | no       | string  | Free-text search across `phone` and `name`. (Spelling preserved for backwards compatibility.) |
| `limit`       | no       | integer | Page size. When omitted, all rows are returned without paging.             |
| `page`        | no       | integer | Zero-based page number. Used with `limit`.                                 |

### Successful response

```json
{
  "phonesList": {
    "10001": {
      "id":        "10001",
      "phone1":    "0501234567",
      "phones":    "0501234567",
      "phoneID":   "00972501234567",
      "name":      "Avi Cohen",
      "firstName": "Avi Cohen",
      "email":     "avi@example.com",
      "emails":    "avi@example.com",
      "note":      "",
      "status":    "0",
      "RHstatus":  0
    }
  },
  "phonesCount": "1,250",
  "fromLine":    0,
  "toLine":      50
}
```

| Field                    | Description                                                              |
|--------------------------|--------------------------------------------------------------------------|
| `phonesList`             | Object keyed by contact ID; each value is the contact row.               |
| `phonesList.<id>.id`     | Contact ID — pass this to `phonesDelete` or `phoneEdit` to address the row. |
| `phonesList.<id>.phone1` | The contact's phone number.                                              |
| `phonesList.<id>.phoneID`| The phone in international (E.164-style, `00…`) form.                     |
| `phonesList.<id>.name`   | Display name. Also mirrored in `firstName` for backward compatibility.   |
| `phonesList.<id>.note`   | Free-text note.                                                          |
| `phonesList.<id>.phones` | Comma-joined string of all phone numbers on the contact.                 |
| `phonesList.<id>.emails` | Comma-joined string of all e-mails on the contact.                       |
| `phonesList.<id>.status` | `"0"` active / `"1"` blocked.                                            |

> **Legacy fields.** For backward compatibility the response also includes `phone2`, `phone3`, `lastName` and `email2`. These are retained as empty strings — the current data model stores a single phone, a single `name`, one `email`, and a `note`.
| `phonesCount`            | Total contacts matching the filter, formatted with thousand-separators.  |
| `fromLine`, `toLine`     | First/last index of the returned page.                                   |

### Example

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=phonesList" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=1042" \
  -d "freeSerch=Cohen" \
  -d "limit=50" \
  -d "page=0"
```

---

## Action — `phoneEdit`

Add or update a single contact.

### Request

| Param         | Required | Type             | Description                                                                  |
|---------------|----------|------------------|------------------------------------------------------------------------------|
| `action`      | yes      | string           | Must be `phoneEdit`.                                                         |
| `apiKey`      | yes      | string           | Tenant API key.                                                              |
| `mailingList` | yes      | integer          | Numeric ID of the mailing list.                                              |
| `id`          | no       | integer / string | Contact ID for update. Omit (or pass anything ≤ 0) to insert a new contact.  |
| `phone1`      | yes\*    | string           | Primary phone. \*Either `phone1` or `email` must be set.                     |
| `name`        | no       | string           | Display name. Alternatively send `firstName` / `lastName` (combined into `name`). |
| `firstName`   | no       | string           | First name (combined into `name` if `name` not sent).                        |
| `lastName`    | no       | string           | Last name (combined into `name` if `name` not sent).                         |
| `email`       | yes\*    | string           | Primary e-mail. \*Either `phone1` or `email` must be set.                    |
| `note`        | no       | string           | Free-text note on the contact (`email2` is accepted as an alias).            |

When `id` is not provided, the server first searches the list by `phone1` or `email`. If a match is found, it is updated; otherwise a new contact is inserted. The phone is normalized before matching, so `0501234567`, `+972501234567` and `972-50-123-4567` all resolve to the same contact.

### Successful response

```json
{ "status": "Ok", "note": "איש הקשר נשמר בהצלחה" }
```

### Error response

```json
{ "status": "Error", "note": "חייב טלפון ראשי או כתובת מייל" }
```

Returned when neither `phone1` nor `email` is supplied.

### Example

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=phoneEdit" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=1042" \
  -d "phone1=0501234567" \
  -d "firstName=Avi" \
  -d "lastName=Cohen" \
  -d "email=avi@example.com"
```

---

## Action — `phonesDelete`

Delete one or more contacts by phone number.

### Request

| Param         | Required | Type           | Description                                                              |
|---------------|----------|----------------|--------------------------------------------------------------------------|
| `action`      | yes      | string         | Must be `phonesDelete`.                                                  |
| `apiKey`      | yes      | string         | Tenant API key.                                                          |
| `mailingList` | yes      | integer        | Numeric ID of the mailing list.                                          |
| `deleteList`  | yes      | string / array | Comma-separated string of phone numbers, or a JSON array of phone numbers. Matched against the `phone` column. |

### Successful response

```json
{ "status": "Ok", "note": "המספרים נמחקו בהצלחה" }
```

### Example

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=phonesDelete" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=1042" \
  -d "deleteList=0501234567,0507654321,0539876543"
```

---

## Action — `setStatus`

Block or unblock contacts. Blocked contacts (`status=1`) are excluded from outgoing campaigns until they are re-activated.

### Request

| Param         | Required | Type    | Description                                                                       |
|---------------|----------|---------|-----------------------------------------------------------------------------------|
| `action`      | yes      | string  | Must be `setStatus`.                                                              |
| `apiKey`      | yes      | string  | Tenant API key.                                                                   |
| `mailingList` | yes      | integer | Numeric ID of the mailing list.                                                   |
| `phone`       | yes      | string  | A **phone number**, or several phone numbers separated by commas (e.g. `0501234567,0507654321`). Matched against the `phone` column of the list — **not** the contact `id`. |
| `status`      | yes      | integer | `1` = block, `0` = unblock.                                                       |

### Successful response

```json
{
  "status": "Ok",
  "note":   "המספר נחסם בהצלחה",
  "phones": ["0501234567", "0507654321"]
}
```

`note` is `המספר נחסם בהצלחה` when blocking and `המספר אושר בהצלחה` when unblocking.

### Example — block two contacts

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=setStatus" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=1042" \
  -d "phone=0501234567,0507654321" \
  -d "status=1"
```

### Example — unblock a single contact

```bash
curl -X POST https://api.tlivr.com/mailingListsApi.php \
  -d "action=setStatus" \
  -d "apiKey=YOUR_API_KEY" \
  -d "mailingList=1042" \
  -d "phone=0501234567" \
  -d "status=0"
```

---

## Recipes

### 0. "Create a list from scratch and fill it"

Two calls: the first returns the ID, the second streams contacts into it.

```python
created = requests.post(URL, data={
    'action': 'mailingListEdit', 'apiKey': KEY,
    'name':   'לקוחות מסר',
}).json()

mailing_list = created['mailingList']      # the new ID — persist this on your side

requests.post(URL, json={
    'action': 'uplodePhones', 'apiKey': KEY, 'mailingList': mailing_list,
    'insertType': 'all',
    'phones': [{'phone': c.phone, 'name': c.name, 'email': c.email} for c in contacts],
})
```

### 0a. "Get-or-create a list by name"

List names are not unique, so a blind create on every run duplicates lists. Look the name up first:

```python
lists = requests.post(URL, data={'action':'mailingLists', 'apiKey':KEY}).json()['lists']
found = next((l['id'] for l in lists if l['name'] == LIST_NAME), None)

if found is None:
    found = requests.post(URL, data={'action':'mailingListEdit', 'apiKey':KEY,
                                     'name':LIST_NAME}).json()['mailingList']
```

### 1. "Sync today's CRM segment to a campaign list"

Use `insertType=all`. New contacts are added, existing ones are updated by phone match.

```python
requests.post(URL, json={
    'action': 'uplodePhones', 'apiKey': KEY, 'mailingList': 1042,
    'insertType': 'all',
    'phones': [
        {'phone': c.phone, 'firstName': c.first, 'lastName': c.last,
         'email': c.email, 'status': 0}
        for c in todays_segment
    ],
})
```

### 2. "Replace the entire weekly broadcast list"

Use `insertType=replacing`. The list is emptied first, then refilled.

```python
requests.post(URL, json={
    'action': 'uplodePhones', 'apiKey': KEY, 'mailingList': 1042,
    'insertType': 'replacing',
    'phones': new_weekly_list,
})
```

### 3. "Process opt-outs"

Call `setStatus` with `status=1` and the opted-out **phone numbers** directly — no lookup needed, since `setStatus` matches on the phone number:

```python
optout_phones = '0501234567,0507654321'   # comma-separated phone numbers
requests.post(URL, data={'action':'setStatus', 'apiKey':KEY,
                         'mailingList':1042, 'phone':optout_phones, 'status':1})
```

### 4. "Remove specific contacts entirely"

When you want the contacts gone (not blocked), use `phonesDelete` with the **phone numbers**:

```python
requests.post(URL, data={'action':'phonesDelete', 'apiKey':KEY,
                         'mailingList':1042, 'deleteList':'0501234567,0507654321'})
```

### 5. "Clean up temporary campaign lists"

A list created for a one-off campaign is removed entirely — list plus contacts:

```python
lists = requests.post(URL, data={'action':'mailingLists', 'apiKey':KEY}).json()['lists']

for l in lists:
    if l['name'].startswith('מבצע-'):
        requests.post(URL, data={'action':'mailingListDelete', 'apiKey':KEY,
                                 'mailingList':l['id']})
```

### 6. "Empty a list without deleting it"

The ID survives — anything pointing at it (campaigns, IVR extensions) keeps working; only the contents reset:

```python
requests.post(URL, json={'action':'uplodePhones', 'apiKey':KEY,
                         'mailingList':1042, 'insertType':'replacing', 'phones':[]})
```

---

## Implementation notes

- **List IDs come from the server.** You cannot choose one up front — it is minted by `mailingListEdit` and returned in `mailingList`. Persist it, or you will have to re-discover the list by name through `mailingLists`.
- **Deleting a list deletes its contacts.** `mailingListDelete` removes both and is irreversible. For a list still wired to a campaign or an IVR extension, prefer emptying it (`insertType=replacing` with an empty `phones`) over deleting it.
- **Ownership is enforced on every list-level action.** `mailingListEdit` (with `id`) and `mailingListDelete` return `"רשימת התפוצה לא נמצאה"` for an ID belonging to another account, and perform no change.
- **Always check `status === "Ok"`** — the mailing-lists API returns `"Ok"` (mixed case) for success, distinct from some sister APIs that return `"OK"` (all-caps). Compare case-insensitively if you talk to several IP-Sales APIs from the same client.
- **Phone normalization.** Phone numbers are normalized server-side (digits only, leading-zero local format). You can send any human-readable form (`050-123-4567`, `+972-50-123-4567`) — the server will canonicalize.
- **Either phone or email is required** for every contact row. Rows with both empty are silently rejected and counted in `errorNumbers`.
- **Block vs. delete.** `setStatus` keeps the row but flags it as inactive — useful for compliant opt-out tracking. `phonesDelete` removes the row completely.
- **`setStatus` matches by phone number.** The `phone` parameter takes one or more **phone-number strings** (comma-separated), matched against the `phone` column of the list — **not** the contact `id`. Passing `phonesList` IDs here will match nothing.
- **Searching.** `freeSerch` (sic) is a free-text query matched against phone digits, first name, and last name. Send only digits to search by phone (`-` is stripped server-side).
- **Spelling preserved for compatibility.** `uplodePhones` and `freeSerch` are intentionally spelled this way and have been stable since launch — keep the misspellings exactly as shown.
