#Отчёты стратега
Статус: Доступно (чтение). Работают
GET /strategist/reportsиGET /strategist/reports/{id}(областьstrategist:read). Отчёты строятся на агрегатах; названия конкурентов скрыты, снимок аналитики, модель и стоимость прогона не отдаются.
Стратег — ИИ-маркетолог Sapport. Он собирает данные вашего сайта и рекламы (трафик, поисковые запросы, статьи, конкуренты, CRM-воронка в счётчиках) и раз в сутки и раз в неделю готовит отчёт: что произошло, что тревожит и что стоит сделать. API отчётов отдаёт этот отчёт вашей системе в виде структуры, а не готового текста для мессенджера.
Доступ: strategist:read. Платно: нет (чтение готового отчёта). ПДн: нет — отчёт строится на агрегатах и счётчиках, персональных данных учеников и лидов в нём нет.
Публичный API отдаёт отчёты стратега двух видов: суточные (daily) и недельные (weekly). Прогоны других модулей, которые лежат в той же таблице отчётов (например, креативы), через API не отдаются.
#Базовый адрес
| Ячейка | Адрес |
|---|---|
| RU | https://formula-cream.pro/api/public/v1 |
| EN | https://wfacademy.org/api/public/v1 |
| ID | https://wfacademy.id/api/public/v1 |
Ошибки, пагинация, пределы — Соглашения. Ключи и области — Аутентификация. Во всех примерах ключ заведомо ненастоящий.
#Точки
| Метод и путь | Область | Что делает |
|---|---|---|
GET /strategist/reports | strategist:read | Список отчётов: шапки без тела |
GET /strategist/reports/{id} | strategist:read | Один отчёт целиком |
Отчёт создаёт сам стратег по расписанию. Заказать отчёт запросом API нельзя. Список — курсорный (Пагинация).
#Структура отчёта
Отчёт отвечает на три вопроса: что главное, что тревожит и что делать.
| Блок | Поле | Что внутри |
|---|---|---|
| Главное | highlights | Короткие факты периода |
| Тревоги | concerns | Проблемы и риски: провалы показателей, разрывы в графике, пункты «Не делать» |
| Рекомендации | actions | Что сделать, с приоритетом, ожидаемым эффектом и доказательствами |
| Доказательства | evidence | Кейсы и данные, на которые опираются рекомендации |
#Шапка отчёта (элемент списка)
| Поле | Тип | Описание |
|---|---|---|
id | string (uuid) | Идентификатор отчёта |
report_date | string (дата, YYYY-MM-DD) | Дата отчёта |
report_type | string | Вид отчёта: daily (суточный) или weekly (недельный) |
summary_text | string | Краткая сводка |
actions_count | integer | Число рекомендаций |
data_as_of | object | На какой момент актуальны данные отчёта: {показатель: дата YYYY-MM-DD} (например, {"gsc": "2026-10-09", "tracker": "2026-10-11"}); ключ snapshot — дата снимка аналитики, на котором построен отчёт. Пустой объект, если снимка нет |
delivered_at | string (ISO 8601) | null | Когда отчёт доставлен в Telegram-бот; null, если не доставлялся |
Список возвращает только шапки: тело отчёта в нём не отдаётся ни при каком наборе прав.
#Отчёт целиком
| Поле | Тип | Описание |
|---|---|---|
id | string (uuid) | Идентификатор отчёта |
report_date | string | Дата отчёта |
report_type | string | Вид отчёта: daily или weekly |
summary_text | string | Краткая сводка |
data_as_of | object | Как в шапке |
highlights | array | Главное |
concerns | array | Тревоги |
actions | array of object | Рекомендации (ниже) |
evidence | object | Доказательства (ниже) |
Ссылки на внутренние объекты (статья, запрос, продукт) в текстах highlights, concerns и rationale заменяются читаемыми подписями.
#Рекомендация (actions[])
| Поле | Тип | Описание |
|---|---|---|
title | string | Что сделать |
priority | string | high, medium или low |
rationale | string | Почему это нужно |
target_ref | string | null | Что затронет: ссылка на объект (статья, запрос, продукт) |
expected_effect | object | null | Ожидаемый эффект: metric (показатель), delta (изменение), horizon_days (срок в днях) |
check_after_days | integer | null | Через сколько дней проверить результат |
basis | string | На чём стоит рекомендация (таблица ниже) |
Поля target_ref, expected_effect, check_after_days и basis есть в структуре новых отчётов. В отчётах, созданных до их появления, этих полей нет или они пусты (см. «Открытые вопросы»).
#Основание рекомендации (basis)
| Значение | Смысл |
|---|---|
own_measured | Измерено на ваших данных |
observed_pattern | Замечено у конкурентов |
research | Опирается на внешнее исследование |
hypothesis | Предположение без подтверждения. Это значение по умолчанию |
Рекомендация без единого кейса помечена как hypothesis: стратег называет догадку догадкой.
#Доказательства (evidence)
Для каждой рекомендации прикладывается до трёх доказательств.
| Поле | Тип | Описание |
|---|---|---|
evidence.actions[] | array | По одному элементу на рекомендацию (в порядке actions) |
evidence.actions[].index | integer | Номер рекомендации |
evidence.actions[].status | string | supported (подтверждена доказательствами) или unsupported |
evidence.actions[].basis | string | Основание (как в таблице выше) |
evidence.actions[].items[] | array | Доказательства |
evidence.actions[].items[].ref | string | Ссылка на источник |
evidence.actions[].items[].kind | string | Вид источника, например own (ваши данные), competitor (конкурент), pulse (пульс), signal (сигнал); набор может расширяться |
evidence.actions[].items[].label | string | Подпись источника |
evidence.actions[].items[].url | string | Адрес страницы (необязательно) |
evidence.actions[].items[].quote | string | Цитата (необязательно) |
Подписи, адреса и цитаты подставляет сервер из данных, которые он сам выдал стратегу; модель присылает только ссылку. Поэтому в доказательствах нет выдуманных адресов.
Проверка доказательств честно ограничена. Сервер проверяет, что ссылка выдана им самим и что цитата взята из источника. Он не проверяет, что кейс действительно подтверждает вывод. Поле
evidence.validation.relevance_checkedвсегдаfalse.
#Срез без сырья конкурентов
В v1 сырьё конкурентов (право competitors.raw) интеграции недоступно, поэтому отчёт всегда урезан: не возвращаются raw_signals (снимок аналитики), модель, стоимость прогона, а в доказательствах скрыты домены конкурентов и модельные детали. Структура блоков при этом сохраняется. Креативные отчёты не отдаются вовсе.
#GET /strategist/reports
| Параметр | Тип | Обязательно | Ограничения | Пример |
|---|---|---|---|---|
limit | integer | нет | 1–100, по умолчанию 25 | 10 |
cursor | string | нет | Значение next_cursor; живёт 24 часа | |
report_type | string | нет | daily или weekly | daily |
{
"data": [
{
"id": "3d8f1b52-6a47-4c9e-b0d3-9e51a2c7f804",
"report_date": "2026-10-11",
"report_type": "daily",
"summary_text": "Трафик из поиска вырос, конверсия в заявку без изменений.",
"actions_count": 3,
"data_as_of": { "gsc": "2026-10-09", "tracker": "2026-10-11" },
"delivered_at": "2026-10-11T06:10:04Z"
}
],
"next_cursor": null
}
curl "https://formula-cream.pro/api/public/v1/strategist/reports?limit=10" \
-H "Authorization: Bearer $SAPPORT_API_KEY"
# SAPPORT_API_KEY=sap_ru_test_EXAMPLE0KEYID_… (пример, не настоящий ключ)
const res = await fetch(
"https://formula-cream.pro/api/public/v1/strategist/reports?limit=10",
{ headers: { Authorization: `Bearer ${process.env.SAPPORT_API_KEY}` } },
);
const { data, next_cursor } = await res.json();
r = httpx.get(
"https://formula-cream.pro/api/public/v1/strategist/reports",
params={"limit": 10},
headers={"Authorization": f"Bearer {os.environ['SAPPORT_API_KEY']}"},
)
page = r.json()
$ch = curl_init("https://formula-cream.pro/api/public/v1/strategist/reports?limit=10");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SAPPORT_API_KEY")],
]);
$page = json_decode(curl_exec($ch), true);
#GET /strategist/reports/{id}
| Параметр | Тип | Обязательно | Ограничения | Пример |
|---|---|---|---|---|
id | string (uuid) | да | Отчёт вашего арендатора | 3d8f1b52-6a47-4c9e-b0d3-9e51a2c7f804 |
Чужой или несуществующий отчёт — 404 not_found.
{
"id": "3d8f1b52-6a47-4c9e-b0d3-9e51a2c7f804",
"report_date": "2026-10-11",
"report_type": "daily",
"summary_text": "Трафик из поиска вырос, конверсия в заявку без изменений.",
"data_as_of": { "gsc": "2026-10-09", "tracker": "2026-10-11" },
"highlights": [
"Переходы из поиска выросли на странице про увлажняющие кремы"
],
"concerns": [
"Нет новых статей уже 12 дней"
],
"actions": [
{
"title": "Выпустить статью про выбор увлажняющего крема",
"priority": "high",
"rationale": "Запрос растёт, статьи по теме на сайте нет.",
"target_ref": "keyword:EXAMPLE",
"expected_effect": { "metric": "переходы из поиска", "delta": "+10%", "horizon_days": 30 },
"check_after_days": 21,
"basis": "own_measured"
}
],
"evidence": {
"actions": [
{
"index": 0,
"status": "supported",
"basis": "own_measured",
"items": [
{ "ref": "own:EXAMPLE", "kind": "own", "label": "Поисковые запросы за 28 дней" }
]
}
]
}
}
curl "https://formula-cream.pro/api/public/v1/strategist/reports/$REPORT_ID" \
-H "Authorization: Bearer $SAPPORT_API_KEY"
const res = await fetch(
`https://formula-cream.pro/api/public/v1/strategist/reports/${reportId}`,
{ headers: { Authorization: `Bearer ${process.env.SAPPORT_API_KEY}` } },
);
const report = await res.json();
for (const action of report.actions) {
console.log(action.priority, action.title);
}
r = httpx.get(
f"https://formula-cream.pro/api/public/v1/strategist/reports/{report_id}",
headers={"Authorization": f"Bearer {os.environ['SAPPORT_API_KEY']}"},
)
report = r.json()
for action in report["actions"]:
print(action["priority"], action["title"])
$ch = curl_init("https://formula-cream.pro/api/public/v1/strategist/reports/" . $reportId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SAPPORT_API_KEY")],
]);
$report = json_decode(curl_exec($ch), true);
#Событие
| Событие | Когда |
|---|---|
strategist.report.ready | Готов новый отчёт |
Нагрузка тонкая: тип события и report_id. Сам отчёт забирайте запросом GET /strategist/reports/{id}. Подпись, повторы и проверка событий — Вебхуки.
#Ошибки
Общий формат и каталог кодов — Соглашения. Для этих точек:
| Код | HTTP | Когда |
|---|---|---|
invalid_api_key | 401 | Ключ не принят |
scope_missing | 403 | Нет области strategist:read |
not_found | 404 | Отчёта нет или он чужой |
validation_failed | 422 | Неверный report_type, limit вне 1–100 или просроченный курсор (errors[].field = /cursor) |
rate_limited | 429 | Превышен предел частоты |
#Открытые вопросы
- Старые отчёты (техническое). Отчёты, созданные до появления структурных полей рекомендации (
target_ref,expected_effect,check_after_days,basis), хранят обоснование только текстом. Как отдавать их публично (пустые поля или перевод в новую структуру), не решено. - Идентификатор рекомендации (техническое). В кабинетной структуре у рекомендации может быть собственный
id, но не во всех отчётах. Нужен ли стабильныйidв публичном API, не решено. - Источник
data_as_of(техническое). Поле добавляется в DTO отчёта; из каких источников оно собирается (поисковая консоль, счётчик, CRM-счётчики), уточнится при выпуске. - Окно списка (техническое). Кабинетный список отдаёт шапки за последние
daysсуток (1–365, по умолчанию 90), не более 400 строк и не постраничный; в публичном API он заменяется курсорным.