---
title: "REST API v1: контакты, история и отправка сообщений"
nav_title: "REST API v1"
url: https://docs.tuk-tuk.online/api/rest
markdown: https://docs.tuk-tuk.online/api/rest.md
section: "API для разработчиков"
description: "REST API v1 tuk-tuk.online: авторизация по API-ключу со scopes, список мессенджеров, карточки контактов, история сообщений, ссылки на медиа и отправка сообщений (текст, голосовые, медиа, email) по ID контакта, телефону или email. Примеры на JavaScript, PHP и Python."
keywords: ["REST API", "API-ключ", "отправка сообщений через API", "send message API", "история сообщений API", "контакты API", "медиа API", "scopes", "rate limit", "API мессенджер для бизнеса", "API пример JavaScript", "API пример PHP"]
lang: ru
product: tuk-tuk.online
app: https://lk.tuk-tuk.online
api_base: https://back.tuk-tuk.online
---

> [!AI] Кратко
> REST API v1 tuk-tuk.online для серверных интеграций: список каналов, карточки контактов, история сообщений, ссылки на медиа и отправка сообщений (текст, голосовые, медиа, email) в любой подключённый канал. Авторизация по API-ключу со scopes, ответы в JSON.
>
> - Базовый URL: `https://back.tuk-tuk.online/api/v1`. Ключ вида `tuk_sk_...` передаётся в заголовке `Authorization: Bearer <ключ>` или `X-Api-Key: <ключ>`.
> - Ключ создаётся в кабинете: Настройки проекта → вкладка API → «Создать ключ». Показывается один раз, хранится как SHA-256 хэш; можно задать scopes, срок действия, белый список IP и лимит запросов в минуту.
> - Scopes (22): `messengers:read`, `contacts:read`, `contacts:write`, `messages:read`, `media:read`, `messages:send`, `files:write`, `funnels:read`, `funnels:write`, `stories:read`, `stories:write`, `broadcasts:read`, `broadcasts:write`, `chains:read`, `chains:write`, `assistants:read`, `assistants:write`, `tasks:read`, `tasks:write`, `webhooks:manage`, `account:read`. Без нужного scope ответ `403`.
> - Самоописание: `GET /api/v1/manifest` возвращает список ресурсов с признаком доступности для текущего ключа, `GET /api/v1/openapi.json` отдаёт спецификацию OpenAPI 3.1. Оба требуют только валидный ключ.
> - Отложенная отправка: `POST /api/v1/messages/schedule` с полем `sendAt` (ISO-8601, минимум на 30 секунд вперёд), список `GET /api/v1/messages/scheduled`, отмена `DELETE /api/v1/messages/scheduled/:id`.
> - Идемпотентность: заголовок `Idempotency-Key` на отправке и постановке в расписание; повтор возвращает первый результат, ключ живёт сутки.
> - Другие ресурсы: контакты на запись, файлы (`POST /files` забирает файл по ссылке), рассылки с расчётом аудитории, связки, воронки, ИИ-ассистенты с паузой в диалоге, задачи, подписки на события, состояние аккаунта.
> - Эндпоинты: `GET /me`, `GET /messengers`, `GET /contacts/:id`, `GET /contacts?phone=|email=`, `GET /contacts/:id/messages` (`limit` 1–100, по умолчанию 30, `offset`), `GET /media/:fileId`, `POST /messages`.
> - Формат ответа: успех `{ "data": ... }` (у списков плюс `{ "meta": ... }`), ошибка `{ "statusCode", "error", "message" }`; у `429` тело `{ "error": { "code": "rate_limited", "message" } }`.
> - Отправка `POST /messages`: адресат `dialogId`/`contactId` или `channelId` + `phone`/`email`/`identifier`; обязателен `text` или `mediaUrl`; `mediaType`: `photo`, `voice`, `audio`, `video`, `document`; для EMAIL `subject` и `rawEmail`.
> - Коды ошибок: `400` (нет text и mediaUrl), `403` (ключ или scope), `404` (не найдено в рабочем пространстве), `429` (лимит запросов ключа в минуту).
>
> Связанные страницы: https://docs.tuk-tuk.online/api, https://docs.tuk-tuk.online/api/agents, https://docs.tuk-tuk.online/api/webhooks, https://docs.tuk-tuk.online/api/verification, https://docs.tuk-tuk.online/channels, https://docs.tuk-tuk.online/dialogs/messages

# REST API v1

REST API v1 даёт вашему серверу прямой доступ к платформе: список каналов, карточки контактов, история переписки, медиа-файлы и отправка сообщений в любой подключённый мессенджер. Авторизация по API-ключу, ответы в JSON.

Базовый URL: `https://back.tuk-tuk.online/api/v1`. Успешные ответы обёрнуты в `{ "data": ... }`, у списков добавляется `{ "meta": ... }`.

## Для ИИ-агента

- Начни с `GET /api/v1/manifest`: он вернёт список ресурсов и покажет, что разрешено именно твоему ключу. Полная спецификация: `GET /api/v1/openapi.json`.
- Чтобы отправить сообщение, вызови `POST /api/v1/messages` с заголовком `Authorization: Bearer tuk_sk_...` и телом `{ "dialogId": "...", "text": "..." }` (нужен scope `messages:send`).
- Чтобы отправить по номеру телефона или email без известного контакта, передай `channelId` и `phone` или `email`: контакт найдётся или создастся в этом канале.
- Чтобы отправить позже, вызови `POST /api/v1/messages/schedule` с полем `sendAt` в ISO-8601: момент отправки держит платформа, ты можешь завершиться сразу.
- Чтобы прочитать историю, вызови `GET /api/v1/contacts/:id/messages?limit=30&offset=0` (scope `messages:read`); новые сообщения идут первыми.
- Чтобы найти контакт, вызови `GET /api/v1/contacts?phone=...` или `?email=...` (scope `contacts:read`); при отсутствии получишь `404`. Без этих параметров тот же адрес отдаёт список с фильтрами.
- При повторе запроса передавай заголовок `Idempotency-Key`: повтор вернёт первый результат, а не отправит дубль.
- Ключ создаётся в кабинете: Настройки проекта → вкладка API → «Создать ключ». Ключ показывается один раз, формат `tuk_sk_...`. Проверить ключ: `GET /api/v1/me`.
- Готовый MCP-сервер и порядок работы для агентов: [Для нейросетей и MCP](https://docs.tuk-tuk.online/api/agents).

## Создать API-ключ


![Вкладка «API» в настройках проекта](https://docs.tuk-tuk.online/assets/screens/settings/api-keys.webp "Ключи выпускаются во вкладке «API» настроек проекта. Секрет показывается один раз.")

1. Откройте [Настройки проекта](https://lk.tuk-tuk.online/settings) в личном кабинете.
2. Перейдите на вкладку **API**.
3. Нажмите **Создать ключ**, задайте название и выберите права (scopes).
4. Скопируйте ключ сразу: он показывается один раз и хранится в зашифрованном виде. Потеряли: отзовите и создайте новый.

Для ключа можно задать срок действия, белый список IP и лимит запросов в минуту. В любой момент ключ можно **отозвать** или **удалить**.

## Авторизация


![Создание API-ключа](https://docs.tuk-tuk.online/assets/screens/settings/api-key-form.webp)

1. Название ключа.
2. Права (scopes) и срок действия задаются здесь же; секрет показывается один раз.

Передавайте ключ в одном из заголовков:

```http
Authorization: Bearer tuk_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Api-Key: tuk_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

У каждого метода ниже примеры на JavaScript, PHP и Python, переключайте вкладки.

## Права (scopes)

У ключа есть набор прав, каждый метод требует своё:

| Scope | Право |
|---|---|
| `messengers:read` | Список привязанных мессенджеров |
| `contacts:read` | Чтение карточек контактов |
| `contacts:write` | Создание и изменение контактов, теги, поля, статусы, заметки |
| `messages:read` | Чтение истории сообщений |
| `media:read` | Ссылки на медиа и список файлов |
| `messages:send` | Отправка сообщений, включая отложенные |
| `files:write` | Загрузка файлов в хранилище |
| `funnels:read` | Чтение воронок автопостинга |
| `funnels:write` | Сборка и запуск воронок |
| `stories:read` | Истории Telegram-аккаунта: эфир, архив, просмотры и реакции |
| `stories:write` | Публикация и удаление историй, принудительный замер |
| `broadcasts:read` | Рассылки и отчёты доставки |
| `broadcasts:write` | Создание, запуск и остановка рассылок |
| `chains:read` | Чтение связок автоматизации |
| `chains:write` | Создание и изменение связок |
| `assistants:read` | Чтение ИИ-ассистентов |
| `assistants:write` | Настройка ассистентов и пауза в диалоге |
| `tasks:read` | Чтение задач |
| `tasks:write` | Создание и изменение задач |
| `webhooks:manage` | Подписки на события платформы |
| `account:read` | Баланс, тариф, лимиты, расход |

Если у ключа нет нужного права, ответ `403` с указанием недостающего scope.

## Формат ответа

Все ответы в JSON. Полезные данные всегда в поле `data`. У списков дополнительно приходит объект `meta` (счётчики и пагинация). Ошибки возвращаются с кодом 4xx/5xx и телом с полями `statusCode`, `error`, `message`.

```json
// один объект
{ "data": { } }

// список
{ "data": [ ], "meta": { "total": 12 } }

// ошибка
{ "statusCode": 403, "error": "Forbidden", "message": "Описание ошибки" }
```

## Эндпоинты

### GET /api/v1/me

Данные ключа и рабочего пространства. Удобно проверить, что ключ работает. Scope не требуется.

Параметров нет.

**Запрос**

**JavaScript**

```javascript
const res = await fetch("https://back.tuk-tuk.online/api/v1/me", {
  headers: { "Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ" },
});
const json = await res.json();
console.log(json.data);
```

**PHP**

```php
$ch = curl_init("https://back.tuk-tuk.online/api/v1/me");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer tuk_sk_ВАШ_КЛЮЧ"],
]);
$json = json_decode(curl_exec($ch), true);
print_r($json["data"]);
```

**Python**

```python
import requests
r = requests.get(
    "https://back.tuk-tuk.online/api/v1/me",
    headers={"Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ"},
)
print(r.json()["data"])
```

**Ответ**

```json
{
  "data": {
    "keyName": "Интеграция с CRM",
    "scopes": ["messengers:read", "contacts:read", "messages:read", "messages:send"],
    "projectId": null
  }
}
```

**Ошибки:** `403` (нет ключа, неверный, отозван, истёк, IP не разрешён), `429` (лимит запросов).

### GET /api/v1/messengers

Все подключённые каналы. Секреты (токены ботов и т.п.) не возвращаются. Scope: `messengers:read`.

Параметров нет.

**Запрос**

**JavaScript**

```javascript
const res = await fetch("https://back.tuk-tuk.online/api/v1/messengers", {
  headers: { "X-Api-Key": "tuk_sk_ВАШ_КЛЮЧ" },
});
const { data } = await res.json();
console.log(data);
```

**PHP**

```php
$ch = curl_init("https://back.tuk-tuk.online/api/v1/messengers");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["X-Api-Key: tuk_sk_ВАШ_КЛЮЧ"],
]);
$json = json_decode(curl_exec($ch), true);
print_r($json["data"]);
```

**Python**

```python
import requests
r = requests.get(
    "https://back.tuk-tuk.online/api/v1/messengers",
    headers={"X-Api-Key": "tuk_sk_ВАШ_КЛЮЧ"},
)
print(r.json()["data"])
```

**Ответ**

```json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "type": "TELEGRAM_BOT",
      "name": "Бот поддержки",
      "botName": "@my_support_bot",
      "isActive": true,
      "status": "connected",
      "error": null,
      "errorAt": null,
      "createdAt": "2026-01-12T09:30:00.000Z",
      "updatedAt": "2026-05-20T15:45:00.000Z"
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "type": "EMAIL",
      "name": "Почта поддержки",
      "botName": null,
      "isActive": false,
      "status": "disabled",
      "error": null,
      "errorAt": null,
      "createdAt": "2026-02-01T10:00:00.000Z",
      "updatedAt": "2026-05-10T08:15:00.000Z"
    }
  ],
  "meta": { "total": 2 }
}
```

Поле `status`: `connected` (подключён), `error` (активен, но есть ошибка), `disabled` (выключен).

**Ошибки:** `403` (нет scope `messengers:read` или проблема с ключом), `429`.

### GET /api/v1/contacts/:id

Карточка контакта по его ID. Scope: `contacts:read`.

| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| `id` (в пути) | string | да | ID контакта |

**Запрос**

**JavaScript**

```javascript
const id = "f1e2aaaa-1111-2222-3333-444455556666";
const res = await fetch("https://back.tuk-tuk.online/api/v1/contacts/" + id, {
  headers: { "Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ" },
});
console.log((await res.json()).data);
```

**PHP**

```php
$id = "f1e2aaaa-1111-2222-3333-444455556666";
$ch = curl_init("https://back.tuk-tuk.online/api/v1/contacts/$id");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer tuk_sk_ВАШ_КЛЮЧ"],
]);
$json = json_decode(curl_exec($ch), true);
print_r($json["data"]);
```

**Python**

```python
import requests
contact_id = "f1e2aaaa-1111-2222-3333-444455556666"
r = requests.get(
    f"https://back.tuk-tuk.online/api/v1/contacts/{contact_id}",
    headers={"Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ"},
)
print(r.json()["data"])
```

**Ответ**

```json
{
  "data": {
    "id": "f1e2aaaa-1111-2222-3333-444455556666",
    "name": "Иван Петров",
    "identifier": "123456789",
    "phone": "+79991234567",
    "email": "ivan@example.com",
    "avatarUrl": "https://back.tuk-tuk.online/uploads/avatars/abc.jpg",
    "channelId": "550e8400-e29b-41d4-a716-446655440001",
    "channelType": "TELEGRAM_BOT",
    "source": "Telegram",
    "tags": ["vip", "оплатил"],
    "customFields": { "Город": "Москва" },
    "dialogStatus": "IN_PROGRESS",
    "archivedAt": null,
    "unreadCount": 0,
    "lastMessageAt": "2026-05-28T10:00:00.000Z",
    "createdAt": "2026-01-12T09:30:00.000Z",
    "updatedAt": "2026-05-28T10:00:00.000Z"
  }
}
```

**Ошибки:** `403` (нет scope `contacts:read`), `404` (контакт не найден в этом рабочем пространстве), `429`.

### GET /api/v1/contacts

Поиск контакта по телефону или email. Возвращает ту же карточку, что и запрос по ID, либо `404`, если контакт не найден. Scope: `contacts:read`.

| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| `phone` (query) | string | одно из двух | Телефон контакта |
| `email` (query) | string | одно из двух | Email контакта |

**Запрос**

**JavaScript**

```javascript
const phone = encodeURIComponent("+79991234567");
const res = await fetch("https://back.tuk-tuk.online/api/v1/contacts?phone=" + phone, {
  headers: { "Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ" },
});
console.log((await res.json()).data);
```

**PHP**

```php
$phone = urlencode("+79991234567");
$ch = curl_init("https://back.tuk-tuk.online/api/v1/contacts?phone=$phone");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer tuk_sk_ВАШ_КЛЮЧ"],
]);
$json = json_decode(curl_exec($ch), true);
print_r($json["data"]);
```

**Python**

```python
import requests
r = requests.get(
    "https://back.tuk-tuk.online/api/v1/contacts",
    params={"phone": "+79991234567"},
    headers={"Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ"},
)
print(r.json()["data"])
```

**Ответ**

Карточка контакта в поле `data`, структура как у `GET /api/v1/contacts/:id`.

```json
{
  "data": {
    "id": "f1e2aaaa-1111-2222-3333-444455556666",
    "name": "Иван Петров",
    "phone": "+79991234567",
    "email": "ivan@example.com",
    "channelId": "550e8400-e29b-41d4-a716-446655440001",
    "channelType": "TELEGRAM_BOT",
    "dialogStatus": "IN_PROGRESS"
  }
}
```

**Ошибки:** `403` (нет scope `contacts:read`), `404` (контакт не найден), `429`.

### GET /api/v1/contacts/:id/messages

История переписки с контактом, новые сообщения сверху. Scope: `messages:read`.

| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| `id` (в пути) | string | да | ID контакта |
| `limit` (query) | number | нет | Сколько сообщений вернуть, 1–100, по умолчанию 30 |
| `offset` (query) | number | нет | Смещение для пагинации, по умолчанию 0 |

**Запрос**

**JavaScript**

```javascript
const id = "f1e2aaaa-1111-2222-3333-444455556666";
const res = await fetch("https://back.tuk-tuk.online/api/v1/contacts/" + id + "/messages?limit=2", {
  headers: { "Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ" },
});
const { data, meta } = await res.json();
console.log(data, meta);
```

**PHP**

```php
$id = "f1e2aaaa-1111-2222-3333-444455556666";
$ch = curl_init("https://back.tuk-tuk.online/api/v1/contacts/$id/messages?limit=2");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer tuk_sk_ВАШ_КЛЮЧ"],
]);
$json = json_decode(curl_exec($ch), true);
print_r($json["data"]);
```

**Python**

```python
import requests
contact_id = "f1e2aaaa-1111-2222-3333-444455556666"
r = requests.get(
    f"https://back.tuk-tuk.online/api/v1/contacts/{contact_id}/messages",
    params={"limit": 2},
    headers={"Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ"},
)
print(r.json()["data"])
```

**Ответ**

```json
{
  "data": [
    {
      "id": "9c0f-msg-2",
      "direction": "OUTBOUND",
      "body": "Здравствуйте! Чем можем помочь?",
      "channelId": "550e8400-e29b-41d4-a716-446655440001",
      "channelType": "TELEGRAM_BOT",
      "mediaUrl": null,
      "mediaType": null,
      "downloadUrl": null,
      "transcription": null,
      "messageStatus": "SENT",
      "externalMessageId": "12346",
      "senderName": null,
      "createdAt": "2026-05-28T10:01:00.000Z"
    },
    {
      "id": "9c0f-msg-1",
      "direction": "INBOUND",
      "body": "",
      "channelId": "550e8400-e29b-41d4-a716-446655440001",
      "channelType": "TELEGRAM_BOT",
      "mediaUrl": "https://back.tuk-tuk.online/uploads/voice/xyz.ogg",
      "mediaType": "voice",
      "downloadUrl": "https://back.tuk-tuk.online/uploads/voice/xyz.ogg",
      "transcription": "Здравствуйте, есть вопрос по заказу",
      "messageStatus": null,
      "externalMessageId": "12345",
      "senderName": null,
      "createdAt": "2026-05-28T10:00:00.000Z"
    }
  ],
  "meta": { "total": 142, "hasMore": true, "limit": 2, "offset": 0 }
}
```

Поле `direction`: `INBOUND` (от клиента), `OUTBOUND` (клиенту), `SYSTEM`, `NOTE`. У медиа-сообщений есть готовая ссылка `downloadUrl` для скачивания. Пагинация: `meta.hasMore` говорит, есть ли ещё сообщения за пределами `limit` + `offset`.

**Ошибки:** `403` (нет scope `messages:read`), `404` (контакт не найден), `429`.

### GET /api/v1/media/:fileId

Метаданные файла и ссылка на скачивание. Scope: `media:read`.

| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| `fileId` (в пути) | string | да | ID файла |

**Запрос**

**JavaScript**

```javascript
const fileId = "file-uuid-1234";
const res = await fetch("https://back.tuk-tuk.online/api/v1/media/" + fileId, {
  headers: { "Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ" },
});
const { data } = await res.json();
console.log(data.downloadUrl);
```

**PHP**

```php
$fileId = "file-uuid-1234";
$ch = curl_init("https://back.tuk-tuk.online/api/v1/media/$fileId");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer tuk_sk_ВАШ_КЛЮЧ"],
]);
$json = json_decode(curl_exec($ch), true);
echo $json["data"]["downloadUrl"];
```

**Python**

```python
import requests
file_id = "file-uuid-1234"
r = requests.get(
    f"https://back.tuk-tuk.online/api/v1/media/{file_id}",
    headers={"Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ"},
)
print(r.json()["data"]["downloadUrl"])
```

**Ответ**

```json
{
  "data": {
    "id": "file-uuid-1234",
    "originalName": "photo.jpg",
    "mimeType": "image/jpeg",
    "size": 184320,
    "url": "https://back.tuk-tuk.online/uploads/2026/05/abc.jpg",
    "downloadUrl": "https://back.tuk-tuk.online/uploads/2026/05/abc.jpg",
    "dialogId": "f1e2aaaa-1111-2222-3333-444455556666",
    "createdAt": "2026-05-28T10:00:00.000Z"
  }
}
```

**Ошибки:** `403` (нет scope `media:read`), `404` (файл не найден), `429`.

### POST /api/v1/messages

Отправка текста, голосового, медиа или email. Scope: `messages:send`. Получателя задают одним из двух способов:

- `dialogId` или `contactId`: в существующий диалог (контакт);
- `channelId` + `phone`, `email` или `identifier`: найти или создать контакт в указанном канале.

| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| `dialogId` / `contactId` | string | один из способов адресации | ID существующего контакта |
| `channelId` | string | да, при отправке по телефону, email или identifier | Канал, через который уходит сообщение |
| `phone` / `email` / `identifier` | string | один из способов адресации | Получатель: контакт ищется или создаётся в канале `channelId` |
| `text` | string | да, если нет `mediaUrl` | Текст сообщения |
| `subject` | string | нет | Тема письма, только для канала EMAIL |
| `mediaUrl` | string | нет | Ссылка на вложение (картинка, голосовое, видео, документ) |
| `mediaType` | string | нет | `photo`, `voice`, `audio`, `video`, `document` |
| `rawEmail` | boolean | нет | EMAIL: `true`, отправить письмо как есть, без обёртки и футера с отпиской |

> [!NOTE]
> Полная замена письма (`rawEmail: true`). По умолчанию для EMAIL TukTuk оборачивает тело в адаптивный шаблон и добавляет футер со ссылкой на отписку (List-Unsubscribe). С флагом `rawEmail: true` уходит ровно ваш HTML, ничего не добавляется. Вёрстка и ссылка на отписку тогда на вашей стороне (её требуют Gmail и Yahoo для массовых рассылок).

**Запрос**

**JavaScript**

```javascript
const res = await fetch("https://back.tuk-tuk.online/api/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    dialogId: "f1e2aaaa-1111-2222-3333-444455556666",
    text: "Ваш заказ готов к выдаче!",
  }),
});
console.log((await res.json()).data);
```

**PHP**

```php
$ch = curl_init("https://back.tuk-tuk.online/api/v1/messages");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer tuk_sk_ВАШ_КЛЮЧ",
    "Content-Type: application/json",
  ],
  CURLOPT_POSTFIELDS => json_encode([
    "dialogId" => "f1e2aaaa-1111-2222-3333-444455556666",
    "text" => "Ваш заказ готов к выдаче!",
  ]),
]);
$json = json_decode(curl_exec($ch), true);
print_r($json["data"]);
```

**Python**

```python
import requests
r = requests.post(
    "https://back.tuk-tuk.online/api/v1/messages",
    headers={"Authorization": "Bearer tuk_sk_ВАШ_КЛЮЧ"},
    json={
        "dialogId": "f1e2aaaa-1111-2222-3333-444455556666",
        "text": "Ваш заказ готов к выдаче!",
    },
)
print(r.json()["data"])
```

**Другие варианты тела** (меняется только JSON).

По номеру телефона, контакт создастся при необходимости:

```json
{ "channelId": "550e8400-...-099", "phone": "+79991234567", "text": "Код подтверждения: 1234" }
```

Голосовое сообщение:

```json
{ "dialogId": "f1e2aaaa-...", "mediaUrl": "https://example.com/note.ogg", "mediaType": "voice" }
```

Email через канал EMAIL:

```json
{ "channelId": "550e8400-...-0ee", "email": "ivan@example.com", "subject": "Ваш договор", "text": "Здравствуйте! Во вложении договор." }
```

**Ответ** (одинаковый по структуре для всех вариантов)

```json
{
  "data": {
    "id": "a1b2-new-msg",
    "dialogId": "f1e2aaaa-1111-2222-3333-444455556666",
    "direction": "OUTBOUND",
    "body": "Ваш заказ готов к выдаче!",
    "mediaUrl": null,
    "mediaType": null,
    "messageStatus": "SENT",
    "externalMessageId": "67890",
    "createdAt": "2026-05-29T12:00:00.000Z"
  }
}
```

Поле `messageStatus`: `SENT` (отправлено), `FAILED` (доставка не удалась). Отправка списывается с баланса так же, как ответ оператора, и идёт через тот же канал, что и в личном кабинете.

**Ошибки:** `400` (нет ни `text`, ни `mediaUrl`), `403` (нет scope `messages:send`), `404` (контакт не найден), `429`.

## Коды ошибок

Ошибки приходят с HTTP-кодом и телом вида `{ "statusCode": ..., "error": ..., "message": ... }`.

| Код | Когда возникает |
|---|---|
| `400` | Некорректный запрос (например, нет ни `text`, ни `mediaUrl`) |
| `403` | Нет ключа, неверный, отозван, истёк, IP не разрешён, не хватает scope |
| `404` | Ресурс не найден в этом рабочем пространстве |
| `429` | Превышен лимит запросов ключа в минуту |

Примеры тел ошибок:

```json
// 403: не хватает прав ключа
{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Недостаточно прав ключа. Требуется scope: messages:send"
}

// 404: контакт не найден
{ "statusCode": 404, "error": "Not Found", "message": "Контакт не найден" }

// 429: превышен лимит запросов
{ "error": { "code": "rate_limited", "message": "Превышен лимит запросов (120/мин) для этого API-ключа" } }
```

> [!WARNING]
> Тело ответа `429` отличается от остальных ошибок: вместо `statusCode` / `error` / `message` приходит объект `error` с полями `code` и `message`. Учитывайте это при разборе ошибок.

## Остальные разделы API

Кроме контактов, истории и отправки, ключ открывает ещё восемь ресурсов. Ниже краткая карта; полный список всех 96 адресов с правами лежит в [справочнике эндпоинтов](https://docs.tuk-tuk.online/api/reference), схемы запросов отдаёт `GET /api/v1/openapi.json`.

### Отложенная отправка

| Метод | Адрес | Право |
|---|---|---|
| POST | `/messages/schedule` | `messages:send` |
| GET | `/messages/scheduled` | `messages:send` |
| DELETE | `/messages/scheduled/:id` | `messages:send` |

Адресат указывается так же, как в обычной отправке. Дополнительно нужно поле `sendAt` в ISO-8601, минимум на 30 секунд вперёд.

```bash
curl -X POST https://back.tuk-tuk.online/api/v1/messages/schedule   -H "Authorization: Bearer $TUK_KEY" -H "Content-Type: application/json"   -d '{"dialogId":"f1e2...","text":"Напоминаю о встрече","sendAt":"2026-09-10T06:00:00Z"}'
```

### Контакты на запись

| Метод | Адрес | Право |
|---|---|---|
| GET | `/contacts` со списком фильтров | `contacts:read` |
| POST | `/contacts` | `contacts:write` |
| PATCH | `/contacts/:id` | `contacts:write` |
| POST | `/contacts/:id/notes` | `contacts:write` |

Фильтры списка: `search`, `tag`, `channelId`, `status`, `hasPhone`, `hasEmail`, `archived`, `includeClosed`, `limit`, `offset`. В `PATCH` можно передать `tags` и `customFields` целиком, сменить `status`, убрать диалог в архив и выставить `chainsBlocked`, чтобы связки не вмешивались в разговор.

### Файлы

| Метод | Адрес | Право |
|---|---|---|
| GET | `/files` | `media:read` |
| POST | `/files` | `files:write` |

`POST /files` с телом `{ "url": "https://..." }` забирает файл по ссылке в хранилище рабочего пространства и возвращает постоянный адрес. Ограничение 50 МБ, дальше используйте загрузку файлом на `POST /api/upload`.

### Рассылки

| Метод | Адрес | Право |
|---|---|---|
| GET POST | `/broadcasts` | `broadcasts:read` / `broadcasts:write` |
| POST | `/broadcasts/preview` | `broadcasts:read` |
| GET | `/broadcasts/audience/options` | `broadcasts:read` |
| PATCH DELETE | `/broadcasts/:id` | `broadcasts:write` |
| POST | `/broadcasts/:id/start`, `/pause`, `/resume`, `/cancel`, `/duplicate`, `/retry-failed` | `broadcasts:write` |
| GET | `/broadcasts/:id/recipients` | `broadcasts:read` |

Порядок такой: создать черновик, посчитать аудиторию через `preview`, запустить. Запуск пересобирает список получателей заново, поэтому он разрешён только для черновика и запланированной рассылки. Рассылку на паузе продолжают через `resume`, завершённую повторяют через `duplicate` с последующим запуском копии: иначе те, кому уже отправили, получат сообщение второй раз. Аудитория задаётся объектом `audience`: теги, статусы, каналы, активность за N дней либо явный список `dialogIds`. Темп задают `delayMinSec` и `delayMaxSec`, окно отправки, `windowFromMin` и `windowToMin`.

### Связки и воронки

| Метод | Адрес | Право |
|---|---|---|
| GET | `/chains/block-types` | `chains:read` |
| GET POST | `/chains` | `chains:read` / `chains:write` |
| GET PATCH DELETE | `/chains/:id` | по действию |
| POST | `/chains/:id/toggle`, `/clone` | `chains:write` |
| GET POST | `/funnels` и вложенные | `funnels:read` / `funnels:write` |
| GET POST DELETE | `/messengers/:id/stories`, `/archive`, `/summary`, `/sync` | `stories:read` / `stories:write` |

### ИИ-ассистенты

| Метод | Адрес | Право |
|---|---|---|
| GET | `/assistants`, `/assistants/:id` | `assistants:read` |
| PATCH | `/assistants/:id` | `assistants:write` |
| POST | `/assistants/:id/toggle` | `assistants:write` |
| POST | `/assistants/pause-dialog` | `assistants:write` |
| DELETE | `/assistants/pause-dialog/:dialogId` | `assistants:write` |

> [!TIP]
> Если внешняя система ведёт переписку сама, сначала поставьте ассистента на паузу в этом диалоге. Иначе клиент получит два ответа: ваш и ассистента.

### Задачи

`GET POST /tasks`, `GET PATCH DELETE /tasks/:id`. Права `tasks:read` и `tasks:write`. Поля: `title`, `description`, `status`, `priority`, `dialogId`, `assigneeId`, `dueDate`.

### Подписки на события

| Метод | Адрес | Право |
|---|---|---|
| GET | `/events/types` | `webhooks:manage` |
| GET POST | `/events/subscriptions` | `webhooks:manage` |
| GET PATCH DELETE | `/events/subscriptions/:id` | `webhooks:manage` |
| POST | `/events/subscriptions/:id/rotate-secret` | `webhooks:manage` |
| GET | `/events/subscriptions/:id/deliveries` | `webhooks:manage` |

Секрет подписи возвращается только при создании и ротации. Проверка подписи и список событий: [Event Webhooks](https://docs.tuk-tuk.online/api/webhooks).

### Состояние аккаунта

`GET /account` с правом `account:read` возвращает баланс, тариф, срок подписки, лимиты хранилища и расход сообщений. Поле `canSend` со значением `false` означает, что отправка не пройдёт, причина в `blockedReason`.

## Самоописание API

| Метод | Адрес | Что отдаёт |
|---|---|---|
| GET | `/manifest` | Список ресурсов, требуемые права, признак доступности для текущего ключа, соглашения и ссылки |
| GET | `/openapi.json` | Спецификация OpenAPI 3.1 |

Оба вызова требуют валидный ключ, но не требуют дополнительных прав. Это первое, что стоит вызвать внешней системе: дальше не нужно угадывать, что доступно.

## Идемпотентность

Отправка и постановка в расписание принимают заголовок `Idempotency-Key`. Повторный запрос с тем же значением вернёт результат первого, а не отправит второе сообщение. Это защита от ретрая при обрыве связи. Ключ живёт сутки.

```http
POST /api/v1/messages
Authorization: Bearer tuk_sk_...
Idempotency-Key: order-4821-confirm
```

## Безопасность

- Ключ хранится только в виде SHA-256 хэша. Восстановить его нельзя, поэтому копируйте сразу при создании.
- Ключ привязан к одному рабочему пространству и видит только его данные.
- Ключ можно ограничить по правам (scopes), сроку действия, IP и лимиту запросов в минуту.
- Отозванный ключ перестаёт работать немедленно.

Уведомления о событиях в обратную сторону (новое сообщение, смена статуса диалога) приходят через [Event Webhooks](https://docs.tuk-tuk.online/api/webhooks).
