#Действия агентов и запросы действий
Статус: Планируется. Запросы действий (
lead.action_requested) и ответ на них (ack) в платформе не реализованы. Сегодня агенты пишут в нашу CRM напрямую. Страница описывает замысел; параметры действий предварительные. Сроки не обещаются.
В CRM платформы работают несколько ИИ-агентов. Они не просто читают лиды, а меняют их: ставят оценку, двигают сделку, назначают задачи, отправляют напоминания клиенту. Когда у вас внешняя CRM и она — источник истины (режим external, после принятия лида), агентам нельзя менять лид молча. Вместо этого агент оформляет запрос действия, вы получаете его вебхуком и решаете: выполнить или отказать.
Доступ: вебхук lead.action_requested (подписка интеграции, webhooks:manage); leads:write для ответа. Платно: нет. ПДн: нагрузка события тонкая; причина и доказательства запрашиваются по API, если у подписки не включён флаг include_pii (Передача ПДн).
#Кто такие агенты
| Агент | Что делает с лидом |
|---|---|
| ИИ-продажник | Ведёт диалог, создаёт лид, ставит напоминание (задача типа «ИИ»), просит подключить оператора |
| Связыватель CRM | После каждого ответа продажника уточняет данные контакта (почта, телефон, теги), связывает или создаёт сделку, ставит намерение и извлечённые данные лида, повторно оценивает |
| Автоматизации (правила арендатора) | Перемещают сделку по воронке, ставят теги, оценивают лид, назначают оператора, расставляют приоритет, закрывают диалог |
| Дожим | Напоминает остывшему лиду или клиенту, не ответившему на предложение |
| Стратег | Только читает счётчики воронки, ничего не пишет |
#Как это работает
Агент решает действие
|
v
Режим internal ----------> действие выполняется у нас, событие lead.updated
Режим both --------------> выполняется у нас, поля по таблице владения
Режим external, лид принят -> запрос действия (lead.action_requested)
|
v
Ваша CRM: ack = принять -> действие выполняется, событие lead.updated
ack = отклонить -> действие не выполняется, агент учитывает отказ
нет ответа в срок -> запрос истёк (expired)
До принятия лида (режим external) действия выполняются у нас, как в режиме internal: лид ещё наш.
#Состояния запроса
| Состояние | Значение |
|---|---|
requested | Запрос создан, ответа нет |
acked | Принят вашей CRM |
rejected | Отклонён вашей CRM |
expired | Срок вышел без ответа либо арендатор переключил режим с external на internal (CRM: режимы) |
#Типы действий
| Тип | Что просит агент |
|---|---|
| Сменить статус | Перевести лид в канонический статус (например, qualified) или отметить потерю с причиной |
| Назначить | Назначить ответственного сотрудника |
| Поставить задачу | Создать задачу по лиду |
| Отправить дожим | Отправить клиенту напоминание |
| Передать оператору | Подключить человека к диалогу |
Уведомление о самой передаче диалога оператору приходит отдельным событием conversation.handoff_requested (с lead_id, если у диалога есть лид); события lead.handoff_requested нет.
Каждый запрос несёт причину (почему агент так решил) и доказательство (на что опирался: фраза из диалога, срок молчания, оценка), чтобы человек в вашей CRM мог принять решение осмысленно.
#Объект запроса
Предварительная схема; финальная фиксируется при выпуске.
| Поле | Тип | Описание |
|---|---|---|
action_id | string (uuid) | Идентификатор запроса |
lead_id | string (uuid) | Лид |
lead_version | integer | Версия лида на момент запроса |
type | string | Тип действия (таблица выше) |
params | object | Параметры действия |
reason | string | Причина |
evidence | array | Доказательства: короткие описания и ссылки на источник |
agent | string | Какой агент просит |
state | string | requested, acked, rejected, expired |
requested_at, expires_at | string (ISO 8601), UTC | Создан и срок ответа |
Примеры params:
| Тип | Параметры |
|---|---|
| Сменить статус | {"status": "qualified"} или {"status": "lost", "lost_reason": "..."} |
| Назначить | {"assignee_id": "<uuid сотрудника>"} |
| Поставить задачу | {"title": "...", "due_at": "..."} |
| Отправить дожим | {"channel": "telegram", "text": "..."} |
| Передать оператору | {} |
#Событие lead.action_requested
Приходит на подписанный адрес вебхука в конверте v2. По умолчанию нагрузка «тонкая» (идентификатор, версия, тип).
{
"id": "evt_EXAMPLE02",
"type": "lead.action_requested",
"api_version": "2026-10-01",
"created_at": "2026-10-11T10:02:41Z",
"livemode": true,
"data": {
"lead_id": "5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b",
"action_id": "a3d2f1c0-8b7e-4c55-9d2a-6e1f0b9c7d34",
"lead_version": 4
}
}
Подпись в заголовке Webhook-Signature (JWS, Ed25519), повторы доставки и дедупликация по Webhook-Event-Id — в разделе о вебхуках. Содержимое запроса получите запросом GET /leads/{id} либо, при include_pii: true, прямо в событии; отдельная точка чтения запроса в проекте не названа (см. «Открытые вопросы»).
#POST /leads/{id}/actions/{action_id}/ack
Ответ на запрос: принять или отклонить.
Область: leads:write. Заголовки: Idempotency-Key (рекомендуется), Content-Type: application/json.
| Параметр | Тип | Обязательно | Ограничения | Пример |
|---|---|---|---|---|
decision | string | да | accept или reject | "accept" |
reason | string | нет | Причина отказа; видна агенту и в журнале | "Лид уже в работе у менеджера" |
При accept вы сообщаете, что действие применено (или будет применено) у вас. Платформа отражает результат в проекции лида.
Ответ 200 OK: запрос с новым состоянием.
{
"action_id": "a3d2f1c0-8b7e-4c55-9d2a-6e1f0b9c7d34",
"lead_id": "5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b",
"state": "acked"
}
| Ситуация | Ответ |
|---|---|
Запрос уже закрыт (acked, rejected, expired) | 409 invalid_state |
Нет decision или значение не accept/reject | 422 validation_failed (errors[].field = /decision) |
| Чужой лид или запрос | 404 not_found |
Повтор с тем же Idempotency-Key и телом | Тот же ответ |
Тот же Idempotency-Key, другое тело | 409 idempotency_conflict |
Остальные ошибки — каталог кодов.
#Примеры
curl -X POST \
https://formula-cream.pro/api/public/v1/leads/$LEAD_ID/actions/$ACTION_ID/ack \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: 4f6c1b9a-0d7e-4a23-8e5f-2b3c9d1a7e60" \
-H "Content-Type: application/json" \
-d '{"decision":"accept"}'
# API_KEY=sap_ru_test_EXAMPLE0KEYID_… (пример, не настоящий ключ)
await fetch(
`https://formula-cream.pro/api/public/v1/leads/${leadId}/actions/${actionId}/ack`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({ decision: "reject", reason: "Лид уже в работе" }),
},
);
httpx.post(
f"https://formula-cream.pro/api/public/v1/leads/{lead_id}/actions/{action_id}/ack",
headers={"Authorization": f"Bearer {os.environ['API_KEY']}",
"Idempotency-Key": str(uuid.uuid4())},
json={"decision": "accept"},
)
$ch = curl_init("https://formula-cream.pro/api/public/v1/leads/$leadId/actions/$actionId/ack");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("API_KEY"),
"Idempotency-Key: " . bin2hex(random_bytes(16)),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(["decision" => "accept"]),
]);
#Пример обработчика во внешней CRM
Обработчик проверяет подпись, берёт событие, читает лид, применяет действие у себя и отвечает платформе. Проверку подписи и защиту от повторов см. в разделе о вебхуках.
// Express-подобный обработчик, схема. Подпись проверяется до разбора тела.
app.post("/webhooks/platform", express.raw({ type: "application/json" }), async (req, res) => {
if (!verifyWebhookSignature(req.headers["webhook-signature"], req.body, WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
res.sendStatus(200); // подтверждаем приём быстро, работу делаем дальше
const event = JSON.parse(req.body);
if (event.type !== "lead.action_requested") return;
if (await alreadyProcessed(event.id)) return; // доставка повторяется
const { lead_id, action_id } = event.data;
const lead = await api(`/leads/${lead_id}`); // GET с ключом
const decision = await applyInCrm(lead, action_id); // ваша логика
await api(`/leads/${lead_id}/actions/${action_id}/ack`, "POST", {
decision: decision.ok ? "accept" : "reject",
reason: decision.reason,
});
});
verifyWebhookSignature, alreadyProcessed, applyInCrm, api — функции вашей системы; здесь они показывают порядок шагов.
#Отправка дожима
Если запрошено «отправить дожим», решает режим и настройки арендатора:
- у арендатора автоотправка по умолчанию выключена;
- чтобы дожим уходил, нужно включить его в настройках арендатора, указать собственные каналы и собственный шаблон сообщения на нужном языке;
- если шаблона на языке клиента нет или канал не включён, письмо не отправляется, вместо этого создаётся задача менеджеру.
Причина в том, что клиенту арендатора не годится текст нашей школы ни на каком языке.
#Открытые вопросы
- Срок действия запроса до состояния
expiredпри отсутствии ответа не назван (техническое). Состояниеexpiredу запросов действий чата стратега — отдельная сущность с собственным сроком (Предложения стратега). - Параметры действий и формат
evidenceв проекте не определены; приведённые таблицы предварительны (техническое). - Отдельной точки чтения запроса (
GET .../actions) в проекте нет; предполагается получение через лид или событие сinclude_pii: true(техническое). - Что делает агент при отказе: повторяет запрос, ждёт или оставляет лид без действия (к владельцу).
- Остаётся ли тип «Передать оператору» среди запросов действий рядом с событием
conversation.handoff_requested(техническое). - Единый сервис действий: сегодня записи агентов разбросаны по ~15 файлам кода; публичный механизм запросов появится только после его выделения. Задачи сегодня не связаны с лидом напрямую (нет
lead_id) — предпосылка выпуска.