# Contacts

A contact is a person who writes to a bot in a messenger or in the chat on a website. The methods on this page export your bots' contacts — for example, to move them into a CRM — count them, and create a contact from your own system when you already know the person's messenger ID. For contacts in the dashboard, see [Contacts](https://support.watbot.org/chatbots/contacts).

You can page through an export in two ways: by pages (`page` and `count`, up to 500 contacts per request) or by an offset from the start (`offset` and `limit`, up to 1000 contacts per request). `getNumberOfContacts` tells you how many contacts match.

The methods return contacts of your own bots only. Contacts created while testing a bot in preview are not included in exports or counts.

## Contact fields

Every method that returns contacts — here and on the [Referrals](https://support.watbot.org/api/referrals) page — returns them in the same shape.

| Field | Type | Description |
|---|---|---|
| `id` | number | Contact ID |
| `bot_id` | number | Bot ID |
| `phone` | string or `null` | Phone number, if the contact shared it or it was set via the API |
| `email` | string or `null` | Email |
| `name` | string | Name |
| `address` | string or `null` | Address |
| `messenger` | string | Where the contact comes from: `telegram`, `max`, `viber`, `vk` or `web` (website chat) |
| `utm` | object | Contact variables whose names start with `utm_`: `{"utm_source": "…"}`. If there are none, an empty array `[]` |
| `avatar` | string or `null` | Link to the contact's photo. A link to a file in storage is temporary — save the file itself, not the link |
| `telegram_id` | string | Telegram user ID. Telegram contacts only |
| `telegram_username` | string or `null` | Telegram username without `@`. Telegram contacts only |
| `max_user_id` | number | MAX user ID. MAX contacts only |
| `max_username` | string or `null` | MAX username. MAX contacts only |
| `vk_user_id` | number or `null` | VK user ID. Present for every contact; `null` for non-VK contacts |
| `viber_id` | string | Viber user ID. Viber contacts only |
| `created_at` | string | When the contact appeared, ISO 8601 in UTC |
| `unsubscribed_at` | string or `null` | When the contact unsubscribed from the bot; `null` means subscribed |
| `tags` | array of strings | Contact tags |
| `variables` | array of objects | Contact variables |

Each variable in `variables` is an object:

| Field | Description |
|---|---|
| `id` | Variable ID within the contact |
| `name` | Variable name |
| `value` | Value |
| `type` | What the value holds: `0` — text, `1` — image, `2` — document, `3` — audio, `4` — video. For media, `value` holds a link to the file — the [decodeShortLink](https://support.watbot.org/api/links) method resolves it |
| `schema_id`, `deletable`, `payload` | Internal fields |

For tags and variables in the dashboard, see [Tags and variables](https://support.watbot.org/chatbots/contacts/tags-variables).

## getContacts

Returns your bots' contacts — page by page (20 per page by default) or by an offset from the start.

```endpoint
GET POST https://watbot.org/api/v1/getContacts
```

| Parameter | Type | Required | Description |
|---|---|---|---|
| `bot_id` | number | no | Only contacts of this bot. Without it — contacts of all your bots |
| `count` | number | no | Contacts per page: 1 to 500. Default 20 |
| `page` | number | no | Page number, starting from 1. Default 1 |
| `offset` | number | no | How many contacts to skip from the start, from 0. Switches to offset paging |
| `limit` | number | no | How many contacts to return in offset paging: 1 to 1000. Default 100. Also switches to offset paging |
| `date_from` | number | no | Contacts that appeared at or after this moment. Unix time in seconds |
| `date_to` | number | no | Contacts that appeared at or before this moment. Unix time in seconds |
| `with` | string | no | Accepted for compatibility, e.g. `variables`. Changes nothing: tags and variables are always in the response |

Contacts are sorted by ascending `id` — both page by page and with an offset. If `offset` or `limit` is passed, offset paging is used and `page` and `count` are ignored.

### Page by page

```bash title="Request"
curl -X POST "https://watbot.org/api/v1/getContacts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"bot_id": 12345, "count": 2, "date_from": 1758931200}'
```

```json title="Response 200"
{
  "data": [
    {
      "id": 4815162,
      "bot_id": 12345,
      "phone": "15550100123",
      "email": null,
      "name": "Emily Johnson",
      "address": null,
      "messenger": "telegram",
      "utm": {"utm_source": "newsletter"},
      "avatar": null,
      "telegram_id": "123456789",
      "telegram_username": "emily_j",
      "vk_user_id": null,
      "created_at": "2026-09-28T07:41:12+00:00",
      "unsubscribed_at": null,
      "tags": ["ordered"],
      "variables": [
        {"id": 17590475010001, "name": "utm_source", "value": "newsletter", "type": 0, "schema_id": null, "deletable": false, "payload": null},
        {"id": 17590475820003, "name": "city", "value": "Austin", "type": 0, "schema_id": null, "deletable": false, "payload": null}
      ]
    },
    {
      "id": 4815170,
      "bot_id": 12345,
      "phone": null,
      "email": null,
      "name": "Michael Brown",
      "address": null,
      "messenger": "vk",
      "utm": [],
      "avatar": null,
      "vk_user_id": 700123456,
      "created_at": "2026-09-29T15:02:47+00:00",
      "unsubscribed_at": null,
      "tags": [],
      "variables": []
    }
  ],
  "links": {
    "first": "https://watbot.org/api/v1/getContacts?page=1",
    "last": "https://watbot.org/api/v1/getContacts?page=38",
    "prev": null,
    "next": "https://watbot.org/api/v1/getContacts?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 38,
    "links": […],
    "path": "https://watbot.org/api/v1/getContacts",
    "per_page": 2,
    "to": 2,
    "total": 75
  }
}
```

- `meta.total` is the total number of matching contacts, `meta.last_page` is the number of the last page. To export everything, request pages until `links.next` becomes `null`. The URLs in `links` carry none of your parameters except `page` — send `bot_id`, `count` and the dates again with every request.
- New contacts land at the end of the export: sorting by `id` keeps pages from shifting. To keep the export from growing while you page through it, cap it with `date_to`.
- A `bot_id` of a bot that is not yours or does not exist does not cause an error: the list is simply empty.

### By offset

Requesting the bot's second hundred contacts:

```bash title="Request"
curl -X POST "https://watbot.org/api/v1/getContacts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"bot_id": 12345, "limit": 100, "offset": 100}'
```

```json title="Response 200"
{
  "data": [
    {
      "id": 4815162,
      "bot_id": 12345,
      "phone": "15550100123",
      "email": null,
      "name": "Emily Johnson",
      "address": null,
      "messenger": "telegram",
      "utm": [],
      "avatar": null,
      "telegram_id": "123456789",
      "telegram_username": "emily_j",
      "vk_user_id": null,
      "created_at": "2026-09-28T07:41:12+00:00",
      "unsubscribed_at": null,
      "tags": ["ordered"],
      "variables": [
        {"id": 17590475820003, "name": "city", "value": "Austin", "type": 0, "schema_id": null, "deletable": false, "payload": null}
      ]
    }
  ],
  "meta": {
    "offset": 100,
    "limit": 100,
    "total": 101
  }
}
```

- `meta.total` is the total number of matching contacts. The export is complete when `data` holds fewer contacts than `limit`, or `offset + limit` has reached `meta.total`.
- For the first batch, pass `offset` `0` or omit `offset`.

## getNumberOfContacts

Counts your bots' contacts, with the same filters as `getContacts`.

```endpoint
GET POST https://watbot.org/api/v1/getNumberOfContacts
```

| Parameter | Type | Required | Description |
|---|---|---|---|
| `bot_id` | number | no | Only contacts of this bot. Without it — all your bots |
| `date_from` | number | no | Contacts that appeared at or after this moment. Unix time in seconds |
| `date_to` | number | no | Contacts that appeared at or before this moment. Unix time in seconds |

```bash title="Request"
curl "https://watbot.org/api/v1/getNumberOfContacts?bot_id=12345" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
```

```json title="Response 200"
{
  "count": 1284
}
```

The number is in the top-level `count` field, without a `data` wrapper.

## createOrUpdateContact

Creates a contact in a bot or updates an existing one. The contact is looked up in the bot by its messenger ID: `telegram_id`, `max_id`, `vk_id` or `viber_id`. If found, its data is updated; if not, a new contact is created.

```endpoint
GET POST https://watbot.org/api/v1/createOrUpdateContact
```

| Parameter | Type | Required | Description |
|---|---|---|---|
| `bot_id` | number | yes | ID of your bot |
| `messenger` | string | yes | The contact's messenger: `telegram`, `max`, `viber` or `vk` |
| `name` | string | yes | Contact name. Replaces the previous name on every call |
| `telegram_id` | number | for `telegram` | Telegram user ID |
| `telegram_username` | string | no | Telegram username, up to 255 characters |
| `max_id` | number | for `max` | MAX user ID. Can also be passed as `max_user_id` — the name it has in the response |
| `max_username` | string | no | MAX username, up to 255 characters |
| `vk_id` | number | for `vk` | VK user ID. Can also be passed as `vk_user_id` — the name it has in the response |
| `viber_id` | string | for `viber` | Viber user ID, up to 255 characters |
| `phone` | string | no | Phone in international format: `+` followed by digits, 9 to 16 characters including the plus — `+15550100123`. Stored as digits, without `+`; `null` is the same as not passing a phone |
| `email` | string | no | Email |
| `address` | string | no | Address, up to 255 characters |
| `tags` | array of strings | no | Tags, up to 100, each up to 255 characters. They **replace** all of the contact's tags |

```bash title="Request"
curl -X POST "https://watbot.org/api/v1/createOrUpdateContact" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_id": 12345,
    "messenger": "telegram",
    "telegram_id": 123456789,
    "telegram_username": "emily_j",
    "name": "Emily Johnson",
    "phone": "+15550100123",
    "email": "emily@example.com",
    "tags": ["client", "from CRM"]
  }'
```

```json title="Response 201"
{
  "data": {
    "id": 4815162,
    "bot_id": 12345,
    "phone": "15550100123",
    "email": "emily@example.com",
    "name": "Emily Johnson",
    "address": null,
    "messenger": "telegram",
    "utm": [],
    "avatar": null,
    "telegram_id": "123456789",
    "telegram_username": "emily_j",
    "vk_user_id": null,
    "created_at": "2026-10-03T09:15:42+00:00",
    "unsubscribed_at": null,
    "tags": ["client", "from CRM"],
    "variables": []
  }
}
```

```json title="Response 404"
{
  "message": "Bot not found",
  "errors": {
    "bot_id": ["Bot not found"]
  }
}
```

Status `201` means the contact was created, `200` means an existing one was updated.

- **What changes on an existing contact.** `name` — always. `email`, `address`, `phone`, `telegram_username`, `max_username` — only if passed and not empty: this method cannot clear them. `tags` — if passed, they replace the whole set of tags; an empty array `[]` removes all tags. The method does not touch contact variables.
- **A new contact** appears in the bot's contact list with the first message "Created from API" and increases the bot's subscriber count. The bot's scenario is not started, and nothing is sent to the contact.
- **The phone** is stored as digits, without `+` — the same way bot blocks store it. `phone` in the response has no `+` either.

Method errors (general ones are under [Errors](https://support.watbot.org/api#errors)):

| Code | When | Response |
|---|---|---|
| `404` | None of your bots has this `bot_id` — it does not exist or belongs to another account | `{"message": "…", "errors": {"bot_id": ["…"]}}` |
| `422` | The ID for the chosen messenger is missing, or the phone or email format is invalid | `{"message": "…", "errors": {…}}` |
