Формула ИИ для разработчиков Справочник API Каталог ошибок OpenAPI
Разделы

#Действия агентов и запросы действий

Статус: Планируется. Запросы действий (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_idstring (uuid)Идентификатор запроса
lead_idstring (uuid)Лид
lead_versionintegerВерсия лида на момент запроса
typestringТип действия (таблица выше)
paramsobjectПараметры действия
reasonstringПричина
evidencearrayДоказательства: короткие описания и ссылки на источник
agentstringКакой агент просит
statestringrequested, acked, rejected, expired
requested_at, expires_atstring (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.

ПараметрТипОбязательноОграниченияПример
decisionstringдаaccept или reject"accept"
reasonstringнетПричина отказа; видна агенту и в журнале"Лид уже в работе у менеджера"

При 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/reject422 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 — функции вашей системы; здесь они показывают порядок шагов.

#Отправка дожима

Если запрошено «отправить дожим», решает режим и настройки арендатора:

Причина в том, что клиенту арендатора не годится текст нашей школы ни на каком языке.

#Открытые вопросы