← Все материалы про ИИ

Дашборд эффективности рекламы для трафик-агентства: Яндекс Директ, Метрика, Google Sheets и MCP с Claude Code

Пошаговая инструкция от performance‑маркетолога, который сам автоматизировал отчётность в агентстве: выгрузки по API, ограничения ЕПК, витрина в таблицах и вопросы к данным из чата Claude Code, Codex и Telegram.

Я performance-маркетолог, работал в трафик-агентстве и сам писал для него автоматизации: выгрузки из Яндекс Директа и Метрики, сводные отчёты для клиентов, алерты по перерасходу, связку рекламы с CRM. Начиналось всё с картины, знакомой любому агентству: в понедельник аккаунт-менеджеры полдня собирают отчёты руками, копируют цифры из Мастера отчётов в таблицу, а на вопрос «какой ДРР (доля рекламных расходов) был у клиента в прошлом месяце по кампаниям на поиске» отвечают через час.

В этой статье я разберу, как собрать дашборд эффективности рекламных кампаний для агентства с помощью Claude Code: забрать данные по API Яндекс Директа и Яндекс Метрики, сложить их в базу, вывести витрину в Google Sheets, а поверх всего этого поднять MCP-сервер, чтобы задавать вопросы по цифрам прямо из чата Claude Code, Codex или Telegram. Отдельно расскажу про ограничения API: часть данных по Единой перфоманс-кампании (ЕПК) получить в нужной детализации нельзя, и это надо закладывать в архитектуру с первого дня.

Зачем агентству свой дашборд, если есть Мастер отчётов

Мастер отчётов в Директе и отчёты Метрики хороши для одного клиента и одного специалиста. Когда клиентов десятки, начинаются проблемы:

🧮
Нет единой картины по всем клиентам
Руководитель агентства хочет видеть расход, CPL (стоимость лида) и ДРР по всем клиентам на одном экране, а не открывать 40 кабинетов.
🔀
Директ и Метрика считают по-разному
Расход живёт в Директе, выручка и цели — в Метрике или CRM. Модели атрибуции разные, и каждый специалист сводит их по-своему.
⏱️
Ручная сборка отчётов съедает часы
Время аккаунтов уходит на копирование цифр вместо анализа и оптимизации кампаний.
❓
Нельзя быстро задать вопрос
«Какие кампании за неделю потратили больше 50 тысяч и не дали ни одного целевого лида?» — такой вопрос требует отдельной выгрузки. С MCP-сервером это одна фраза в чате.

Архитектура: из чего состоит дашборд

Я строю такие системы в четыре слоя. Главное правило: сырые данные хранятся в базе, а не в таблице. Google Sheets — это витрина для людей, а не хранилище.

Яндекс Директ API (Reports) ─┐
                             ├─► сборщик на Python (cron, раз в сутки)
Яндекс Метрика API ──────────┘          │
                                        ▼
                          База: SQLite / PostgreSQL
                          (сырые таблицы + витрина daily)
                                        │
               ┌────────────────────────┼─────────────────────────┐
               ▼                        ▼                         ▼
        Google Sheets             MCP-сервер              DataLens / Looker Studio
    (сводка для клиентов)   (Claude Code, Codex,          (графики, если нужны)
                                  Telegram-бот)
  • Сборщики. Отдельный модуль на каждый источник: Директ, Метрика, при необходимости CRM. Каждый пишет в свою сырую таблицу.
  • База. Для небольшого агентства хватает SQLite-файла, при росте — PostgreSQL. Здесь же собирается витрина daily: клиент, дата, кампания, расход, клики, лиды, выручка.
  • Витрины. Google Sheets для сводок, которые можно открыть клиенту, и BI-инструмент для графиков.
  • MCP-сервер. Набор read-only инструментов поверх той же базы, через которые ИИ-агент отвечает на вопросы.

Как я веду такой проект в Claude Code

Claude Code — это ИИ-агент от Anthropic, который работает в терминале или в VS Code: читает код проекта, пишет файлы, запускает скрипты и тесты. Для проекта с выгрузками это удобно: документация API длинная, форматы отчётов капризные, а агент может сам сделать запрос, посмотреть на ответ и поправить парсер.

Шаг 1. Опишите проект в CLAUDE.md

Первым делом я кладу в корень репозитория файл CLAUDE.md. Claude Code читает его в начале каждой сессии. Туда идут термины, правила подсчёта метрик и ограничения — всё, что иначе пришлось бы объяснять заново:

CLAUDE.md — пример для проекта дашборда
# Дашборд агентства: Директ + Метрика

## Метрики
- ДРР = расход с НДС / выручка × 100%. Выручка — из Метрики (ecommerce) или CRM.
- CPL = расход / число лидов. Лид = достижение цели из config/goals.yaml.
- Атрибуция конверсий в Директе: LSC (последний значимый переход).

## Правила
- Токены только из .env, никогда не выводить их в лог и в ответ.
- Все API-вызовы только на чтение. Ставки и бюджеты не менять.
- Расход берём из отчёта уровня кампании — это источник истины.
  Детализированные отчёты сверяем с ним, разницу пишем в строку «не распределено».
- Последние 7 дней перезагружаем каждый запуск: Директ корректирует статистику задним числом.

## Команды
- python -m collector.direct --days 7
- python -m collector.metrika --days 7
- pytest

Шаг 2. Начните с режима планирования

Я не прошу агента «сделай дашборд». Я включаю режим планирования (plan mode) и даю конкретную задачу с ограничениями. Например:

Промпт для Claude Code
Напиши модуль collector/direct.py: выгрузка отчёта CAMPAIGN_PERFORMANCE_REPORT
из API Директа v5 для каждого клиента агентства (список — через AgencyClients.get).
Поля: Date, CampaignId, CampaignName, CampaignType, Impressions, Clicks, Cost,
Conversions по целям из config/goals.yaml, модель атрибуции LSC, с НДС.
Учитывай офлайн-режим отчётов (коды 201/202 и заголовок retryIn).
Результат — upsert в таблицу direct_campaign_daily в SQLite.
Сначала покажи план, потом напиши код и тесты на сохранённом TSV-ответе.

Дальше рабочий цикл такой: агент пишет код, запускает его на одном тестовом клиенте, я сверяю итог по расходу с интерфейсом Директа. Если цифры не сходятся, я прошу агента найти причину, а не «подогнать». Так всплывают почти все ограничения, о которых я пишу ниже.

🔐
Не вставляйте токены в чат

OAuth-токены Директа и Метрики храните в .env и добавьте его в .gitignore. Агенту достаточно знать имя переменной окружения, сам токен ему видеть не нужно.

API Яндекс Директа: доступ, отчёты, лимиты

Доступ для агентства

  • Приложение и токен. Зарегистрируйте OAuth-приложение на oauth.yandex.ru с правом доступа к API Директа и подайте заявку на доступ к API в интерфейсе Директа. Сначала обычно выдают тестовый доступ, для боевых данных нужен полный.
  • Агентский токен. Токен представителя агентства даёт доступ ко всем клиентам. Список клиентов отдаёт сервис AgencyClients, а в каждом запросе к данным конкретного клиента передаётся заголовок Client-Login.
  • Прямые клиенты. Если кабинет не под агентством, нужен токен владельца кабинета или его представителя. На практике у агентства всегда есть пара таких клиентов, поэтому токены лучше хранить в таблице «клиент → токен», а не в одной переменной.

Сервис Reports

Статистику отдаёт сервис Reports. Он возвращает TSV, а тяжёлые отчёты формирует в офлайн-режиме: сервер отвечает кодом 201 или 202 и заголовком retryIn, через сколько секунд повторить запрос. Минимальный рабочий вариант выглядит так:

Python — отчёт по кампаниям из API Директа
import os, time, requests

API = "https://api.direct.yandex.com/json/v5/reports"

def direct_campaign_report(client_login, date_from, date_to, goals):
    headers = {
        "Authorization": f"Bearer {os.environ['DIRECT_TOKEN']}",
        "Client-Login": client_login,
        "Accept-Language": "ru",
        "processingMode": "auto",
        "returnMoneyInMicros": "false",
        "skipReportHeader": "true",
        "skipReportSummary": "true",
    }
    body = {"params": {
        "SelectionCriteria": {"DateFrom": date_from, "DateTo": date_to},
        "FieldNames": ["Date", "CampaignId", "CampaignName", "CampaignType",
                       "Impressions", "Clicks", "Cost", "Conversions"],
        "Goals": goals,                  # ID целей Метрики
        "AttributionModels": ["LSC"],
        # имя отчёта должно быть уникальным для набора параметров
        "ReportName": f"campaigns_{client_login}_{date_from}_{date_to}",
        "ReportType": "CAMPAIGN_PERFORMANCE_REPORT",
        "DateRangeType": "CUSTOM_DATE",
        "Format": "TSV",
        "IncludeVAT": "YES",
    }}
    while True:
        r = requests.post(API, json=body, headers=headers, timeout=180)
        if r.status_code == 200:
            return r.text                # TSV, колонки Conversions_<goal>_LSC
        if r.status_code in (201, 202):
            time.sleep(int(r.headers.get("retryIn", 10)))
            continue
        r.raise_for_status()

Лимиты, о которых стоит знать заранее

  • Баллы API. Запросы к сервисам Директа расходуют баллы, суточный лимит считается на рекламодателя. Остаток приходит в заголовке ответа Units — логируйте его, иначе однажды выгрузка молча оборвётся на середине списка клиентов.
  • Очередь офлайн-отчётов. Число отчётов, которые одновременно формируются в офлайне, ограничено. Не запускайте выгрузку всех клиентов параллельно — ставьте их в очередь по одному или небольшими пачками.
  • Статистика меняется задним числом. Директ списывает недействительные клики и уточняет конверсии, поэтому последние дни я перезагружаю каждый запуск (upsert по ключу «клиент + дата + кампания»).
  • НДС. Решите один раз, считаете ли вы расход с НДС, и пропишите это в CLAUDE.md. ДРР с НДС и без НДС отличаются на размер налога, и клиент обязательно заметит расхождение с закрывающими документами.

Ограничения API: ЕПК и кампании, которые нельзя разложить по группировкам

Именно здесь дашборды начинают врать, поэтому на этом разделе я остановлюсь подробнее. Директ переводит рекламодателей на Единую перфоманс-кампанию (ЕПК), которая объединяет поиск, РСЯ, товарные и медийные форматы в одной кампании. Для рекламодателя это удобно, для аналитики хуже: API не всегда отдаёт данные ЕПК в той детализации, к которой все привыкли по классическим кампаниям «Текстово-графические объявления».

Что я вижу на проектах:

  • Сумма по детализированному отчёту не сходится с итогом кампании. Отчёт уровня кампании показывает полный расход, а отчёты по группам, объявлениям и условиям показа для части трафика ЕПК (автотаргетинг, товарные форматы, объявления, собранные из фида) приходят агрегированными или не приходят совсем.
  • Настройки кампании читаются не для всех типов. Для ЕПК в методе Campaigns.get нужно отдельно запрашивать блок полей UnifiedCampaignFieldNames. Типы кампаний, которые API не поддерживает, возвращаются с типом UNKNOWN: расход по ним в отчётах есть, а стратегию и бюджет через API не прочитать.
  • Поисковые запросы — только часть. Отчёт по поисковым запросам и раньше показывал не все запросы, а в ЕПК доля «невидимого» трафика бывает заметно выше.
  • Кампании из упрощённых интерфейсов. Кампании, созданные в Мастере кампаний или упрощённом режиме, ведут себя в API не так, как созданные в режиме эксперта. Перед запуском дашборда я выгружаю список кампаний клиента с типами и сверяю его с интерфейсом.
Что можно получить из API Директа и где подвох (по моей практике на сентябрь 2026 года; поддержка ЕПК в API регулярно меняется, сверяйтесь с документацией)
Что нужно Откуда в API Где подвох
Расход, клики, конверсии по кампании за деньReports, CAMPAIGN_PERFORMANCE_REPORTНадёжно. Это источник истины для расхода
Разрез по группам и объявлениямADGROUP_ и AD_PERFORMANCE_REPORTВ ЕПК часть трафика не раскладывается, сумма меньше итога кампании
Ключевые фразы и автотаргетингCRITERIA_PERFORMANCE_REPORTАвтотаргетинг и товарные условия показа детализированы слабо
Поисковые запросыSEARCH_QUERY_PERFORMANCE_REPORTОтдаётся только часть запросов
Площадки РСЯCUSTOM_REPORT с полем PlacementДля части форматов ЕПК площадки не детализируются
Стратегия и бюджет кампанииCampaigns.getДля ЕПК нужен UnifiedCampaignFieldNames, неподдерживаемые типы приходят как UNKNOWN
Конверсии по целямReports: Goals и AttributionModelsМодель атрибуции должна совпадать с той, что стоит в интерфейсе, иначе цифры разойдутся

Как я с этим живу

  1. Два уровня выгрузки. Отчёт уровня кампании — всегда, это расход и конверсии, на которых строится ДРР. Детализированные отчёты — дополнительно, для разборов.
  2. Строка «не распределено». Разницу между итогом кампании и суммой детализации я явно пишу в витрину отдельной строкой. Так дашборд честно показывает, какую часть расхода нельзя разложить, и никто не делает выводов по неполным данным.
  3. Автосверка. После каждой выгрузки скрипт сравнивает сумму расхода по детализации с итогом и пишет предупреждение, если расхождение больше порога. Порог подбирается под клиента.
  4. Разрез через UTM-метки, а не через API. Если группировка нужна для отчёта клиенту, я закладываю её в UTM-метки (utm_campaign={campaign_id}, utm_content={ad_id}, utm_term={keyword}) и беру разрез из Метрики. Расход при этом всё равно берётся из Директа на уровне кампании.
Проверка перед запуском дашборда

Выгрузите расход по всем кампаниям клиента за прошлый месяц через API и сравните с Мастером отчётов в интерфейсе. Затем сравните сумму по группам с суммой по кампаниям. Если первая пара сходится, а вторая нет, вы нашли кампании, которые API не раскладывает по группам. Эту проверку удобно поручить Claude Code одним промптом.

API Яндекс Метрики: цели, выручка, атрибуция

Из Метрики я забираю визиты, достижения целей и выручку. Для агрегатов подходит API отчётов (stat/v1/data), для сырых визитов с ClientID — Logs API. Сырые визиты нужны, когда выручку надо связать с CRM и офлайн-конверсиями.

Python — цели и выручка по UTM-кампаниям из Метрики
def metrika_by_campaign(counter_id, date1, date2, goal_id):
    r = requests.get("https://api-metrika.yandex.net/stat/v1/data", params={
        "ids": counter_id,
        "date1": date1, "date2": date2,
        "metrics": f"ym:s:visits,ym:s:goal{goal_id}reaches,ym:s:ecommerceRevenue",
        "dimensions": "ym:s:date,ym:s:lastsignUTMSource,ym:s:lastsignUTMCampaign",
        "accuracy": "full",     # без семплирования
        "limit": 100000,
    }, headers={"Authorization": f"OAuth {os.environ['METRIKA_TOKEN']}"}, timeout=120)
    r.raise_for_status()
    data = r.json()
    if data.get("sampled"):
        raise RuntimeError("Метрика вернула семплированные данные")
    return data["data"]

Подводные камни, на которые я натыкался:

  • Разная атрибуция. В Директе конверсии считаются по выбранной модели (в примере выше — последний значимый переход), в Метрике — по той, что указана в названии группировки (lastsign, last, first). Если модели разные, лиды в Директе и в Метрике не совпадут, и это нормально. Главное — в дашборде выбрать один источник для лидов и подписать его.
  • Семплирование. На больших счётчиках Метрика может отдать выборку. Проверяйте флаг sampled в ответе и дробите период, если он есть.
  • Склейка Директа и Метрики. Надёжнее всего связывать данные через utm_campaign={campaign_id}. Названия кампаний меняются, а ID — нет.
  • Мусорные лиды. Если на сайте нет проверки номера, в цели «Отправка формы» попадут боты и фейковые телефоны. CPL в дашборде будет красивым, а реальная стоимость целевого лида — нет. Поэтому для дашборда я ставлю цель на подтверждённый по SMS номер, а не на отправку формы.

Витрина в Google Sheets

Google-таблица остаётся самым удобным форматом для аккаунтов и клиентов: её можно открыть с телефона, дописать комментарий, построить сводную таблицу. Я выгружаю туда уже посчитанную витрину, а не сырые строки.

  1. Создайте сервисный аккаунт в Google Cloud, включите Google Sheets API и скачайте JSON-ключ.
  2. Откройте доступ к таблице на e-mail сервисного аккаунта с правами редактора.
  3. Запишите данные одним пакетным запросом: построчная запись быстро упирается в квоты API.
Python — выгрузка витрины в Google Sheets (gspread)
import gspread

gc = gspread.service_account(filename="service_account.json")
ws = gc.open_by_key(os.environ["SHEET_ID"]).worksheet("Сводка")

header = ["Клиент", "Кампания", "Расход", "Клики", "Лиды", "CPL", "Выручка", "ДРР, %"]
rows = build_summary(date_from, date_to)   # SELECT из витрины daily

ws.clear()
ws.update([header] + rows, value_input_option="USER_ENTERED")

Несколько практических правил:

  • Одна таблица на клиента, если вы открываете её клиенту. Одна общая — для руководителя агентства. Так проще управлять доступами.
  • Формулы — только поверх данных. Лист, куда пишет скрипт, не редактируйте руками: при следующей выгрузке он перезапишется. Расчёты и комментарии держите на соседних листах.
  • Следите за объёмом. У Google Sheets есть лимит ячеек на таблицу, а формулы начинают тормозить задолго до него. Дневная детализация по объявлениям за год для десятков клиентов в таблицу не поместится — для этого и нужна база.
  • Графики. Если нужны полноценные дашборды с фильтрами, подключите базу к Yandex DataLens или Looker Studio. Таблица при этом остаётся для ручного анализа.

Подробнее про выгрузки в таблицы и базы я писал в статье про выгрузку данных маркетплейсов по API: подход тот же, меняются только источники.

MCP-сервер: вопросы к данным из Claude Code и Codex

MCP (Model Context Protocol) — открытый протокол, через который ИИ-агент вызывает внешние инструменты. Вы описываете функции вроде «статистика по кампаниям клиента за период», и любой клиент с поддержкой MCP (Claude Code, Codex, Claude Desktop, ваш бот) может их вызывать. Агент сам решает, какие инструменты вызвать и как свести результаты в ответ.

Одно решение я принимаю сразу: MCP-сервер ходит в вашу базу, а не в API Директа напрямую. Так ответы быстрые, баллы API не тратятся, а цифры в чате совпадают с цифрами в таблице.

Python — MCP-сервер поверх витрины (пакет mcp)
import sqlite3
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("ads-dashboard")
DB = "ads.db"

def query(sql, params=()):
    con = sqlite3.connect(DB)
    con.row_factory = sqlite3.Row
    return [dict(r) for r in con.execute(sql, params)]

@mcp.tool()
def list_clients() -> list[str]:
    """Список клиентов агентства (логины Директа)."""
    return [r["client"] for r in query("SELECT DISTINCT client FROM daily ORDER BY client")]

@mcp.tool()
def campaign_stats(client: str, date_from: str, date_to: str) -> list[dict]:
    """Расход (с НДС), клики, лиды, выручка, CPL и ДРР по кампаниям клиента.
    Даты в формате YYYY-MM-DD. Строка 'не распределено' — расход,
    который API не раскладывает по кампаниям/группам."""
    rows = query("""
        SELECT campaign_name, SUM(cost) AS cost, SUM(clicks) AS clicks,
               SUM(leads) AS leads, SUM(revenue) AS revenue
        FROM daily WHERE client = ? AND date BETWEEN ? AND ?
        GROUP BY campaign_name ORDER BY cost DESC""", (client, date_from, date_to))
    for r in rows:
        r["cpl"] = round(r["cost"] / r["leads"]) if r["leads"] else None
        r["drr_pct"] = round(100 * r["cost"] / r["revenue"], 1) if r["revenue"] else None
    return rows

if __name__ == "__main__":
    mcp.run()   # stdio: для Claude Code и Codex на своей машине

Описания инструментов (docstring) — это инструкция для модели. Пишите в них единицы измерения, формат дат, с НДС расход или без. От этого напрямую зависит, насколько точно агент будет отвечать.

Подключение к Claude Code и Codex

Терминал — регистрация MCP-сервера
# Claude Code
claude mcp add ads-dashboard -- python /path/to/server.py

# Codex CLI
codex mcp add ads-dashboard -- python /path/to/server.py

После этого в чате можно спрашивать обычным языком:

  • «Какие кампании у клиента shop-msk за сентябрь дали ДРР выше 30%?»
  • «Сравни CPL по всем клиентам за эту и прошлую неделю, покажи, где рост больше 20%».
  • «Где за последние 7 дней был расход без единого лида?»

Если в агентстве пользуются Claude Desktop или веб-версией Claude, сервер можно запустить в режиме HTTP (mcp.run(transport="streamable-http")) на своём сервере и подключить его как удалённый коннектор. Такой же удалённый сервер нужен и для Telegram-бота.

Telegram-бот: вопросы по рекламе из мессенджера

Руководителю агентства или клиенту неудобно открывать терминал. Поэтому последний слой — Telegram-бот, который пересылает вопрос в Claude API вместе с адресом вашего MCP-сервера. Claude API сам подключается к серверу, вызывает инструменты и возвращает готовый ответ.

Python — Telegram-бот поверх MCP-сервера (python-telegram-bot + anthropic)
import os, anthropic
from telegram.ext import Application, MessageHandler, filters

claude = anthropic.AsyncAnthropic()            # ключ из ANTHROPIC_API_KEY
ALLOWED = {int(x) for x in os.environ["TG_ALLOWED_IDS"].split(",")}

async def answer(update, context):
    if update.effective_user.id not in ALLOWED:
        return
    resp = await claude.beta.messages.create(
        model="claude-opus-5",
        max_tokens=16000,
        betas=["mcp-client-2025-11-20", "server-side-fallback-2026-07-01"],
        fallbacks="default",                   # резервная модель, если запрос отклонён
        mcp_servers=[{
            "type": "url",
            "url": "https://mcp.example.ru/mcp",
            "name": "ads",
            "authorization_token": os.environ["MCP_TOKEN"],
        }],
        tools=[{"type": "mcp_toolset", "mcp_server_name": "ads"}],
        system="Ты аналитик трафик-агентства. Отвечай только по данным инструментов, "
               "указывай период и источник цифр. Расход с НДС.",
        messages=[{"role": "user", "content": update.message.text}],
    )
    text = "".join(b.text for b in resp.content if b.type == "text")
    await update.message.reply_text(text or "Не получилось ответить, попробуйте переформулировать")

app = Application.builder().token(os.environ["TG_TOKEN"]).build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, answer))
app.run_polling()

Что я проверяю перед запуском бота:

  • Сервер должен быть доступен из интернета по HTTPS. К MCP-серверу подключается инфраструктура Anthropic, а не ваш бот, поэтому localhost не подойдёт. Я ставлю сервер за nginx, который пропускает только запросы с правильным токеном в заголовке Authorization.
  • Белый список пользователей. Бот отвечает только тем Telegram ID, которые перечислены в настройках. Иначе любой, кто найдёт бота, увидит расходы ваших клиентов.
  • Клиентский бот — отдельный сервер. Если хотите дать бота клиенту, поднимите для него отдельный MCP-сервер с фильтром по его логину на уровне SQL, а не на уровне промпта.

Безопасность и данные клиентов

  • Только чтение. Все инструменты MCP-сервера читают из базы. Не давайте ИИ-агенту инструменты, которые меняют ставки, бюджеты или статусы кампаний. Если очень хочется автоматизации управления — делайте её отдельным скриптом с подтверждением человеком.
  • Без персональных данных. В витрину для MCP кладите агрегаты: расход, клики, число лидов, выручку. Телефоны и имена лидов из CRM туда попадать не должны: это и 152-ФЗ, и здравый смысл.
  • Договор с клиентом. Если данные клиента уходят во внешний ИИ-сервис, проверьте, что это не противоречит договору и NDA.
  • Логи. Сохраняйте, кто и что спрашивал у бота. Это помогает и в разборе ошибок, и в понимании, какие отчёты людям на самом деле нужны.

Короткий чек-лист

  1. Завести CLAUDE.md с определениями метрик (ДРР, CPL, НДС, атрибуция) и правилами работы с токенами.
  2. Получить доступ к API Директа для агентства, собрать список клиентов через AgencyClients.
  3. Выгружать отчёт уровня кампании как источник истины для расхода, последние дни перезагружать.
  4. Проверить, какие кампании (прежде всего ЕПК) API не раскладывает по группам, и вывести «не распределено» отдельной строкой.
  5. Склеить Директ и Метрику через utm_campaign={campaign_id}, выбрать один источник лидов.
  6. Считать лидом только подтверждённый контакт, чтобы CPL и ДРР не искажали боты.
  7. Выгрузить витрину в Google Sheets пакетной записью.
  8. Поднять read-only MCP-сервер поверх базы, подключить его к Claude Code, Codex и Telegram-боту.
💡
Нужен такой дашборд для вашего агентства?

Я настраиваю выгрузки из Директа и Метрики, витрины в Google Sheets и MCP-серверы с Telegram-ботом под процессы конкретного агентства. Напишите в Telegram, обсудим ваш стек и число клиентов.

Вопросы о дашборде и API Директа

Можно ли получить статистику Единой перфоманс-кампании через API Яндекс Директа?
Да, на уровне кампании расход, клики и конверсии ЕПК приходят в отчёте CAMPAIGN_PERFORMANCE_REPORT. Проблемы начинаются с детализацией: часть трафика ЕПК (автотаргетинг, товарные форматы, объявления из фида) не раскладывается по группам и объявлениям, и сумма детализированного отчёта получается меньше итога кампании. Я вывожу разницу в дашборде отдельной строкой «не распределено».
Почему расход в дашборде не совпадает с интерфейсом Директа?
Частые причины: разный учёт НДС (параметр IncludeVAT), статистика, скорректированная задним числом (списание недействительных кликов), и сумма по детализированному отчёту вместо отчёта уровня кампании. Перезагружайте последние дни каждый запуск и берите расход из отчёта по кампаниям.
Почему число лидов в Директе и в Метрике разное?
Директ и Метрика считают конверсии по разным моделям атрибуции, если их явно не выровнять. В отчёте Директа модель задаётся параметром AttributionModels, в Метрике — префиксом группировки (lastsign, last, first). Выберите один источник лидов для дашборда и подпишите его.
Зачем MCP-серверу своя база, почему не ходить в API Директа напрямую?
Прямые запросы тратят баллы API, тяжёлые отчёты формируются в офлайн-очереди, и ответ в чате приходил бы минутами. Кроме того, цифры в чате должны совпадать с Google Sheets. Поэтому MCP-сервер читает ту же витрину, что и таблица.
Можно ли подключить один MCP-сервер к Claude Code, Codex и Telegram одновременно?
Да. Локально сервер работает через stdio и подключается командами claude mcp add и codex mcp add. Для Telegram-бота и веб-версии Claude тот же код запускается в режиме HTTP на сервере с HTTPS и проверкой токена.
Безопасно ли давать ИИ-агенту доступ к рекламным кабинетам клиентов?
Я даю агенту только инструменты на чтение из своей базы, без доступа к изменению ставок и бюджетов. В витрину не попадают персональные данные лидов. Бот отвечает только пользователям из белого списка, а для клиентов поднимается отдельный сервер с фильтром по их кабинету.
Почему CPL в дашборде ниже, чем реальная стоимость клиента?
Если лидом считается любая отправка формы, в статистику попадают боты и фейковые номера. Ставьте цель на подтверждённый контакт, например после SMS-верификации номера, тогда CPL и ДРР в дашборде будут отражать целевых лидов.

Полезные материалы

Другие статьи и инструкции

Как снизить стоимость лида в Яндекс Директ Набор решений: СМС‑верификация, цели, офлайн‑конверсии, сквозная аналитика и CRM Фрод при оплате за конверсии в Директе Как не платить за заявки с несуществующими номерами, когда цена цели выше 1 000 ₽ Как снизить CPL с ID‑авторизацией Яндекс ID, Сбер ID, Т‑Банк и офлайн‑конверсии: обучение автостратегий на продажах из CRM Не приходит СМС‑код: причины и решение Чек‑лист для пользователя и владельца сайта: как не терять заявки, если код не дошёл Мобильный ID операторов на сайте Вход по номеру с push‑подтверждением от оператора и резервным СМС‑кодом СМС‑верификация формы на Tilda и Bitrix Живой пример OTP‑формы: ввод телефона, код из СМС, адаптивный дизайн SMS verification API на PHP и Node.js Для разработчиков: SDK формы, серверная генерация кода, webhook статусов, защита от SMS‑бомберов Дашборд рекламы для агентства с Claude Code API Директа и Метрики, ограничения ЕПК, Google Sheets и MCP‑сервер с Telegram‑ботом Офлайн‑конверсии из amoCRM в Метрику и Директ Пошаговая настройка через Google Таблицу и Apps Script: ClientID в сделке, загрузка через API, расчёт ДРР Как сделать сайт видимым в ChatGPT, Gemini и Claude SEO для ИИ‑поиска, уникальный контент и публичные MCP‑серверы с примерами ИИ‑менеджер по продажам: что умеет ИИ Возможности ИИ в автоматизации продаж по этапам воронки, внедрение по шагам и метрики Сквозная аналитика из WhatsApp Как доказать клиенту, что в WhatsApp пишут с рекламы, а не по сарафану HLR‑запрос: зачем он нужен для СМС‑рассылок Как проверить, что номер жив, до отправки СМС: статус, оператор, перенос номера и экономия на мёртвой базе Как почистить базу номеров перед рассылкой Формат, дубли, отписки, HLR и подтверждённые номера: порядок очистки без потери живых клиентов Можно ли делать СМС‑рассылку без имени отправителя Что уйдёт абоненту без регистрации подписи, чем рискуете и в каких случаях можно обойтись Рассылка по закону о рекламе: как не получить штраф за спам Согласие абонента, отписка, доказательство согласия и роль подтверждения номера Как сделать СМС‑рассылку по базе клиентов из amoCRM Какие поля и статусы выгружать, как сегментировать и не слать на мёртвые номера Офлайн‑конверсии Яндекса из amoCRM без платных виджетов Бесплатный коннектор Метрики за 10 минут: ClientID в сделку, статусы в цели, проверка журнала
Все статьи блога

Соберу дашборд и MCP-сервер для вашего агентства

Выгрузки из Директа и Метрики, витрина в Google Sheets, вопросы к данным из Claude Code, Codex и Telegram. От вас — доступы и список метрик.

Частые вопросы о СМС‑верификации и ID‑авторизации

tildasms.ru — сервис с опытом в настройке рекламы. Решение придумано именно для экономии на рекламе: снижения стоимости лида и борьбы с фродовыми заявками.

Что такое сервис верификации номера по СМС?
Это технология, которая подтверждает, что заявку на сайте оставил реальный человек с настоящим номером телефона: пользователь получает одноразовый код в СМС и вводит его в форме. tildasms.ru внедряет такую верификацию под ключ — мы много лет настраиваем рекламу и разработали это решение именно для того, чтобы клиенты экономили на трафике и не платили за фродовые и нецелевые заявки.
Как добавить проверку номера в форму на сайте?
Достаточно подключить наш JS‑скрипт к существующей форме — без переделки дизайна и вёрстки. Мы настраиваем интеграцию для Tilda, Bitrix, WordPress, OpenCart, Wix и других платформ за 1–3 дня, включая передачу данных и UTM‑меток в вашу CRM.
Как снизить стоимость лида в рекламе?
Внедрите верификацию номера телефона. Опыт настройки рекламных кампаний показывает: значительная часть бюджета в Яндекс Директе и других каналах уходит на фродовые и некачественные заявки. Отсекая их SMS‑верификацией или ID‑авторизацией, вы платите только за реальных клиентов — по нашей статистике стоимость целевого лида снижается на 40% и более.
Как зарегистрировать номер для отправки одноразовых СМС?
Номер отправителя регистрируется через SMS‑шлюз (например, SMSAero, SMS.ru или smsc.ru) — там же оформляется имя отправителя и тарифицируются сообщения. Мы помогаем подобрать подходящий шлюз, регистрируем отправителя и настраиваем отправку OTP‑кодов с вашего сайта под ключ.
Как внедрить коды через СМС на сайте?
Мы подключаем к вашей форме готовый OTP‑виджет: пользователь вводит номер, получает СМС с кодом, подтверждает его — и только после этого заявка уходит в CRM. Никакого программирования с вашей стороны не требуется, всё настраивается за 1–3 рабочих дня.
Как внедрить авторизацию через WB ID?
Авторизация через WB ID позволяет пользователю входить на сайт одной кнопкой, без ввода номера и оплаты SMS. Мы настраиваем OAuth‑интеграцию с WB ID и передачу полученных данных (телефон, ФИО) прямо в вашу CRM — это особенно эффективно для селлеров и партнёров Wildberries.
Как внедрить логин пользователя на сайте через Яндекс ID?
Подключаем кнопку входа через Яндекс ID к вашей форме или личному кабинету: пользователь авторизуется в один клик, а верифицированные ФИО, телефон и email автоматически попадают в CRM. Это дешевле SMS‑верификации в 3–5 раз и повышает конверсию формы.
Безопасна ли авторизация на сайте через Т‑Банк, Альфа, Сбер ID?
Да. Авторизация через ID банков-экосистем (Т‑Банк ID, Альфа ID, Сбер ID) использует защищённый протокол OAuth 2.0 — сайт не получает и не хранит пароли пользователя, только подтверждённые данные профиля. Мы настраиваем такую интеграцию и подключаем сразу несколько провайдеров на выбор пользователя.
Как внедрить авторизацию через Госуслуги, ВК ID?
Госуслуги и ВК ID дают максимально проверенные данные пользователя (вплоть до паспортных для Госуслуг) и подходят для ниш с высокими требованиями к достоверности контактов — недвижимость, финансы, визы. Мы подключаем нужный провайдер ID‑авторизации к вашей форме или кабинету и настраиваем передачу данных в CRM.
Задать свой вопрос в WhatsApp