# Voicyfy Agent API — создание и настройка автономных агентов

API для программного управления автономными агентами обзвона Voicyfy
(страница `https://voicyfy.ru/static/agent.html`) и каскад-ассистентами
(страница `https://voicyfy.ru/static/voice-assistants.html`). Предназначено
для интеграций вроде Claude Code: агент или ассистент создаётся и
настраивается HTTP-запросами.

Документ также содержит полный справочник возможностей агента (инструменты
оркестратора, функции голосового агента, каналы, автоматика, тарифы) — чтобы
писать профессиональные промпты и отвечать пользователям на вопросы «а умеет
ли агент X» без домыслов.

**Base URL:** `https://voicyfy.ru`

---

## Шпаргалка: что умеет агент (одним экраном)

- **Звонит и принимает звонки**: голосовой агент говорит в живом разговоре,
  оркестратор перед звонком готовит стратегию, после — анализирует и
  действует сам (память, стадия воронки, следующий шаг).
- **Ведёт CRM**: контакты, воронка `new → active → success / rejected /
  do_not_call`, память о каждом клиенте, поиск и массовые действия по
  фильтрам (стадия, компания, попытки, «не звонили N дней», «клиент молчит
  N дней» и др.).
- **Планирует касания**: звонки с учётом рабочих часов, отложенные сообщения
  в Telegram и MAX, **проверку ответа** («через 24 ч посмотри, ответил ли
  клиент; если молчит — реши, что делать»).
- **Пишет клиентам**: SMS, личный Telegram и MAX владельца (в т.ч. отвечает
  на входящие), массовая рассылка по фильтру.
- **Делает документы**: PDF (КП, памятка, итоги), таблицы Excel, выгрузка
  контактов по условию — и отправляет их файлом клиенту или владельцу.
- **Сообщает владельцу**: уведомления в Telegram-бот (с файлом), вебхук во
  внешние системы.
- **Помнит правила**: собственная память агента (поручения владельца,
  наблюдения, планы) — владелец задаёт её в чате, в кабинете или по API.
- **Знает материалы**: база знаний (оркестратор и голосовой агент ищут по
  ней), Google Calendar и Gmail через коннекторы.

По API-ключу доступно: создание / чтение / правка агентов, справочник
моделей, **заметки памяти агента**, CRUD каскад-ассистентов. Всё остальное
(номера, база знаний, подключения, контакты, оплата) — в кабинете.

---

## Аутентификация

Все запросы передают персональный API-ключ в заголовке:

```
X-Api-Key: vfy_...
```

Ключ генерируется в кабинете: **Настройки → API-ключ для интеграций**
(`https://voicyfy.ru/static/settings.html`). Ключ показывается один раз при
генерации; при перевыпуске старый ключ мгновенно перестаёт работать.

Ошибки авторизации: `401 {"detail": "invalid_api_key"}`.

Доступные по ключу эндпоинты — только перечисленные ниже: агенты (создание,
редактирование, чтение + справочник моделей), заметки памяти агента и
каскад-ассистенты (CRUD + баланс кредитов каскада). Всё остальное API
Voicyfy по ключу недоступно.

---

## Важно: как устроен агент (прочитай перед настройкой)

У каждого агента ДВА независимых AI-компонента, и у каждого — свой скрытый
базовый системный промпт, который через API **не редактируется**:

1. **Оркестратор** — текстовый «мозг»: ведёт CRM, планирует звонки, готовит
   стратегию каждого звонка и анализирует результат. Его промпт собирается из:
   базовый шаблон + 5 документов компании (`doc_*`) + поле
   `additional_instructions` (секция «Дополнительные инструкции от владельца»)
   + **память агента** (заметки, см. ниже).
2. **Голосовой агент** — говорит в живом телефонном разговоре. Его промпт:
   - исходящий звонок: базовый шаблон + поле `voice_additional_instructions`
     (дописывается отдельной секцией);
   - входящий звонок на номер агента: `voice_additional_instructions` + правила
     вызова функций (send_sms, hangup_call), без остального шаблона, + карточка звонящего, если он есть в контактах агента.
     Поэтому для входящих пиши в поле всё, что голос должен знать: кто он,
     какая компания, как отвечать. Пустое поле — используется шаблон.
   Документы `doc_*` и память агента в живом разговоре НЕ используются.

### Что писать в какое поле

| Поле | Куда попадает | Что писать |
|------|---------------|------------|
| `doc_who_am_i` | оркестратор | Кто компания: название, город, чем занимается |
| `doc_who_we_call` | оркестратор | Целевая аудитория: кому звоним и зачем |
| `doc_how_we_talk` | оркестратор | Стиль общения бренда |
| `doc_what_we_offer` | оркестратор | Продукты/услуги, цены, условия |
| `doc_rules_and_goals` | оркестратор | Цели, KPI, ограничения |
| `additional_instructions` | оркестратор | Правила планирования и работы с контактами, которые не вписываются в документы (например «не звонить чаще 1 раза в 3 дня») |
| `voice_additional_instructions` | голосовой агент | Поведение именно в живом разговоре: имя ассистента, манера речи, запретные темы, обработка возражений |
| `inbound_first_phrase` | голосовой агент | Приветствие ТОЛЬКО для входящих звонков (до 500 символов) |
| Память агента (API `/api/agent/memory`) | оркестратор | Короткие отдельные правила владельца, которые удобно добавлять и убирать по одному (см. «Память агента») |

### Первая фраза

- **Исходящие звонки:** первую фразу генерирует оркестратор индивидуально под
  каждый звонок (стратегия PreCall) — статически она не настраивается.
  Влиять на неё можно через `doc_*`, `additional_instructions` и память агента.
- **Входящие звонки:** статическое поле `inbound_first_phrase`.

---

## Жизненный цикл звонка (PreCall → звонок → PostCall)

Каждый исходящий звонок агента проходит три фазы. Понимание этого цикла —
ключ к правильным промптам.

**1. PreCall (оркестратор, без инструментов).** Перед набором номера
оркестратор получает: задачу (название + заметки), карточку контакта, память
о контакте, полный транскрипт последнего звонка и единую хронологию всего
общения по всем каналам (звонки + SMS + Telegram + MAX). На выходе —
JSON-стратегия: `first_phrase` (точная первая фраза), `call_strategy`
(тактика), `tone` (тон), `key_points` (ключевые факты). Первая фраза и
стратегия передаются голосовому агенту.

**2. Живой разговор (голосовой агент).** Говорит по стратегии PreCall,
которая приходит ему как первое сообщение и контекст звонка. Документов
`doc_*` и CRM-доступа у него нет — вся нужная фактура должна быть в стратегии
или в базе знаний (см. функции голосового агента ниже).

**3. PostCall (оркестратор, с инструментами).** После звонка оркестратор
получает транскрипт + всю предысторию и выполняет действия через инструменты.
Обязательные правила его базового промпта:

- **Память о контакте обновляется ВСЕГДА** (`update_contact_memory`): краткий
  итог, новые факты, лучшее время для звонка, тон разговора.
- **Стадия воронки меняется только при реальном основании**
  (`move_contact_stage`): цель достигнута → `success`, явный отказ →
  `rejected`, «не звоните больше» → `do_not_call`, первый живой контакт →
  `active`. «Клиент думает» — стадия не меняется.
- **Исходящий звонок: следующее касание планируется ВСЕГДА**, кроме случая
  когда цель уже достигнута. Не дозвонились → перезвон через 24 часа;
  дозвонились, но цель не достигнута → перезвон через 1–3 дня; либо
  отложенное сообщение в Telegram/MAX, если это уместнее звонка. Если агент
  написал клиенту и ждёт реакции — может поставить **проверку ответа**.
- **Входящий звонок:** перезвон планируется только если это нужно по сути
  разговора (сам факт входящего — не повод для авто-перезвона).
- По итогам агент может подготовить **PDF** (например КП) и отправить его
  клиенту в мессенджер или ссылкой в SMS.
- При важных событиях (горячий лид, жалоба, просьба позвать человека) —
  уведомление владельцу в Telegram (`send_telegram_notification`, можно с
  файлом).
- Если из звонка следует общее правило или наблюдение (не про этого
  клиента) — агент записывает его в свою память (`update_agent_memory`).

Итог звонка: `SUCCESS` / `REJECTED` / `DO_NOT_CALL` — только когда агент сам
поставил эту стадию (`move_contact_stage`); `FOLLOWUP` — запланирован следующий
шаг; `ANSWERED` — разговор без итога (контакт уходит «В работу»); `NO_ANSWER`;
`ERROR` — разбор не удался (стадия не меняется, владельцу приходит уведомление).
Входящие звонки и сообщения клиента попыткой дозвона не считаются.

### Стадии воронки

`new` (новый) → `active` (в работе) → `success` (цель достигнута) /
`rejected` (отказ) / `do_not_call` (просил не звонить). Воронка видна
владельцу на канбан-доске на странице агента. Контакты `do_not_call` агент
никогда не обзванивает и не включает в рассылки.

### Память о контакте

У каждого контакта — накапливаемая память агента: `summary` (сводка),
`key_facts` (факты), `best_time` (лучшее время звонка), `tone` (история
тона). Память автоматически попадает в контекст каждого следующего PreCall,
PostCall и чата — агент «помнит» клиента между звонками.

### Память агента

Отдельно от памяти о контактах у агента есть **собственная память** —
блокнот из коротких заметок в трёх разделах:

| Раздел | Кто пишет | Что там |
|--------|-----------|---------|
| `instructions` | владелец (в чате, в кабинете или по API) | Поручения и правила на будущее: «по пятницам не звонить», «КП отправляй только в PDF» |
| `observations` | агент | Наблюдения о работе: «аргумент про рассрочку работает лучше скидки» |
| `plans` | агент | Намерения, не привязанные к конкретному клиенту |

Память приклеивается к каждому запросу оркестратора (PreCall, PostCall, чат,
входящие), поручения владельца для него обязательны. Правки только
точечные: добавить / изменить / удалить заметку по id. Лимиты: 60 заметок,
400 символов на заметку, 8000 символов всего. Управление по API — раздел
«Эндпоинты → Память агента».

**Память или `additional_instructions`?** В `additional_instructions` —
цельный постоянный регламент. В память — отдельные правила, которые
добавляются и отменяются по одному (сезонные, временные, уточнения по ходу
работы). Агент сам дописывает туда правила, которые владелец даёт ему в чате.

---

## Инструменты оркестратора (чат с владельцем)

Владелец общается с оркестратором в чате на странице агента и (если
подключён бот) в Telegram. Оркестратор выполняет действия этими
инструментами. В «Условие» указано, когда инструмент доступен;
пустое условие = доступен всегда.

### CRM: контакты

| Инструмент | Что делает | Условие |
|------------|-----------|---------|
| `create_agent_contact` | Создать контакт (телефон обязателен) | |
| `bulk_create_contacts` | Создать несколько контактов разом, дубли по номеру пропускаются | |
| `find_contact` | Найти конкретного человека по имени/телефону/компании: любой порядок слов, ё/е, телефон в любом формате; нет точного совпадения — похожие (`did_you_mean`: падеж, опечатка) | |
| `search_contacts` | Поиск и подсчёт по фильтру (см. «Фильтр контактов»), постранично, `count_only` — только число | |
| `get_agent_contacts` | Свежий общий список контактов, постранично | |
| `get_contact_details` | Полная карточка: поля, стадия, заметки, память, попытки, сводка звонков | |
| `get_contacts_by_stage` | Разбивка контактов по стадиям воронки (счётчики + примеры) | |
| `update_contact_info` | Перезаписать имя/компанию/должность/заметки | |
| `append_contact_note` | Дописать заметку с датой, не стирая старые | |
| `move_contact_stage` | Перевести контакт на стадию воронки | |
| `bulk_move_contacts_stage` | Перевести группу контактов по фильтру (перевод в `do_not_call` отменяет их задачи) | |
| `snooze_contact` | Пауза до даты: отменяет запланированные звонки, новые сдвигаются на конец паузы | |
| `delete_agent_contact` | Удалить контакт (необратимо, только по явной просьбе) | |

### Фильтр контактов

Один и тот же фильтр понимают `search_contacts`, все массовые действия и
выгрузка в Excel. Поля (любые сочетания, условия складываются через «И»):

| Поле | Смысл |
|------|-------|
| `query` | Имя, телефон или компания: слова в любом порядке (каждое — в имени или компании), ё = е, телефон сравнивается по цифрам (+7/8, скобки, пробелы не важны). Нет совпадений — в ответе `did_you_mean` с похожими |
| `stage` / `stages` | Стадия / любая из стадий воронки |
| `company` | Подстрока компании |
| `attempts_min` / `attempts_max` | Попыток звонка не меньше / не больше N |
| `never_called` / `called_at_least_once` | Ни разу не звонили / звонили хотя бы раз |
| `not_called_days` / `called_within_days` | Последний звонок N+ дней назад / звонили за последние N дней |
| `no_reply_days` | **Клиент молчит N+ дней**: агент выходил на связь N и более дней назад, а клиент за последние N дней ни разу не ответил ни в одном канале |
| `created_after` / `created_before` | Добавлен в базу после / до даты |
| `has_scheduled_call` / `no_scheduled_call` | Есть / нет запланированного звонка |

В массовых действиях дополнительно: `agent_contact_ids` (явный список) и
`all_contacts: true` (вся база — только по явной просьбе владельца; пустой
фильтр в массовых действиях запрещён). Перед действием над большой группой
агент сначала делает `dry_run` и называет число.

### Планирование звонков и касаний

| Инструмент | Что делает | Условие |
|------------|-----------|---------|
| `create_agent_task` | Запланировать звонок (абсолютное время или «через N минут»); сдвигается в рабочие часы | |
| `update_agent_task` | Перенести/переименовать запланированную задачу | |
| `delete_agent_task` | Отменить запланированную задачу (необратимо) | |
| `bulk_schedule_calls` | Обзвон группы по фильтру с интервалом между звонками; `do_not_call` и уже запланированных пропускает | |
| `bulk_cancel_calls` | Отменить задачи группы по фильтру: звонки, сообщения, проверки ответа или все | |
| `trigger_immediate_call` | Позвонить прямо сейчас (только по явной просьбе); ночью — в начале рабочих часов | |
| `schedule_reply_check` | **Проверка ответа**: в назначенное время система смотрит, выходил ли клиент на связь (Telegram, MAX, SMS, звонок). Ответил — проверка закрывается сама; молчит — агент решает, что делать дальше | |
| `get_agent_tasks` | Список задач с фильтрами (также проверка дублей перед созданием) | |
| `get_upcoming_schedule` | Календарь ближайших задач по всем контактам | |

**Как работает проверка ответа.** Агент (сам после звонка или сообщения, или
по просьбе владельца) ставит проверку «через N часов» с заметкой, какого
ответа ждёт и что планирует при молчании. Проверку не нужно отменять вручную:
если клиент ответит раньше, она закроется сама, модель не запускается и
кредиты не тратятся. Если клиент молчит — агента будят, и он по своему
промпту и памяти выбирает шаг: позвонить, написать в другой канал, поставить
новую проверку, сменить стадию, сообщить владельцу или ничего. У контакта
одновременно ждёт одна проверка — новая заменяет прежнюю. Ночью (вне рабочих
часов агента) проверка ждёт утра, как и звонки с сообщениями.

### Документы и таблицы

| Инструмент | Что делает | Условие |
|------------|-----------|---------|
| `create_pdf_document` | PDF (КП, памятка, итоги разговора, отчёт). Текст агент пишет сам простой разметкой: заголовки, списки, таблицы, жирный | |
| `create_spreadsheet` | Таблица Excel из данных агента: прайс, сравнение, отчёт | |
| `export_contacts_table` | Выгрузка контактов в Excel: вся база или по фильтру; по желанию — лист «Звонки» с транскриптами | только чат |
| `get_agent_files` | Список уже созданных файлов, чтобы отправить повторно, а не делать заново | |

Файл получает `file_id` и ссылку `url` (открывается без входа в кабинет,
ссылка содержит секретный токен). Отправка: вложением —
`telegram_send_message` / `max_send_message` / `send_telegram_notification`
с `file_id`; ссылкой — в SMS или в ответе в чате. Лимиты: файл до 5 МБ,
выгрузка до 10 000 контактов. Цены и условия в документы агент берёт только
из документов компании, базы знаний и инструкций — пропиши их там.

### Аналитика и история

| Инструмент | Что делает | Условие |
|------------|-----------|---------|
| `get_agent_stats` | Сводная статистика: контакты, звонки, задачи | |
| `get_period_report` | Отчёт за период: дозвоны, успехи, конверсия, длительность | |
| `get_failed_calls` | Очередь недозвонов на перезвон | |
| `get_contact_call_history` | История звонков контакта (кратко) | |
| `get_contact_timeline` | Единая хронология по всем каналам: звонки + SMS + Telegram + MAX | |
| `get_call_transcript` | Полный транскрипт конкретного звонка | |

### Каналы и внешние системы

| Инструмент | Что делает | Условие |
|------------|-----------|---------|
| `send_sms` | SMS клиенту с номера агента (до 500 символов) | |
| `send_telegram_notification` | Уведомление ВЛАДЕЛЬЦУ в Telegram-бот агента, можно с файлом (`file_id`) | подключён бот |
| `send_webhook` | Событие (event + payload) на вебхук владельца; URL подставляется сервером | задан `webhook_url` |
| `search_knowledge_base` | Поиск по базе знаний агента | создана база знаний |
| `telegram_send_message` | Сообщение КЛИЕНТУ с личного Telegram-аккаунта владельца, можно с файлом | подключён личный Telegram |
| `telegram_get_thread` | Прочитать переписку с клиентом в личном Telegram | подключён личный Telegram |
| `schedule_telegram_message` | Отложенное сообщение в Telegram: передаётся ИНСТРУКЦИЯ, текст составится в момент отправки с учётом свежего контекста | подключён личный Telegram |
| `max_send_message` / `max_get_thread` / `schedule_max_message` | То же для мессенджера MAX | подключён личный MAX |
| `bulk_schedule_messages` | Рассылка группе по фильтру в Telegram или MAX: каждому клиенту текст составляется отдельно по инструкции. До 200 контактов за раз; контактам без переписки — не чаще раза в 12 минут (антиспам мессенджеров) | только чат; подключён Telegram или MAX |
| `update_agent_memory` | Добавить / исправить / удалить заметки своей памяти | |
| Google Calendar (Composio) | Создать/найти/изменить/удалить событие, найти свободные слоты | подключён коннектор |
| Gmail (Composio) | Отправить/прочитать/ответить/переслать письмо | подключён коннектор |

### Инструменты PostCall-анализа

После звонка (и при обработке входящих сообщений, отложенных отправок,
проверок ответа) оркестратору доступен сокращённый набор:
`update_contact_memory` (вызывается всегда), `create_agent_task`,
`move_contact_stage`, `update_contact_info`, `send_telegram_notification`,
`search_knowledge_base`, `send_sms`, `send_webhook`, `update_agent_memory`,
`schedule_reply_check`, `create_pdf_document`, `create_spreadsheet`, плюс
условные инструменты личного Telegram / MAX и коннекторов. Массовых действий,
удаления, выгрузки базы и списка файлов (`get_agent_files`) в PostCall нет.
В разбор попадает текст клиента, поэтому все инструменты здесь привязаны к
контакту события: SMS и сообщения уходят только ему (явный номер или username
игнорируется), вложением — только файл, созданный для него, правила владельца
в памяти агента (секция `instructions`) не меняются.

---

## Функции голосового агента (в живом звонке)

Набор голосового агента намеренно маленький — он говорит, а не ведёт CRM:

| Функция | Что делает | Условие |
|---------|-----------|---------|
| `hangup_call` | Завершить звонок с прощальной фразой (разговор окончен / клиент просит) | |
| `send_sms` | Отправить клиенту SMS прямо во время звонка (адрес, ссылка, код, реквизиты) | |
| `search_pinecone` | Поиск по базе знаний во время разговора | создана база знаний |
| `google_calendar_create_event` / `google_calendar_find_events` | Записать на встречу / посмотреть календарь владельца | подключён Google Calendar |
| `gmail_send_email` / `gmail_fetch_emails` | Отправить/прочитать письмо с Gmail владельца | подключён Gmail |

Набор одинаков для всех голосовых провайдеров.

**Чего у голосового агента НЕТ** (важно для промптов):

- нет CRM-инструментов (контакты, стадии, заметки) — всё это делает PostCall
  автоматически после звонка;
- нет планирования задач и проверок ответа — если клиент просит перезвонить,
  голосовой агент просто уточняет время, а перезвон ставит PostCall;
- нет PDF и таблиц — пообещать «пришлю КП» можно, отправит его PostCall;
- нет личного Telegram / MAX и вебхуков;
- нет документов `doc_*` и памяти агента — фактура приходит только через
  стратегию PreCall и базу знаний.

Поэтому в `voice_additional_instructions` НЕ нужно писать «запиши в CRM»,
«поставь задачу», «обнови стадию», «отправь PDF» — это произойдёт само. Пиши
только про поведение в разговоре.

---

## Каналы и автоматика вне звонков

- **Входящие звонки.** Клиент звонит на номер агента → голосовой агент
  отвечает (`inbound_first_phrase`), после разговора выполняется тот же
  PostCall-анализ (память, стадия, уведомления; перезвон — только при
  необходимости).
- **Входящие SMS.** SMS от клиента на номер агента обрабатывает оркестратор:
  может ответить SMS, запланировать звонок, обновить память/стадию,
  уведомить владельца.
- **Входящие в Telegram и MAX** (если подключён личный аккаунт и включён
  автоответ): оркестратор отвечает клиенту как живой человек, планирует
  звонки/отложенные сообщения, ведёт память. Любое входящее сообщение сразу
  закрывает ожидающие проверки ответа этого клиента.
- **Отложенные сообщения** (Telegram/MAX) живут в общем списке задач
  (`channel="telegram"` / `"max"`). В момент отправки оркестратор сверяется
  с актуальным контекстом: если договорённость уже закрыта — сообщение не
  отправляется, память обновляется с пометкой.
- **Проверки ответа** — задачи `channel="reply_check"` в том же списке; в
  календаре и истории помечены «Проверка ответа».
- **Приём заявок с сайта / CRM** (публичный канал). Включается в кабинете на
  странице агента: там же выдаётся отдельный ключ канала и адрес
  `POST https://voicyfy.ru/api/agent/public/{agent_id}/message`. Внешняя
  система шлёт туда JSON или текст (заявка с формы, лид из CRM) с ключом в
  `X-Api-Key`; оркестратор оформляет заявку узким набором инструментов:
  создать или найти контакт, дополнить его, поставить задачу или звонок,
  уведомить владельца, вебхук, база знаний. Удаления, массовых действий,
  выгрузок и рассылок в этом канале нет — текст пишет посторонний. Ответ —
  `{"reply": "...", "timestamp": "..."}`. Это ключ канала агента, а не
  персональный `vfy_`-ключ.
- **Рабочие часы** (`working_hours_start`/`working_hours_end`, МСК, по
  умолчанию 9–21; `start == end` — круглосуточно). Вне окна клиенту не звонят
  и не пишут: задачи (звонки, сообщения Telegram/MAX, проверки ответа,
  `trigger_immediate_call`) переносятся на утро — массовые с сохранением
  интервала. Ответ на входящее сообщение клиента уходит сразу. Контакту в
  стадии «Не звонить» ничего не планируется, перевод в неё отменяет
  запланированное.
- **Вебхук** (`webhook_url`). Оркестратор отправляет события (заявка, лид,
  бронирование, результат звонка) POST-запросом на URL владельца — для
  интеграций с n8n / Make / Zapier / CRM.
- **Планировщик.** Фоновый планировщик проверяет задачи каждые ~30 секунд —
  «позвони сейчас» означает старт в пределах полуминуты. За проход уходит до
  20 задач (по 5 одновременно), по одной на контакт; большая очередь
  растягивается, а не уходит лавиной. Без денег на кошельке звонки агента
  отменяются, владельцу приходит уведомление.

---

## Гайд: как писать промпты для агента

### `additional_instructions` (оркестратор)

Пиши правила планирования, работы с базой и приоритеты — в терминах реальных
инструментов, стадий и каналов из таблиц выше.

Хорошо:
- «Не звонить одному контакту чаще 1 раза в 3 дня».
- «После двух недозвонов подряд отправляй SMS с предложением перезвонить».
- «Отказников (`rejected`) не трогать 3 месяца, потом можно один звонок».
- «Горячих клиентов (просят счёт) — сразу уведомление владельцу».
- «После отправки КП ставь проверку ответа через 24 часа; если молчит —
  один звонок, после второго молчания переводи в `rejected`».
- «КП отправляй PDF-файлом в Telegram, если есть переписка, иначе ссылкой в
  SMS».

Не нужно (уже есть в базовом промпте): «отвечай по-русски», «проверяй дубли
задач перед созданием», «показывай время по МСК», «сначала читай данные,
потом действуй», «не выдумывай контакты и статистику», «подтверждай
результат после действия», «обновляй память о клиенте после звонка».

### `voice_additional_instructions` (голосовой агент)

Пиши только про поведение в живом разговоре: имя, манера речи, запретные
темы, обработка возражений, когда завершать звонок.

Хорошо:
- «Твоё имя — Алёна. Говори коротко, тепло, без канцелярита».
- «Не называй цены на монтаж — предлагай замер».
- «На возражение „дорого“ — расскажи про рассрочку».
- «Если просят прислать предложение — скажи, что пришлёшь в Telegram или
  SMS сразу после звонка».
- «Если просят человека — скажи, что менеджер перезвонит, и завершай звонок».

Не нужно: «запиши результат в CRM», «поставь задачу на перезвон», «обнови
стадию», «отправь PDF» (делает PostCall автоматически); «говори по-русски»,
«не затягивай разговор», «вызови hangup_call в конце» (уже в базовом
промпте).

### Куда класть фактуру о компании

- Короткая (умещается в документы) → `doc_what_we_offer` и остальные `doc_*`.
  Помни: в живой разговор она попадает только через стратегию PreCall —
  сжато.
- Объёмная (каталог, прайс, регламенты) → база знаний агента: её ищут и
  оркестратор (`search_knowledge_base`), и голосовой агент в звонке
  (`search_pinecone`). Заполняется владельцем в кабинете на странице агента
  (текстом, до 200 000 символов); по API не загружается.
- Отдельные правила «на сейчас» → память агента (раздел `instructions`).

### Рецепты: готовые связки для типовых сценариев

**Дожим после коммерческого предложения**
- `additional_instructions`: «Если клиент интересуется — после звонка
  подготовь КП в PDF и отправь в Telegram (если нет переписки — ссылкой в
  SMS). Поставь проверку ответа через 24 часа. Если молчит — один звонок с
  вопросом, получил ли КП. Ещё неделя тишины — переводи в `rejected`».
- `voice_additional_instructions`: «Если клиент просит подробности —
  пообещай прислать предложение сразу после звонка».
- Цены и условия КП — в `doc_what_we_offer` или базе знаний.

**Реактивация «молчунов»**
- Владелец в чате: «Сколько клиентов молчит больше двух недель?» →
  `search_contacts` с `no_reply_days`. «Напиши им в Telegram, напомни про
  акцию» → `bulk_schedule_messages` (агент сначала покажет число и сроки).
- В `additional_instructions`: «Ответивших на рассылку — сразу в работу:
  звонок в течение дня».

**Горячий лид — сразу владельцу**
- `additional_instructions`: «Если клиент просит счёт или готов купить —
  уведоми владельца в Telegram с именем, телефоном и сутью; приложи итоги
  разговора в PDF».

**Заявки с сайта**
- Включить публичный канал в кабинете, подключить форму сайта.
- `additional_instructions`: «Заявку с сайта — создай контакт и позвони в
  течение 5 минут в рабочее время; вне рабочего времени — напиши в Telegram и
  позвони утром».

**Регулярная отчётность**
- Владелец в чате: «Сделай таблицу по воронке» → `export_contacts_table`;
  «Отчёт за неделю в PDF и пришли мне в Telegram» → `get_period_report` +
  `create_pdf_document` + `send_telegram_notification` с файлом.

### Частые ошибки в настройке

- **Правила про звонок — в голосовой промпт.** «Перезвони через день» в
  `voice_additional_instructions` не работает: у голосового агента нет
  планирования. Такие правила — в `additional_instructions` или память.
- **Обещания без фактуры.** «Отправляй КП с ценами», а цен нет ни в `doc_*`,
  ни в базе знаний — агент их не выдумает и КП выйдет пустым.
- **Слишком частые касания.** Правило «звонить каждый час до ответа» упрётся
  в здравый смысл клиента и лимиты мессенджеров. Используй проверку ответа
  с разумным сроком.
- **Массовые действия без явного запроса.** Рассылки и обзвон по всей базе
  агент делает только по прямой просьбе владельца и после подсчёта
  (`dry_run`) — не пиши в инструкциях «каждый день пиши всем».
- **Противоречащие правила.** Одно правило в `additional_instructions` и
  противоположное в памяти агента — агент получает оба и может выбрать любое.
  Меняя правило, убери или исправь старую формулировку там, где она была.
- **Лишние условия в выгрузке.** «Таблица всех контактов» — это вся база
  без фильтров; условия добавляй, только если они реально нужны.

---

## Тарифы, кредиты и подключения (контекст для ответов пользователям)

Через API это не настраивается — всё делается в кабинете, но знать полезно:

- **Доступ к агенту**: тестовый период 3 дня (активируется при создании
  первого агента, единожды; начисляется 1500 кредитов) или тариф **profi**
  (даёт 10 000 кредитов). Без доступа — `402 subscription_required`.
- **Кредиты оркестратора** — внутренняя валюта «мозга» агента: списываются за
  токены каждого вызова (PreCall, PostCall, чат, входящие, отложенные
  сообщения, проверки ответа по молчащему клиенту, каждое сообщение
  массовой рассылки) по ставкам выбранной модели `orchestrator_model`. У
  каждой модели в справочнике есть `credits_per_call` — оценка стоимости
  типичного звонка. Баланс ниже 100 кредитов — оркестратор не запускается
  (`402 insufficient_credits`). Пакеты пополнения — в кабинете на странице
  агента.
- **Голосовая часть** оплачивается отдельно от кредитов:
  - Если в профиле указан **свой ключ** провайдера (OpenAI, Gemini, Yandex,
    Cartesia) — минуты голоса пользователь оплачивает у провайдера сам,
    Voicyfy за них не списывает.
  - Если своего ключа нет — работает **серверный ключ Voicyfy**, минуты
    списываются с **кошелька** (рубли, посекундно, минимум 10 секунд за
    сессию, тарифы по моделям). Кошелёк пополняется в кабинете; при нуле
    новые сессии не начинаются.
  - **Fish Audio** работает только на серверном ключе — всегда по тарифу из
    кошелька.
  - Звонки OpenAI (GPT-Live) на серверном ключе списываются по тарифу
    **OpenAI Live** (`openai-live`) за всё время голосовой сессии: у исходящих
    сюда входят гудки, в том числе когда клиент не ответил.
  - **Каскад** работает на внутренних ресурсах платформы, свои ключи не
    нужны.
  - Телефония (номера, минуты, SMS) — Voximplant, подключается в кабинете.
- **Telegram-бот агента** — подключается в кабинете (токен бота): уведомления
  владельцу (в т.ч. с файлами) + полноценный чат с оркестратором прямо в
  Telegram.
- **Личный Telegram и личный MAX** — подключаются в кабинете на странице
  агента: агент пишет клиентам от имени владельца, читает и (при включённом
  автоответе) отвечает на входящие. Встроены анти-бан лимиты: ~30 исходящих
  в час и не больше 5 новых диалогов по номеру телефона в час на аккаунт.
- **Коннекторы** (Google Calendar, Gmail) — OAuth-подключение в кабинете,
  раздел «Коннекторы» на странице агента.
- **База знаний** — заполняется в кабинете на странице агента; флаг
  `has_knowledge_base` в объекте агента показывает её наличие.
- **Лимит**: до 3 агентов на аккаунт; у каждого агента свои контакты, задачи,
  память, файлы и подключения. Это отдельный счётчик: голосовой ассистент,
  который создаётся вместе с агентом, **не занимает** лимит ассистентов
  тарифа (start — 5, profi — 10).

---

## Эндпоинты

### 1. GET /api/agent/list — список агентов

```bash
curl -H "X-Api-Key: vfy_..." https://voicyfy.ru/api/agent/list
```

Ответ:

```json
{
  "total": 1,
  "max_agents": 3,
  "can_create_more": true,
  "has_agent_access": true,
  "agents": [ { ...объект агента, см. ниже... } ]
}
```

Отсюда берётся `agent_id` (поле `id`) для остальных запросов.

### 2. GET /api/agent/?agent_id={id} — один агент

```bash
curl -H "X-Api-Key: vfy_..." "https://voicyfy.ru/api/agent/?agent_id=<uuid>"
```

Без `agent_id` возвращается первый (самый старый) агент пользователя.
Перед любым редактированием сначала прочитай текущее состояние этим запросом.

### 3. GET /api/agent/orchestrator-models — доступные модели оркестратора

```bash
curl -H "X-Api-Key: vfy_..." https://voicyfy.ru/api/agent/orchestrator-models
```

Ответ: `{"models": [...], "default": "<slug>"}`. У каждой модели: `slug`,
`name`, `description`, `is_recommended` (рекомендуемая по умолчанию),
`credits_per_call` (оценка кредитов за типичный звонок) и ставки за 1k
токенов. В `orchestrator_model` передавай только `slug` из этого списка.
Если не уверен — не передавай поле вовсе: применится модель по умолчанию.

### 4. POST /api/agent/create — создать агента

```bash
curl -X POST https://voicyfy.ru/api/agent/create \
  -H "X-Api-Key: vfy_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Алёна",
    "assistant_type": "fish",
    "doc_who_am_i": "Компания «АкваЛето», Иркутск. Продаём надувные бассейны...",
    "doc_who_we_call": "Частные домовладельцы, дачники...",
    "doc_how_we_talk": "Дружелюбно, на «вы», без канцелярита...",
    "doc_what_we_offer": "Бассейны Intex и Bestway от 15 000 ₽...",
    "doc_rules_and_goals": "Цель — записать на замер или выставить счёт...",
    "additional_instructions": "Не звонить одному контакту чаще раза в 3 дня.",
    "voice_additional_instructions": "Твоё имя — Алёна. Говори коротко, тепло.",
    "inbound_first_phrase": "Здравствуйте! Компания АкваЛето, меня зовут Алёна. Чем могу помочь?",
    "working_hours_start": 9,
    "working_hours_end": 21
  }'
```

Поля запроса:

| Поле | Тип | Обяз. | Описание |
|------|-----|-------|----------|
| `name` | string | да | Имя агента (1–255 символов) |
| `assistant_type` | string | да | Провайдер голоса: `fish` \| `openai` \| `gemini` \| `yandex` \| `cascade` \| `cartesia` (последний скрыт из витрины кабинета, но работает) |
| `doc_who_am_i` … `doc_rules_and_goals` | string | да (все 5) | Документы компании для оркестратора |
| `additional_instructions` | string | нет | Доп. инструкции оркестратора |
| `voice_additional_instructions` | string | нет | Доп. инструкции голосового агента |
| `inbound_first_phrase` | string | нет | Приветствие входящих (≤500 символов) |
| `working_hours_start` / `working_hours_end` | int 0–23 | нет | Рабочие часы для звонков (МСК), по умолчанию 9–21 |
| `orchestrator_model` | string | нет | Slug из `/orchestrator-models`; по умолчанию — дефолтная |
| `voice` | string | нет | Имя голоса для cascade/gemini/openai/yandex (см. списки ниже) |
| `fish_voice_id` | string | нет | Голос Fish Audio: id из списка ниже или свой `reference_id` с fish.audio; пусто — голос по умолчанию |
| `fish_model` | string | нет | Модель Fish: `s2.1-pro` (по умолчанию), `s2-pro`, `s1`, `s2.1-pro-free` |
| `fish_latency` | string | нет | Режим задержки Fish: `low` \| `balanced` (по умолчанию) \| `normal` |
| `cartesia_voice_id` | string | нет | ID голоса Cartesia (только для `cartesia`) |
| `voice_speed` | float 0.5–1.5 | нет | Скорость речи (Cartesia и Fish) |

Успех: `200` с объектом агента (+ `trial_activated`). Ответ содержит `id` —
сохрани его как `agent_id`.

Предусловия (иначе `400`/`402` — см. «Ошибки»): у пользователя верифицирована
телефония Voximplant, активен тариф profi или доступен триал, меньше 3
агентов. Свой ключ голосового провайдера **не обязателен**: без него
работает серверный ключ Voicyfy с оплатой минут из кошелька.

### 5. PUT /api/agent/?agent_id={id} — редактировать агента

Передавай **только изменяемые поля** — остальные не трогаются:

```bash
curl -X PUT "https://voicyfy.ru/api/agent/?agent_id=<uuid>" \
  -H "X-Api-Key: vfy_..." \
  -H "Content-Type: application/json" \
  -d '{
    "voice_additional_instructions": "Твоё имя — Алёна. Отвечай короче.",
    "inbound_first_phrase": "Добрый день! АкваЛето, Алёна слушает."
  }'
```

Доступны все поля из create, а также:

| Поле | Тип | Описание |
|------|-----|----------|
| `is_active` | bool | Включить/выключить агента |
| `default_caller_id` | string | Номер, с которого звонит агент |
| `webhook_url` | string ≤500 | URL вебхука для передачи событий во внешнюю систему |
| `assistant_type` | string | Смена голосового провайдера: создаётся новый голосовой ассистент, запланированные звонки и привязанные номера переносятся на него. Голос сбрасывается на дефолтный, если текущий не поддержан новым провайдером |

При изменении `voice_additional_instructions` системный промпт голосового
агента пересобирается автоматически. При изменении `doc_*` промпт оркестратора
обновляется автоматически (он собирается на лету).

### 6. Память агента — заметки

Все запросы принимают `?agent_id=<uuid>` (без него — первый агент).
Заметки, добавленные по API, помечаются как заметки владельца
(`source: "owner"`).

```bash
# прочитать всю память
curl -H "X-Api-Key: vfy_..." "https://voicyfy.ru/api/agent/memory?agent_id=<uuid>"

# добавить заметку
curl -X POST "https://voicyfy.ru/api/agent/memory?agent_id=<uuid>" \
  -H "X-Api-Key: vfy_..." -H "Content-Type: application/json" \
  -d '{"section": "instructions", "text": "По пятницам после 16:00 не звонить."}'

# изменить заметку (id — из списка notes; text обязателен, section — если нужно перенести)
curl -X PUT "https://voicyfy.ru/api/agent/memory/m3?agent_id=<uuid>" \
  -H "X-Api-Key: vfy_..." -H "Content-Type: application/json" \
  -d '{"text": "По пятницам после 15:00 не звонить."}'

# удалить заметку
curl -X DELETE "https://voicyfy.ru/api/agent/memory/m3?agent_id=<uuid>" \
  -H "X-Api-Key: vfy_..."
```

Ответ любого запроса — актуальная память целиком:

```json
{
  "notes": [
    {"id": "m3", "section": "instructions", "text": "По пятницам после 16:00 не звонить.",
     "source": "owner", "created_at": "2026-09-27T10:00:00", "updated_at": "2026-09-27T10:00:00"}
  ],
  "sections": [{"key": "instructions", "label": "Инструкции владельца", "ui_label": "Вы поручили"}, "..."],
  "count": 1, "chars_used": 36, "chars_limit": 8000,
  "notes_limit": 60, "note_max_chars": 400
}
```

`section`: `instructions` (правила владельца — для API это основной раздел),
`observations`, `plans` (обычно их ведёт сам агент). Ошибки: `400
bad_section`, `400 note_too_long …`, `400 empty_text`, `400 not_found`
(нет заметки с таким id), `404 not_found` (нет агента). Очистить память
целиком по ключу нельзя — только в кабинете.

Перед добавлением прочитай память: если похожее правило уже есть — измени
его (`PUT`), а не добавляй второе.

---

## Голоса по провайдерам

- **fish:** `1ac3ce2f7ba24e90ac2a08055c253fe7` — Светлана (по умолчанию),
  `5ddd9a81cc554841a53b75e355d52628` — Сергей; или любой свой `reference_id`
  с fish.audio. Передаётся в `fish_voice_id`.
- **cascade:** `Anna`, `Sergey` (русская речь)
- **openai:** `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, `cedar`, а также голоса GPT-Live `beacon`, `bossa`, `cinder`, `delta`, `gleam`, `meridian`, `quartz`, `ripple`, `stone`, `tempo`, `vesper`, `willow` (голоса GPT-Live)
- **gemini:** `Zephyr`, `Puck`, `Charon`, `Kore`, `Fenrir`, `Leda`, `Orus`, `Aoede`, `Callirrhoe`, `Autonoe`, `Enceladus`, `Iapetus`, `Umbriel`, `Algieba`, `Despina`, `Erinome`, `Algenib`, `Rasalgethi`, `Laomedeia`, `Achernar`, `Alnilam`, `Schedar`, `Gacrux`, `Pulcherrima`, `Achird`, `Zubenelgenubi`, `Vindemiatrix`, `Sadachbia`, `Sadaltager`, `Sulafat`
- **yandex:** `marina`, `dasha`, `alexander`, `julia`, `lera`, `masha`, `anton`, `kirill`, `filipp`, `ermil`, `jane`, `omazh`, `zahar`, `madi_ru`, `saule_ru`
- **cartesia:** голос задаётся не именем, а `cartesia_voice_id` + опционально `voice_speed` (0.5–1.5)

Невалидное имя голоса → `400 invalid_voice` (в update) или молча дефолт (в create).
Дефолты: fish — Светлана, cascade — `Anna`, openai — `alloy`, gemini — `Kore`,
yandex — `marina`.

Звонки (входящие и исходящие) и веб-виджет OpenAI-ассистентов (и агентов, чей голосовой
ассистент — OpenAI) обслуживает модель **GPT-Live** (`gpt-live-1`, full-duplex) с
бэкенд-моделью `gpt-5.6-luna` для функций. Голос ассистента используется как есть
(все 22 голоса списка выше поддерживаются).

---

## Объект агента (ответ GET/POST/PUT)

Ключевые поля: `id`, `name`, `assistant_type`, `is_active`,
`orchestrator_model`, `doc_who_am_i` … `doc_rules_and_goals`,
`additional_instructions`, `voice_additional_instructions`,
`inbound_first_phrase`, `working_hours_start`, `working_hours_end`,
`default_caller_id`, `webhook_url`, `voice`, `cartesia_voice_id`,
`voice_speed`, `fish_voice_id`, `fish_model`, `fish_latency`,
`has_knowledge_base`, `kb_name`, `kb_char_count`, `created_at`, `updated_at`.

Плюс `assistant_id` и идентификатор голосового ассистента того провайдера,
который выбран: `fish_assistant_id`, `gemini_assistant_id`,
`openai_assistant_id`, `cartesia_assistant_id`, `yandex_assistant_id`,
`cascade_assistant_id` (заполнен ровно один).

---

## Каскад-ассистенты (отдельно от агента)

Каскад — это ещё и **самостоятельный тип ассистента**, не только голос агента.
Такой ассистент отвечает на звонки сам, без оркестратора, задач и CRM: у него
есть системный промпт, приветствие, голос и набор функций. В кабинете такие
ассистенты живут на общей странице `voice-assistants.html`.

Главное отличие от остальных типов: **не нужны никакие свои API-ключи** —
каскад работает на внутренних ресурсах платформы. Доступен на всех тарифах.

Каскад-ассистенты **занимают общий лимит ассистентов тарифа** (start — 5,
profi — 10) наравне с остальными голосовыми ассистентами.

### 7. GET /api/grok-assistants/cascade — список

```bash
curl -H "X-Api-Key: vfy_..." https://voicyfy.ru/api/grok-assistants/cascade
```

### 8. POST /api/grok-assistants/cascade — создать

```bash
curl -X POST https://voicyfy.ru/api/grok-assistants/cascade \
  -H "X-Api-Key: vfy_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Приёмная АкваЛето",
    "system_prompt": "Ты — оператор компании «АкваЛето»...",
    "greeting_message": "Здравствуйте! Компания АкваЛето, чем помочь?",
    "tts_voice": "Anna"
  }'
```

| Поле | Тип | Обяз. | Описание |
|------|-----|-------|----------|
| `name` | string | да | Название ассистента (1–255 символов) |
| `system_prompt` | string | да | Инструкция ассистента — как себя вести в разговоре |
| `description` | string | нет | Описание для себя (≤500 символов) |
| `greeting_message` | string | нет | Первая фраза при входящем звонке (≤500 символов) |
| `tts_voice` | string | нет | Голос: `Anna` или `Sergey`. По умолчанию `Anna` |
| `tts_provider` | string | нет | Оставляй значение по умолчанию (`voxtts`) |
| `temperature` | float 0–2 | нет | Креативность ответов, по умолчанию 0.7 |
| `max_tokens` | int 1–8192 | нет | Лимит длины ответа, по умолчанию 1024 |
| `functions` | array | нет | Функции ассистента (тот же формат, что у остальных провайдеров) |
| `is_telephony_enabled` | bool | нет | Разрешить приём звонков, по умолчанию `true` |

Ответ `201` с объектом ассистента; `id` понадобится для остальных запросов.

Ошибки: `400` — неизвестный голос (в ответе список допустимых);
`402 subscription_required` — нет активной подписки;
`403 assistant_limit_reached` — исчерпан лимит ассистентов тарифа.

### 9. GET / PUT / DELETE /api/grok-assistants/cascade/{id}

```bash
# прочитать
curl -H "X-Api-Key: vfy_..." https://voicyfy.ru/api/grok-assistants/cascade/<uuid>

# изменить (только передаваемые поля)
curl -X PUT https://voicyfy.ru/api/grok-assistants/cascade/<uuid> \
  -H "X-Api-Key: vfy_..." -H "Content-Type: application/json" \
  -d '{"tts_voice": "Sergey", "greeting_message": "АкваЛето, слушаю вас."}'

# удалить
curl -X DELETE https://voicyfy.ru/api/grok-assistants/cascade/<uuid> \
  -H "X-Api-Key: vfy_..."
```

В `PUT` доступны все поля из create плюс `is_active`. `DELETE` возвращает `204`.

### 10. GET /api/grok-assistants/cascade/credits/balance — баланс кредитов каскада

```bash
curl -H "X-Api-Key: vfy_..." \
  https://voicyfy.ru/api/grok-assistants/cascade/credits/balance
```

Возвращает баланс отдельного кошелька кредитов каскада. Сейчас звонки
каскада кредитами не тарифицируются (списание включается на стороне
платформы), поэтому нулевой баланс звонкам не мешает.

### Что делается в кабинете, а не по ключу

Привязка ассистента к телефонному номеру, запуск звонков, контакты агента,
база знаний, подключение Telegram / MAX / коннекторов, публичный канал
заявок, покупка кредитов, пополнение кошелька и номера по API-ключу
**недоступны** — это делает владелец аккаунта в кабинете
(`https://voicyfy.ru/static/agent.html`, `voice-assistants.html`, страница
телефонии). По ключу доступны создание и настройка агентов и ассистентов и
заметки памяти агента.

---

## Ошибки

| Код | `detail` | Причина |
|-----|----------|---------|
| 401 | `invalid_api_key` | Неверный или отозванный API-ключ |
| 400 | `telephony_not_verified` | Телефония Voximplant не верифицирована (делается в кабинете) |
| 400 | `api_key_required_<тип>` | Для провайдера нет ни ключа пользователя, ни серверного ключа платформы (редкий случай — сообщи пользователю, что провайдер временно недоступен, или предложи другой тип) |
| 400 | `agent_limit_reached` | Уже 3 агента |
| 400 | `invalid_assistant_type` / `invalid_orchestrator_model` / `invalid_voice` | Невалидное значение поля |
| 400 | `bad_section` / `note_too_long …` / `empty_text` | Ошибка в заметке памяти агента |
| 402 | `subscription_required` | Триал использован, нужен тариф |
| 403 | `assistant_limit_reached` | Исчерпан лимит ассистентов тарифа (при создании каскад-ассистента) |
| 404 | `not_found` | Агент не найден (чужой или несуществующий `agent_id`) |

---

## Рекомендуемый флоу для Claude Code

**Редактирование существующего агента:**
1. `GET /api/agent/list` → найти агента, взять `id`.
2. `GET /api/agent/?agent_id=<id>` → прочитать текущие настройки.
3. `PUT /api/agent/?agent_id=<id>` → отправить только изменённые поля.
4. Перечитать агента и показать пользователю, что изменилось.

**Создание нового агента:**
1. `GET /api/agent/list` → проверить `can_create_more`.
2. `GET /api/agent/orchestrator-models` → выбрать модель (обычно
   `is_recommended`) или пропустить.
3. Составить 5 документов `doc_*` и инструкции по таблице выше, разделам
   «Гайд», «Рецепты» и «Частые ошибки».
4. `POST /api/agent/create`.
5. Обработать возможные 400/402 (предусловия выполняются пользователем в кабинете).
6. Если у пользователя есть отдельные правила «на сейчас» — добавить их
   заметками памяти (`POST /api/agent/memory`, раздел `instructions`).
7. Напомнить, что сделать в кабинете: привязать номер, заполнить базу
   знаний, подключить Telegram-бот / личный Telegram или MAX, загрузить
   контакты.

**Правила владельца («запомни, что…»):**
1. `GET /api/agent/memory?agent_id=<id>` → посмотреть, нет ли похожей заметки.
2. Есть — `PUT /api/agent/memory/{note_id}`; нет — `POST /api/agent/memory`.
3. Устаревшее правило — `DELETE /api/agent/memory/{note_id}`.

**Какой голос выбрать.** Без своих ключей работают все типы (минуты — из
кошелька). Fish — качественная русская речь, голоса Светлана и Сергей,
тариф из кошелька. Каскад — без ключей и без списаний. Если у пользователя
есть свой ключ OpenAI/Gemini/Yandex — с ним минуты голоса не списываются с
кошелька.

**Создание каскад-ассистента (без агента):**
1. `GET /api/grok-assistants/cascade` → посмотреть, что уже есть.
2. `POST /api/grok-assistants/cascade` → `name`, `system_prompt`,
   `greeting_message`, `tts_voice`.
3. При `403 assistant_limit_reached` — сказать пользователю, что исчерпан
   лимит ассистентов тарифа, и предложить удалить лишнего или поднять тариф.
4. Напомнить, что привязать ассистента к номеру нужно в кабинете.

**При настройке промптов** сверяйся с разделами «Жизненный цикл звонка»,
«Инструменты оркестратора» и «Функции голосового агента»: инструкции,
написанные в терминах реальных инструментов и стадий, работают заметно
лучше абстрактных пожеланий.
