API-ключи
API-ключ - это учётные данные вашего аккаунта для Leadgram API. Управляются они в настройках: здесь вы создаёте ключ, даёте ему имя и отзываете, когда нужно. Разберём, что ключ умеет и чего не умеет, как его создать, как выдать ему права, как отправить им событие и как ротировать или отозвать.
Зачем нужны API-ключи
API-ключ позволяет внешнему инструменту или скрипту репортить конверсии в Leadgram от вашего имени, не передавая ему логин или сессионную куку. Это механизм для server-to-server интеграций: партнёрская сеть, CRM рекламодателя или собственный скрипт отправляют событие в момент депозита, а не человек кликает в дашборде. Для использования в браузере ключ не предназначен.
Создание ключа
- Откройте Настройки → API-ключи.
- Дайте ключу название, по которому узнаете его позже, например «Keitaro» или «скрипт отчётов» - это единственное поле в форме.
- Нажмите Создать ключ. Секрет показывается ровно один раз, сразу после создания, в блоке для копирования - скопируйте его сейчас же. Ни таблица ключей, ни какой-либо запрос больше не покажут полное значение - сохраняются только название и дата создания.

Что ключ может и чего не может
Ключ привязан к вашему аккаунту, но полноценным доступом к дашборду он не является, а объём его прав вы задаёте сами:
- Можно из коробки: отправлять события на
POST /api/events. Это единственное, что открыто ключу без отдельного права, и открыто оно только пока права этому ключу ни разу не сохраняли, и только по HTTP: на MCP-сервере это послабление не действует. - Можно выдать отдельно: чтение отчётов и ленты кликов; приём конверсий (слаг
events); создание кампаний, офферов, лендингов, воронок и Click Flows, а переключение статуса - только у офферов и лендингов (архивирование и возврат из архива) и у Click Flows (снятие с трафика и архивирование): отдельного права на статус у кампаний и воронок нет вовсе; отдельным уровнем - правку содержимого, публикацию графа в живой трафик, запись рекламного расхода, создание, правку и удаление правил постбэков и удаление сущностей. Каждое право даётся конкретному ключу, привязано к одной организации и по умолчанию выключено у всех уже выпущенных ключей. - Не выдаётся ничем: боты и их токены, каналы, рекламные кабинеты, рассылки подписчикам, биллинг и пополнение, участники команды, удаление организации, смена пароля и почты, отзыв сессий, кастомные домены, тестовая отправка правила постбэка и тестовое событие в рекламный кабинет, переотправка уже сделанной доставки, а ещё выдача прав самому ключу - ключ, который умеет расширить сам себя, это захват аккаунта, а не удобство. Запрос с ключом к
/api/auth/*получает403всегда, что бы владельцу ключа ни было доступно в браузере.
С выданными правами ключ работает не на одной поверхности, а на трёх. Кроме приёма конверсий это читающий API - GET /api/v1/clicks (постраничная лента кликов) и GET /api/v1/report (сводка воронки со счётчиками) - и MCP-сервер на POST https://leadgram.org/api/mcp, через который разговорный клиент (Claude, Cursor, любой другой хост MCP) работает вашими же правами: 25 инструментов, каждый исполняется на настоящем маршруте и упирается ровно в те же проверки. Каталог инструментов при этом не фильтруется по правам ключа: подключённому хосту показаны все 25, а про невыданное право он узнаёт на вызове - из 403, который называет нужный слаг. Параметры, коды ответа и лимиты обеих поверхностей - в Справочнике API.
Организация ключа - это не та рабочая область, что открыта у вас в дашборде. Пока ключу ни разу не сохраняли права, он не привязан ни к одной области явно: при вызове платформа берёт самое раннее живое членство владельца, то есть обычно пространство, созданное при регистрации. Если пространств у вас больше одного, события лягут не туда, куда вы смотрите. Лечится это одним действием: выдайте ключу права и выберите в них рабочую область явно - дальше ключ работает только в ней. Роль владельца при этом перечитывается на каждом вызове, поэтому ключ участника, которого разжаловали из админов, перестаёт работать сам. Исключение одно, и оно про дату выпуска: к ключу, выпущенному до 2 сентября 2026 года, проверка роли на POST /api/events по HTTP не применяется вовсе - он принимает конверсии и после разжалования владельца, и средство здесь одно: удалить такой ключ и выпустить новый.
Разбивки прав по участникам команды в текущем интерфейсе нет: если доступ к интеграциям нужен нескольким людям или системам, создайте ключ на каждую отдельно - так вы отличите их в таблице и сможете отозвать один, не сломав остальные.
Выдача прав
Права выдаются не при выпуске ключа, а отдельным действием, и у нового ключа не отмечено ничего:
- Откройте Настройки → API-ключи и нажмите Права в строке нужного ключа - откроется диалог «Права ключа».
- Выберите Рабочую область. Без неё сохранять права некуда: это и есть то поле, которое привязывает ключ к организации явно.
- Отметьте нужное в трёх блоках - «Чтение», «Запись», «Удаление и необратимое». Права называются машинными слагами (
campaigns:update), теми же, что печатают справочник API и текст отказа; они не переводятся специально. - Чтобы добавить право из блока «Удаление и необратимое», введите
DELETEв поле Подтверждение. Чтобы снять уже выданное, подтверждение не нужно. - При желании сузьте доступ до конкретных кампаний: блок Кампании → «Ограничить кампаниями».
- Нажмите Сохранить права. Блок «Сейчас выдано» показывает результат, а Снять все права отзывает выданное целиком - вместе с приёмом конверсий: после этой кнопки ключ не несёт ничего, включая
events, и в состояние нового ключа, который конверсии ещё принимает, он не возвращается.
Сохранение замещает прежние права целиком: работает ровно то, что отмечено в диалоге в момент нажатия. Добавить одно право, не трогая остальные, можно только отметив остальные заново.
Что именно можно отметить:
- Чтение:
report- сводка воронки,clicks- лента кликов. - Запись:
campaigns,offers,offers:status,landings,landings:status,funnels,click-flows,click-flows:status,events. - Удаление и необратимое:
campaigns:update,campaigns:delete,offers:update,offers:delete,landings:update,landings:delete,funnels:update,funnels:delete,click-flows:update,click-flows:delete,click-flows:publish,postbacks,postbacks:update,postbacks:delete,ad-spend.
По HTTP работает каждое из этих прав, а MCP-инструмент есть у всех, кроме одного: у click-flows:update его нет вовсе - парного чтения графа под ключ не существует, и разговорному клиенту осталось бы только затирать чужой граф вслепую.
Второй уровень читается как «необратимое по последствиям», а не как HTTP-метод DELETE. В нём лежат замена оффера и лендинга целиком (не переданные поля очищаются, а прочитать текущие значения ключом нельзя), публикация графа в живой трафик, создание правила постбэка - оно стреляет на каждой будущей конверсии на произвольный внешний адрес - и запись рекламного расхода, у которой пустая сумма удаляет строку. Ярлыка «обратимое» у первого уровня при этом нет: его худшая ветка - events, а приём конверсии списывает деньги организации и отправляет событие в подключённый рекламный кабинет, откуда его уже не забрать.
Ограничение по кампаниям доезжает не до каждого права. Сузить можно чтение, приём конверсий, всё про кампании и постбэки, запись расхода, удаление воронки и Click Flows, кроме click-flows:update. Офферы, лендинги, создание и переименование воронок и замена графа целиком общие на всю рабочую область по устройству модели, поэтому набор, где одновременно отмечены такое право и ограничение по кампаниям, не сохранится вовсе: редактор скажет об этом отдельной строкой, а прямой запрос к API получит 400 с grant_scope_unsupported_resource. Под эту проверку попадают только права записи и необратимого уровня: чтение в неё не входит, и сужение к нему применяется по-настоящему - report и clicks отдадут только выбранные кампании.
Если ключ уже работает в проде, первое же сохранение может его выключить. Пока ключу ни разу не сохраняли права, он принимает конверсии по временному послаблению для ключей, выпущенных до появления прав. После первого сохранения работает ровно отмеченное, и events, не отмеченный в блоке «Запись», перестаёт работать: живая интеграция начнёт получать 403 с кодом api_key_write_not_granted. Диалог предупреждает об этом отдельной строкой, но отметить events придётся вам. На MCP-сервер послабление не распространяется вовсе: там ключ без выданного events получает отказ всегда, даже пока по HTTP конверсии у него проходят.
Отправка события: POST /api/events
Ключ передаётся в заголовке x-api-key:
curl -X POST https://leadgram.org/api/events \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"clickId": "tz4k9m2p1xq7v0b3n8s6d5fg",
"type": "deposit",
"payout": 42.50,
"nonce": "crm-conversion-8813",
"payload": { "telegram_user_id": 123456789 }
}'
Поля тела:
clickId- обязательное. Id клика, который выдал Leadgram: это то значение, что приходит к вам из макроса{click_id}в URL оффера, редиректа кампании или лендинга. Клик чужой организации не подойдёт.type- обязательное, одно из:click,land,bot_start,channel_join,join_request,first_dm,miniapp_launch,lead,registration,deposit,ftd,purchase,subscription_renewed,subscription_cancelled,custom_1-custom_8. Принимать тип и производить его - разные вещи: уclick,landиfirst_dmвнутри Leadgram продюсера нет, они существуют только потому, что вы их прислали; а уbot_start,channel_join,join_request,miniapp_launch,purchaseи обоихsubscription_*продюсер внутри Leadgram уже есть - пришлёте их вручную, рядом с автоматическим появится второе событие. Единственное исключение -bot_start, и оно с условиями: ваш вызов сойдётся с уже записанным стартом, только если несёт telegram user id вpayload(без него - 422,bot_start_missing_telegram_user_id) и в запросе есть чем назвать бота, которого запустил человек -payload.bot_usernameилиpayload.bot_id, а если их нет, то бот названногоclickId. У клика из обычной редирект-кампании бота нет, как и у клика, чьего бота удалили: такой вызов безbot_username/bot_idпару назвать не может и запишется отдельным событием. Второго списания это вам не стоит - Leadgram списывает один раз за человека на рабочее пространство, а Telegram-аккаунт узнаёт этого человека всегда, так что дубль бесплатен, - но в Meta уйдёт второй Lead с другимevent_id, свести который никакая дедупликация уже не сможет, и оптимизатор вашей кампании выучит, что один человек сконвертился дважды. Подробности в Событиях и конверсиях.payout- необязательное. Выплата по этой конверсии: число от 0 до 1 000 000 (строка вида"42.50"тоже принимается, отрицательные значения отклоняются). Если поле не передать, конверсия унаследует «Выплату» оффера, привязанного к клику.nonce- необязательное, от 1 до 128 символов. Ключ идемпотентности: id события выводится из тройкиclickId+type+nonce, поэтому повтор того же запроса не создаст вторую строку и не задвоит ни списание, ни постбэки, ни конверсии в рекламных кабинетах. А вот две действительно разные конверсии одного клика (второй депозит) обязаны прислать разныйnonce. Вместо поля можно передать заголовокIdempotency-Key- он подхватывается как запасной источник того же значения.payload- необязательный объект произвольной формы, но три ключа внутри него платформа разбирает отдельно. Поtelegram_user_idсчитается биллинг и работает дедупликация постбэков по пользователю.bot_usernameиbot_idотносятся только кbot_startи называют бота, которого человек на самом деле запустил: @имя (со знаком@или без) либо внутренний id бота в Leadgram. Оба необязательны, но их стоит присылать, как только у вас больше одного бота: без них пара опирается на бота из клика, а это догадка, и она неверна, когда клик пришёл из кампании с другим ботом. Имя, которое не принадлежит вашим ботам, получает403, а не тихий откат к догадке.
Успешный ответ - 201 с созданной строкой события. 400 означает, что тело не прошло валидацию, 401 - ключ прислан, но не принят: неизвестен, отозван, выключен или истёк. Запрос вообще без заголовка x-api-key до этой проверки не доходит: его отбивает раньше 403 с телом CSRF: Origin header required for mutating requests, и это признак потерянного заголовка, а не проблемы с кликом. А 403 на самом маршруте приходит в шести разных случаях, и различить их можно только по телу отказа: клик не найден или принадлежит другой рабочей области - у этого отказа в теле одно поле error, ни code, ни hint в нём нет, и тем же отказом без code отвечает bot_start, у которого payload.bot_username или payload.bot_id называет бота чужой организации; ключ сужен по кампаниям, а клик лежит вне этого сужения или не привязан к кампании вовсе (code: campaign_scope_forbidden); ключу не выдано нужное право (code: api_key_write_not_granted, а на маршрутах необратимого уровня - api_key_delete_not_granted); владелец ключа больше не владелец и не админ организации (code: role_forbidden); права ключа не удалось разобрать, либо у них есть уровни записи и они не называют рабочую область (code: api_key_grant_unverifiable); права называют рабочую область, а живого членства владельца ключа в ней уже нет (code: api_key_org_not_member). Поле code есть у всех, кроме первого, а hint с готовой подсказкой - у всех, кроме первого и отказа по сужению кампаний; логируйте code: по одному номеру статуса причину не отличить.
Эндпоинт держит 1800 запросов в минуту на организацию; сверх этого приходит 429 с заголовком Retry-After, и это утверждение «вы выбрали свою квоту» - повторяйте после указанного интервала. Тело больше 1 МиБ отклоняется с 413 ещё до разбора: этот потолок стоит на любом пишущем запросе по ключу. 503 с Retry-After: 60 означает другое: временную недоступность на нашей стороне. Для запросов по ключу отказ на аварии всегда закрытый, то есть событие не записано и запрос надо повторить - с тем же nonce, это безопасно и есть рекомендованная стратегия ретрая: повтор либо запишет событие, либо схлопнется в уже записанное. Тот же смысл у 504: обработчик не уложился в 55-секундный бюджет, и приходит такой ответ только у запросов с заголовком Idempotency-Key - без заголовка обёртка с таймером не включается вовсе. 409 бывает двух видов: без поля code - запрос с тем же Idempotency-Key ещё выполняется, повторите чуть позже; с code: org_deleted - рабочую область удалили, и до её восстановления запрос не пройдёт.
Ротация и отзыв
Механизма «перегенерировать на месте» нет - секрет ключа фиксирован на всё время его жизни. Чтобы ротировать ключ: сначала создайте новый, подставьте его в интеграцию, убедитесь, что события проходят, и только потом отзовите старый ключ в таблице, чтобы ничего не осталось работать на нём. Отзыв происходит мгновенно - нажмите Отозвать в строке ключа, и он сразу перестаёт работать, без периода на переезд, поэтому сначала переключайтесь, потом отзывайте, а не наоборот.
Частые грабли
- Секрет показывается один раз, при создании. Закрыли диалог, не скопировав, - он потерян навсегда: отзывайте ключ и создавайте новый.
- Ключ пишет конверсии в вашу организацию, так что утёкший ключ - это чужие события в вашей аналитике и в ваших списаниях. Что он умеет сверх этого, зависит от выданных прав, а у нового ключа их нет. Но чем шире права, тем дороже утечка, и дорожает она не только удалением: одного права
postbacksхватает, чтобы завести правило, которое будет отправлять каждую вашу будущую конверсию на чужой адрес, аoffers:updateуводит живой трафик на чужой URL, не тронув ни одной кампании. Не вставляйте ключ в чат поддержки, публичный репозиторий или общий скрипт, а если утёк - ротируйте. - Забытый
nonceна ретраях. Без него повторная отправка того же события схлопнется в одну строку, и это хорошо, но вторая настоящая конверсия по тому же клику без новогоnonceпотеряется точно так же. - Чужой или устаревший
clickIdдаёт403, а не тихий успех: событие никуда не запишется, и молчание в отчётах будет означать именно это. У того же403есть и другие источники: клик лежит в другой рабочей области, чем та, в которую разрешился ключ; ключ сужен по кампаниям, а клик вне этого сужения (code: campaign_scope_forbidden); право просто не выдано (api_key_write_not_granted); владельца ключа разжаловали (role_forbidden). Различает их полеcodeтела отказа, и его отсутствие - тоже ответ:codeнет ровно тогда, когда клик не найден. - Ключ без сохранённых прав не привязан к рабочей области явно: платформа берёт самое раннее живое членство владельца, а не вашу активную область. С двумя пространствами события лягут в старое - выдайте права и выберите область в диалоге «Права ключа».
- У отзыва нет отмены и нет задержки: любая интеграция, которая всё ещё использует старый ключ, сразу начинает падать.
- Называйте ключи по тому, куда они подключены (например «Keitaro prod»), чтобы точно знать, какой отзывать, когда что-то сломалось, а не гадать по голой дате создания.