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

#API лидов

Статус: Доступно (чтение) / Планируется (запись). Работают GET /leads и GET /leads/{id} (область leads:read). Создание, изменение, приём, оценка, заметки и задачи — Планируется: вызовы вернут 404. Поля лида ниже — фактические, как их отдаёт чтение (схема Lead в OpenAPI).

Лид — это обращение человека, которое может стать клиентом: он связан с контактом, диалогом, источником, оценкой и сделкой. Через API внешняя CRM создаёт лиды, читает их, меняет статус и ответственного, принимает лид в работу, оставляет заметки и ставит задачи.

Доступ: leads:read — чтение; leads:write — запись; leads:score — оценка (платно, см. Оценка лидов). ПДн: да — контакт содержит имя, почту, телефон; свободный текст лида (title, intent, ai_summary, score.reason, next_action, lost_reason) тоже может нести ПДн, поэтому лид и без контакта считается раскрытием. Внутренние заметки персонала (notes) наружу не отдаются. Каждая выдача записывается в журнал выдач без значений (см. Передача ПДн). В ячейке RU для области leads:* список IP интеграции обязателен, а при подключении указываются страна размещения принимающей системы (только РФ; иначе 403 pii_transfer_not_allowed) и основание передачи (Передача ПДн). Платно: нет, кроме оценки лида нашей моделью. Режим: какие поля вы вправе менять, зависит от режима CRM арендатора — CRM: режимы.

#Базовый адрес

ЯчейкаАдрес
RUhttps://formula-cream.pro/api/public/v1
ENhttps://wfacademy.org/api/public/v1
IDhttps://wfacademy.id/api/public/v1

Ключ действует только на своей ячейке: ключ RU на адресе EN вернёт 401 wrong_cell с полем correct_base_url. Заголовки, формат ошибок, пагинация, пределы — в Соглашениях. Аутентификация — Ключи и области. Во всех примерах ключ заведомо ненастоящий (EXAMPLE).

#Точки

Метод и путьОбластьЧто делает
POST /leadsleads:writeСоздать лид (идемпотентно по external_id)
GET /leadsleads:readСписок лидов с фильтрами
GET /leads/{id}leads:readЛид по идентификатору платформы
GET /leads/by-external/{external_id}leads:readЛид по идентификатору во внешней CRM
PATCH /leads/{id}leads:writeИзменить поля; требует If-Match и Idempotency-Key
POST /leads/{id}/acceptleads:writeПринять лид во внешнюю CRM
POST /leads/{id}/scoreleads:scoreОценить лид нашей моделью (платно)
PUT /leads/{id}/scoreleads:writeЗаписать оценку внешней CRM (без списания)
POST /leads/{id}/actions/{action_id}/ackleads:writeПодтвердить или отклонить запрос действия агента
POST /leads/{id}/notesleads:writeДобавить заметку
POST /leads/{id}/tasksleads:writeПоставить задачу

Статус по точкам: Доступно — GET /leads и GET /leads/{id}; все остальные точки таблицы — Планируется.

Оценка, действия агентов и карта статусов (PUT /crm/status-map, leads:write) описаны отдельно: оценка, действия агентов, карта статусов.

#Объекты

#Лид

ПолеТипОписание
idstring (uuid)Идентификатор лида на платформе
external_idstring | nullИдентификатор лида во внешней CRM; null, пока внешняя CRM не подключена
external_urlstring | nullСсылка на карточку во внешней CRM
versioninteger | nullВерсия для If-Match; пока null (в базе нет колонки версии) — используйте etag
etagstringМетка версии; совпадает с заголовком ETag карточки (слабая, вида W/"…")
statusstringОдин из шести канонических статусов: new, qualified, proposal, negotiation, won, lost (см. карту статусов). Архив — не статус, а флаг archived
archivedbooleanЛид в архиве (отметка на лиде, не статус)
archived_reasonstring | nullПричина архива, например ai_not_client
owner"internal" | "external"Кто владеет статусом, ответственным и исходом (см. режимы)
titlestring | nullНазвание лида
sourcestring | nullИсточник
assigned_tostring (uuid) | nullОтветственный — только сотрудник арендатора
valueobject | nullОжидаемая сумма: {"amount": "15000.00", "currency": "RUB"}
product_idstring (uuid) | nullПродукт, к которому относится лид
course_slugstring | nullКурс
contactobject | nullКонтакт (см. ниже)
conversation_idstring (uuid) | nullДиалог, из которого возник лид
attributionobject | nullИсточник и путь посетителя (см. ниже)
scoreobject | nullОценка: score (0–100), grade (A ≥ 75, B 50–74, C 25–49, D < 25), reason, flags, source (ai, external, manual), model, scored_at; null у лида без оценки (балл 0 и нет следов ИИ). Пока reason, source и scored_at могут быть null (Оценка лидов)
flagsobject | nullФлаги buy_intent, support, supplier (также в score.flags)
intentstring | nullНамерение, выделенное агентом
ai_summarystring | nullСводка диалога
next_action, next_action_atstring | nullСледующее действие и срок
lost_reasonstring | nullПричина потери
qualified_at, won_at, lost_atstring (ISO 8601) | nullОтметки исходов
last_activity_atstring (ISO 8601) | nullПоследняя активность
created_at, updated_atstring (ISO 8601)Создан и изменён, UTC

На платформе у диалога не бывает двух активных лидов: один диалог — один активный лид.

#Контакт (ПДн)

ПолеТипОписание
idstring (uuid)Идентификатор контакта
first_name, last_namestring | nullИмя и фамилия
emailstring | nullПочта
phonestring | nullТелефон
tagsarray of stringТеги
geo_country, geo_citystring | nullГеография по IP посетителя

#Атрибуция

ПолеТипОписание
utm_source, utm_medium, utm_campaignstring | nullМетки кампании
first_pagestring | nullПервая страница посещения
referrerstring | nullОткуда пришёл посетитель

Атрибуция записывается один раз; изменить её через API нельзя.

#POST /leads

Создаёт лид и, если нужно, контакт. Повторный запрос с тем же external_id не создаёт второй лид, а возвращает существующий.

Заголовки: Authorization: Bearer <ключ>, Idempotency-Key (рекомендуется; повтор с тем же external_id и без заголовка не создаёт дубль), Content-Type: application/json.

ПараметрТипОбязательноОграниченияПример
external_idstringдаУникален в пределах арендатора и интеграции"crm-48213"
external_urlstringнетАбсолютный https-адрес"https://crm.example.com/leads/48213"
contactobjectдаНужна хотя бы почта или телефон{"first_name":"Анна","email":"anna@example.com"}
titlestringнетПроизвольная строка"Заявка на курс"
sourcestringнетПроизвольная строка"landing"
valueobjectнетamount строкой, currency — код ISO 4217{"amount":"15000.00","currency":"RUB"}
product_idstring (uuid)нетПродукт арендатора"8a6f3d10-2b94-4c7e-a1d5-0e9b7c4f2a11"
attributionobjectнетПоля атрибуции{"utm_source":"yandex"}
statusstringнетОдин из шести канонических статусов; по умолчанию new"new"
{
  "external_id": "crm-48213",
  "external_url": "https://crm.example.com/leads/48213",
  "contact": { "first_name": "Анна", "email": "anna@example.com", "phone": "+79990001122" },
  "title": "Заявка на курс",
  "source": "landing",
  "value": { "amount": "15000.00", "currency": "RUB" },
  "attribution": { "utm_source": "yandex", "utm_campaign": "spring" }
}

Ответ: 201 Created для нового лида, 200 OK при повторе с тем же external_id. Тело — объект лида.

{
  "id": "5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b",
  "external_id": "crm-48213",
  "external_url": "https://crm.example.com/leads/48213",
  "version": 1,
  "status": "new",
  "archived": false,
  "owner": "internal",
  "title": "Заявка на курс",
  "source": "landing",
  "assigned_to": null,
  "value": { "amount": "15000.00", "currency": "RUB" },
  "contact": { "id": "0c4a9d52-7e71-4c63-8f0b-2a9c6e1d3b44", "first_name": "Анна", "last_name": null,
               "email": "anna@example.com", "phone": "+79990001122", "tags": [] },
  "score": null,
  "flags": null,
  "created_at": "2026-10-11T09:30:00Z",
  "updated_at": "2026-10-11T09:30:00Z"
}

Ошибки: коды из каталога.

КодHTTPКогда
validation_failed422Поля не прошли проверку; детали в errors[]
scope_missing403Нет leads:write
ip_not_allowed403Запрос с адреса вне списка IP ключа (в ячейке RU список обязателен)
idempotency_conflict409Тот же Idempotency-Key с другим телом
rate_limited429Превышен предел записи

Пример 422 (формат):

{
  "type": "https://formula-cream.pro/developers/errors#validation_failed",
  "title": "Запрос не прошёл проверку",
  "status": 422,
  "code": "validation_failed",
  "detail": "Один или несколько параметров некорректны.",
  "request_id": "req_EXAMPLE0101",
  "errors": [
    { "field": "/contact", "code": "required", "message": "Нужна хотя бы почта или телефон." }
  ]
}

#Примеры

curl -X POST https://formula-cream.pro/api/public/v1/leads \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"crm-48213","contact":{"first_name":"Анна","email":"anna@example.com"}}'
# API_KEY=sap_ru_test_EXAMPLE0KEYID_… (пример, не настоящий ключ)
const res = await fetch("https://formula-cream.pro/api/public/v1/leads", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    external_id: "crm-48213",
    contact: { first_name: "Анна", email: "anna@example.com" },
  }),
});
const lead = await res.json();
import os, uuid, httpx

r = httpx.post(
    "https://formula-cream.pro/api/public/v1/leads",
    headers={
        "Authorization": f"Bearer {os.environ['API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"external_id": "crm-48213",
          "contact": {"first_name": "Анна", "email": "anna@example.com"}},
)
lead = r.json()
$ch = curl_init("https://formula-cream.pro/api/public/v1/leads");
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([
        "external_id" => "crm-48213",
        "contact" => ["first_name" => "Анна", "email" => "anna@example.com"],
    ], JSON_UNESCAPED_UNICODE),
]);
$lead = json_decode(curl_exec($ch), true);

#GET /leads

Статус: Доступно. Область leads:read.

Возвращает страницу лидов арендатора. Пагинация курсорная (Соглашения): порядок возрастающий по (updated_at, id), курсор живёт 24 часа, подписан и привязан к ячейке, арендатору, интеграции, маршруту и набору фильтров. Ответ — {data, next_cursor}; пустая страница — честный 200 с data: []. Содержит персональные данные (контакт и свободный текст лидов): каждая выдача пишется в журнал выдач, при сбое записи журнала — 503.

ПараметрТипОбязательноОграниченияПример
limitintegerнет1–100; по умолчанию 2550
cursorstringнетЗначение next_cursor из прошлого ответа; действителен 24 часа"eyJ1Ijoi…"
include_deletedbooleanнетпока не поддерживается: true даёт 422 not_supported (удалённые лиды не хранятся)false
statusstringнетОдин из шести канонических статусовqualified
updated_sincestringнетлиды, изменённые не раньше момента (включительно); ISO 8601, дата или дата-время с поясом2026-10-01T00:00:00Z
updated_afterstringнетто же, но строго позже момента (обход «с последнего увиденного»); вместе с updated_since нельзя2026-10-01T00:00:00Z
sourcestringнетточное значение источника лидаwebsite
archivedbooleanнетtrue — только архивные; по умолчанию (false) — только неархивныеfalse

Перечень фильтров может быть расширен (например, по ответственному); действующий — в OpenAPI.

{
  "data": [ { "id": "5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b", "status": "new", "version": 1 } ],
  "next_cursor": "eyJ1IjoiMjAyNi0xMC0xMVQwOTozMDowMFoiLCJpIjoiNWQxYiJ9"
}
curl "https://formula-cream.pro/api/public/v1/leads?status=qualified&limit=50" \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(
  "https://formula-cream.pro/api/public/v1/leads?status=qualified&limit=50",
  { headers: { Authorization: `Bearer ${process.env.API_KEY}` } },
);
const { data, next_cursor } = await res.json();
r = httpx.get(
    "https://formula-cream.pro/api/public/v1/leads",
    params={"status": "qualified", "limit": 50},
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
)
page = r.json()
$ch = curl_init("https://formula-cream.pro/api/public/v1/leads?status=qualified&limit=50");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("API_KEY")],
]);
$page = json_decode(curl_exec($ch), true);

Чтобы получать изменения, а не весь список, запрашивайте updated_after с последним увиденным updated_at (или идите по next_cursor) и подписывайтесь на lead.updated. «Надгробий» удалённых лидов пока нет. Недействительный или просроченный курсор — 422 validation_failed с errors[].field = /cursor: начните обход заново.

#GET /leads/{id} и GET /leads/by-external/{external_id}

Статус: GET /leads/{id} — Доступно (область leads:read); GET /leads/by-external/{external_id} — Планируется (внешней CRM пока нет).

Возвращают объект лида; заголовок ETag равен полю etag. Содержит ПДн (контакт и свободный текст лида) и пишется в журнал выдач до ответа; сбой записи — 503. Чужой, несуществующий или не-uuid идентификатор — 404 not_found (платформа не подтверждает существование ресурса другого арендатора).

ПараметрТипОбязательноОграниченияПример
id (путь)string (uuid)даИдентификатор лида на платформе5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b
external_id (путь)stringдаИдентификатор во внешней CRM; только для by-externalcrm-48213
curl https://formula-cream.pro/api/public/v1/leads/by-external/crm-48213 \
  -H "Authorization: Bearer $API_KEY"

#PATCH /leads/{id}

Меняет поля лида. Запрос обязан нести версию, которую вы прочитали, — так два писателя не затрут друг друга.

Заголовки: If-Match: <version> — обязателен; Idempotency-Key — рекомендуется.

ПараметрТипОбязательноОграниченияПример
statusstringнетКанонический статус"proposal"
assigned_tostring (uuid)нетТолько сотрудник вашего арендатора"0c4a9d52-7e71-4c63-8f0b-2a9c6e1d3b44"
valueobjectнет{amount, currency}; amount — строка{"amount":"15000.00","currency":"RUB"}
notesstringнетПроизвольная строка"Просила перезвонить"
lost_reasonstringнетДля статуса lost"дорого"
external_id, external_urlstringнетИдентификатор и ссылка во внешней CRM"crm-48213"
СитуацияОтвет
Нет If-Match428 precondition_required
Версия устарела412 precondition_failed; прочитайте лид заново и повторите
Поле принадлежит другой стороне (режим external до или после принятия, both по таблице владения)409 invalid_state; в errors[].field — путь к полю
Недопустимое значение (неизвестный статус, ответственный не из вашего арендатора)422 validation_failed
Нет leads:write403 scope_missing
curl -X PATCH https://formula-cream.pro/api/public/v1/leads/5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b \
  -H "Authorization: Bearer $API_KEY" \
  -H "If-Match: 1" -H "Content-Type: application/json" \
  -d '{"status":"proposal"}'
await fetch(`https://formula-cream.pro/api/public/v1/leads/${id}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.API_KEY}`,
    "If-Match": String(lead.version),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ status: "proposal" }),
});
r = httpx.patch(
    f"https://formula-cream.pro/api/public/v1/leads/{lead_id}",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}",
             "If-Match": str(lead["version"])},
    json={"status": "proposal"},
)
$ch = curl_init("https://formula-cream.pro/api/public/v1/leads/$leadId");
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => "PATCH",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("API_KEY"),
        "If-Match: " . $lead["version"],
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode(["status" => "proposal"]),
]);

#POST /leads/{id}/accept

Принять лид во внешнюю CRM (режим external). После вызова owner лида становится external, наши агенты прекращают собственные касания. Тело запроса необязательно; рекомендуется передать external_id и external_url, если вы ещё не сохранили их через PATCH. Ответ 200 OK — объект лида. Если лид уже принят или режим арендатора не external, вернётся 409 invalid_state.

ПараметрТипОбязательноОграниченияПример
external_idstringнетИдентификатор лида во внешней CRM"crm-48213"
external_urlstringнетАбсолютный https-адрес"https://crm.example.com/leads/48213"
curl -X POST https://formula-cream.pro/api/public/v1/leads/5d1b2c7e-3f0a-4a8e-9a3b-1c2d3e4f5a6b/accept \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 3b241101-e2bb-4255-8caf-4136c566a962" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"crm-48213"}'

#POST /leads/{id}/notes

ПараметрТипОбязательноОграниченияПример
textstringдаНепустая строка"Просила перезвонить после 18:00"

Ответ 201 Created — созданная заметка. Рекомендуется Idempotency-Key.

curl -X POST https://formula-cream.pro/api/public/v1/leads/$LEAD_ID/notes \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"text":"Просила перезвонить после 18:00"}'

#POST /leads/{id}/tasks

ПараметрТипОбязательноОграниченияПример
titlestringда"Позвонить клиенту"
due_atstringнетISO 8601, UTC"2026-10-12T15:00:00Z"
assignee_idstring (uuid)нетСотрудник арендатора"0c4a9d52-7e71-4c63-8f0b-2a9c6e1d3b44"

Ответ 201 Created — созданная задача. Рекомендуется Idempotency-Key. Задача связывается с лидом. Сегодня задачи на платформе связаны с контактом и диалогом, а не с лидом напрямую; связь с лидом — одна из предпосылок в коде (предпосылка выпуска).

curl -X POST https://formula-cream.pro/api/public/v1/leads/$LEAD_ID/tasks \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"title":"Позвонить клиенту","due_at":"2026-10-12T15:00:00Z"}'

#Заметки

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