Первый день с публичным API: quickstart, ключи, журнал
TL;DR: первый день с чужим API решают три вещи — как быстро вы делаете первый успешный запрос, где лежит ключ и есть ли журнал, чтобы разобрать первый же 4xx без тикета в поддержку. Ниже — quickstart за пять минут на реальных эндпоинтах BOTIX (`api.botix.pro/public/v1/*`), семь антипаттернов хранения ключа с рабочей схемой поверх них и разбор audit-лога в кабинете. Всё грунтовано на действующем API; секреты в примерах замаскированы.
Коротко
- Quickstart решает судьбу интеграции за 5 минут. Первый запрос — это
GET /public/v1/me: вернулproject_id,scopes,plan_keyи остаток rate-limit — ключ живой, API совместим. Если между копированием сниппета и видимым ответом есть лишние шаги — разработчик закрывает вкладку. - Ключ — это бессрочный пароль без 2FA и попыточного лимита. Семь антипаттернов (репозиторий, plaintext
.env, один ключ на команду, ключ в логах, ключ в URL, без ротации, избыточный scope) закрываются одной схемой: secret manager → ENV → SDK, маскирование в логах, свой ключ с минимальнымscopeна сервис. - Журнал API в кабинете — не диагностика поставщика, а самообслуживание интегратора.
app.botix.pro/settings?tab=api-keysпоказывает каждый запрос сrequest_id, а соседний таб «Webhooks» — историю доставок с retry-каскадом. Без него первый 4xx превращается из 15 секунд в полдня переписки.
Quickstart за пять минут: первый запрос решает всё
У публичного API нет второго шанса на первую встречу. Разработчик пришёл проверить одну гипотезу — «подходит ли этот API под мою задачу» — и каждая лишняя минута снижает шанс, что гипотеза вообще будет проверена. Если за пять-десять минут не получилось сделать первый осмысленный запрос и увидеть ответ, вкладка закрывается: появится другая задача, коллега предложит другое решение, ссылка потеряется в закладках. Это не лень, это фокус.
Отсюда главная ошибка — относиться к quickstart как к сокращённой документации. Это разные жанры. Документация отвечает на вопрос «как устроена система», её читают последовательно и возвращаются многократно. Quickstart отвечает на вопрос «как получить первый ответ от API прямо сейчас», его читают один раз и по диагонали. Признаки правильного quickstart, которые проверяются визуально:
- первый блок кода виден без скролла, на первом экране после заголовка;
- ключ выдаётся на этой же странице, а не «зарегистрируйтесь, подтвердите email, дождитесь письма, зайдите в кабинет, найдите вкладку»;
- сниппет содержит рабочие значения, а не плейсхолдеры
YOUR_API_KEY/https://api.example.com; - есть переключатель языка: curl и два-три языка с SDK;
- между копированием кода и видимым результатом нет промежуточных шагов.
В BOTIX первый запрос — это служебный GET /public/v1/me: он ничего не меняет, возвращает контекст ключа и годится как «пинг» на проверку всей цепочки.
# Проверяем сразу, что ключ живой
curl -X GET 'https://api.botix.pro/public/v1/me' \
-H 'Authorization: Bearer XXXXXXXX'200 OK с телом, где лежат project_id, scopes, plan_key и остаток rate-limit — ключ работает и API совместим. 401 — опечатка при копировании. Дальше — первая мутирующая операция, создание контакта:
curl -X POST 'https://api.botix.pro/public/v1/contacts' \
-H 'Authorization: Bearer XXXXXXXX' \
-H 'Content-Type: application/json' \
-d '{"phone":"+79991234567","name":"Test"}'Тот же запрос через SDK, ключ читается из окружения (почему именно так — в следующем разделе):
import os
from botix import Client
client = Client(api_key=XXXXXXXX["BOTIX_API_KEY"])
contact = client.contacts.create(phone="+79991234567", name="Test")Что обязательно должно быть в первом ответе. Первый запрос почти всегда заканчивается одним из четырёх исходов, и каждый требует подсказки:
- 200 — возвращённый объект содержит достаточно полей, чтобы было видно, что произошло: не
{"id":123}, а полный объект со временем создания и статусом. - 401 — ответ явно говорит, что не так с ключом: отсутствует, отозван, не имеет нужного scope. Без этого разработчик гадает.
- 422 — список ошибок валидации по конкретным полям (RFC 7807 Problem Details — хорошая отправная точка).
- 5xx — уникальный
request_id(UUID) в теле ответа, который можно приложить к обращению в поддержку. Без него диагностика на стороне поставщика занимает в десять раз больше времени.
Что осознанно отрезается из quickstart. Webhook-подписи, OAuth, идемпотентность, версионирование, полный список кодов ошибок — всё это критично для продакшена, но не для первого запроса. Если затащить их в quickstart, страница раздуется и потеряет жанр. Правильная структура — короткий линейный quickstart, от которого отходят ссылки на разделы, актуальные после первого успеха. Полная спецификация живёт в Redoc на developers.botix.pro/ (машиночитаемая — developers.botix.pro/openapi.yaml), а логика «первый запрос → SDK из OpenAPI → триал» подробно разобрана в Первый запрос к API за 5 минут и Онбординг разработчика: quickstart, sandbox, ключи.
Метрика, которая отвечает на вопрос «работает ли quickstart», — медианное время от открытия страницы до первого успешного запроса с того же ключа. Меньше пяти минут — quickstart делает свою работу. Число просмотров, время на странице, глубина прокрутки — вторичны: quickstart нужен не для того, чтобы его читали, а для того, чтобы после него сделали работающий запрос.
Где хранить ключ: семь антипаттернов и рабочая схема
API-ключ — это пароль, написанный в открытом виде. По умолчанию у него нет срока действия, нет второго фактора, нет попыточного лимита. Кто получил ключ — делает всё, что доступно тарифу проекта: читает контакты, шлёт сообщения, меняет сценарии, удаляет данные. Поэтому компрометация одного ключа — это не «инцидент в одной фиче», а потенциально весь проект. И почти все утечки происходят не из-за изощрённого взлома, а из-за семи повторяющихся антипаттернов.
Антипаттерн 1 — ключ в репозитории. Самый частый: $key = 'btx_live_...'; прямо в конфиге, коммит. Через неделю репозиторий становится публичным (open-source, переезд на GitHub, утечка бэкапа) — ключ доступен всем. Секрет-сканеры (GitHub Secret Scanning, GitLab Secret Detection) ловят паттерн btx_live_... и шлют владельцу уведомление — но между публикацией и отзывом проходят часы, за которые базу контактов успевают выгрузить.
Антипаттерн 2 — ключ в .env без шифрования. Файл .env лежит на диске в plaintext. Кто получил доступ к ФС (RCE-уязвимость, бэкап-копия, уволенный админ), читает все ключи разом. Классика — Docker-образ с забытым .env внутри, ушедший в публичный реестр. .env — приемлемое место для *переменной*, но не для *хранения*: ключ должен попадать туда из защищённого источника при деплое, а не лежать вечно.
Антипаттерн 3 — один ключ на всю команду. Master-ключ в Slack. За два года ушли пять человек, при увольнении ключ не ротировали — любой из них может вернуться и навредить. Контрмера — один ключ на исполнителя (бот, воркер, сервис), не на человека: когда сервис выводят из эксплуатации, отзывают именно его ключ.
Антипаттерн 4 — ключ в логах. Простой error_log(print_r($headers, true)) — и в логах полный Authorization: Bearer XXXXXXXX. Логи уходят в централизованный сборщик (ELK, Loki, Datadog), где их видят все с доступом к мониторингу. Полу-публичная утечка. Контрмера — маскирование чувствительных полей. Официальные SDK BOTIX делают это автоматически: при сериализации заголовков значение Authorization заменяется на Bearer ***. Руками фильтр добавляется явно:
import logging
class SecretFilter(logging.Filter):
def filter(self, record):
if hasattr(record, 'headers'):
record.headers = {
k: ('***' if k.lower() == 'authorization' else v)
for k, v in record.headers.items()
}
return True
logger = logging.getLogger()
logger.addFilter(SecretFilter())Антипаттерн 5 — ключ в URL. Конструкция ?api_key=... встречается реже, но не исчезла. URL пишется в access.log nginx, в логи прокси, в Referer при переходах на сторонние ресурсы — ключ размазан по всему сетевому стэку. BOTIX категорически не принимает ключ через query-string, только заголовок; это закреплено в app/Middleware/PublicApiAuth.php явной проверкой источника заголовка.
# Хорошо
curl -H 'Authorization: Bearer XXXXXXXX' \
'https://api.botix.pro/public/v1/contacts'
# Платформа отбивает такой запрос
curl 'https://api.botix.pro/public/v1/contacts?api_key=XXXXXXXX'Антипаттерн 6 — ключ без ротации. Ключ выпущен два года назад и до сих пор работает. За это время сменилась половина команды, прошли два рефакторинга, было подозрение на компрометацию («но не точно, оставим»). Бессрочный пароль тем опаснее, чем дольше живёт. Гигиена — плановая ротация раз в полгода плюс внеплановая после любого подозрения. Чтобы ротация была безболезненной, держат два состояния ключа: новый создан и работает, старый ещё не отозван; свитч в коде атомарный, отзыв старого — отложенный.
Антипаттерн 7 — ключ с избыточными правами. Сервис, который только шлёт сообщения, использует ключ с полным набором прав — при компрометации злоумышленник может удалять контакты и менять сценарии, хотя сервис делал только POST /messages. Контрмера — scopes на уровне ключа. В BOTIX scopes — это JSON-поле в btx_api_keys, middleware проверяет соответствие при каждом запросе. Несоответствие scope — 403 INSUFFICIENT_SCOPE. Воркеру отправки нужен ключ с messages:send, аналитическому скрипту — с contacts:read; утёк один — ущерб ограничен его scope.
Рабочая схема поверх всех семи. Источник правды — secret manager (HashiCorp Vault, AWS Secrets Manager, Yandex Lockbox, в простом случае — pass поверх gpg): ключ хранится зашифрованным, доступ выдаётся ролям, у каждого доступа свой audit-лог. При деплое скрипт читает ключ и подставляет в окружение процесса:
# Для текущей сессии
export BOTIX_API_KEY="btx_live_XXXXXXXX"
# .env кладём в .gitignore ДО первого коммита
echo "BOTIX_API_KEY=btx_live_XXXXXXXX" >> .env
echo ".env" >> .gitignoreSDK читает ключ только из ENV — ни строковой константы, ни конфига, ни файла с ключом. Если ENV не задан, SDK падает на старте с понятной ошибкой (это лучше, чем тихо подставить пустую строку и поймать 401 в проде). Контейнер пересобирается без ключа, ключ инжектится в момент запуска через docker run -e BOTIX_API_KEY=... или Kubernetes Secret. Примеры инициализации SDK на PHP/Python/Node из ENV — на developers.botix.pro/best-practices.
Чек-лист для код-ревью (каждая проверка — минута): .env в .gitignore; в исходниках нет строк на btx_live_; конфиг читает ключ через getenv(); SDK инициализируется один раз на старте процесса, а не на каждом запросе; в логах headers фильтруются; URL не содержат ключа; на каждый сервис — свой ключ, имя сервиса записано в поле name таблицы btx_api_keys. Как хранение ключа встраивается в общую безопасность интеграции — в API-ключи, audit-лог и OAuth: безопасность как UX и Zero-deps SDK, хранение API-ключа, open-source.
Журнал запросов в кабинете: отладка без тикетов
Рано или поздно в интеграции случается простая сцена: контакт не создался, заявка не привязалась, webhook не пришёл. Команда открывает свои логи и видит, что запрос отправлен. Дальше нужно понять — дошёл ли он до API поставщика, что ответил сервер и почему. Если у поставщика нет публичного журнала, единственный путь к ответу — тикет «у нас не работает, посмотрите со своей стороны». Это растягивает диагностику с пятнадцати секунд (взгляд в журнал) до полудня (тикет, переписка, эскалация), а на стороне интегратора в этот момент стоит продакшен. Поэтому журнал API — не диагностическая функция поставщика, а обязательный инструмент самообслуживания.
Что лежит в журнале. В BOTIX журнал доступен в кабинете владельца проекта на app.botix.pro/settings?tab=api-keys. По каждому запросу пишется: HTTP-метод, endpoint, статус-код, IP-адрес, user-agent, response_time_ms и уникальный request_id (UUID) — тот самый, что возвращается в теле ответа. Хранится это в таблице btx_api_log 90 дней; дальше архивация (пока не автоматизирована). Если интегратор пишет «не создался контакт, вот UUID из ответа» — запрос находится одной командой, и одной строкой видно, какой код был выдан. Доступ к журналу — только владельцу проекта, не агентам и не операторам: метаданные чувствительны для безопасности.
Чем это отличается от server-side логов. Серверный лог поставщика — это лог инфраструктуры: запросы всех клиентов, внутренние вызовы между сервисами, периодические задачи; в нём пересекаются данные разных тенантов. Журнал в кабинете — вид, отфильтрованный по проекту, с удалёнными чувствительными полями (тела запросов и ответов целиком обычно не хранятся, чтобы не утекли персональные данные через интерфейс) и с удобными фильтрами: по методу, по статусу (2xx / 4xx / 5xx по отдельности), по пути и поиском по request_id. Над журналом — детальная статистика: график за 30 дней, топ эндпоинтов и доля ошибок; это базовая обсервабилити, которую раньше собирали у себя в Datadog или Grafana.
Webhook delivery — соседний таб. Когда webhook-endpoint не получает событий, причин несколько: платформа не сгенерировала событие; сгенерировала, но не положила в очередь; положила, но получила timeout; получила 5xx и поставила в retry; получила 2xx, но интегратор перепутал тело. Для исходящих попыток доставки в журнале входящих запросов места нет — поэтому webhook delivery вынесен в соседний таб «Webhooks». По каждой попытке видно: событие, endpoint, HTTP-код ответа интегратора, длительность, тело ответа (первые 4 KB — прочитать сообщение об ошибке), номер попытки и время следующего retry. Retry-расписание на стороне сервера — 30 секунд / 5 минут / 30 минут / 2 часа / 6 часов; после 5 неудач попытка помечается failed. Интегратор открывает свой endpoint, видит, что вернул 500 на седьмое событие подряд, и понимает — ошибка на его стороне. Механику доставки целиком разбираем в Доставка webhook: HMAC, retry и идемпотентность.
Retention и compliance. Под аудит (SOC 2, ISO 27001, 152-ФЗ, GDPR) горячего участка на 90 дней хватает не всегда — энтерпрайз требует иногда год и дольше. Причина не в том, что аудитор ежедневно смотрит старые запросы, а в том, что инцидент может всплыть через два месяца, и журнал должен быть на месте. Тема упирается в стоимость хранения: при 10 RPS на проект 90 дней — это 70–80 миллионов записей. Индустриальное решение типовое — горячий журнал в основной БД с быстрой фильтрацией плюс архивный лог на нужный срок в холодном хранилище (S3 или аналог). В BOTIX архивация за пределами 90 дней пока не автоматизирована (TODO); когда появится клиент с требованием длинного retention, под него включат архивный режим — пометка выйдет на developers.botix.pro/best-practices. Отдельный нюанс — кто вправе скачать архивную выгрузку: по 152-ФЗ она содержит персональные данные (как минимум IP-адреса), поэтому «кнопка скачать за год» не работает — нужен контролируемый flow с подтверждением владельца.
Чего в журнале делать не нужно. Не показывать полные тела запросов и ответов — типичный источник утечек персональных данных. Не делать журнал в реальном времени с поллингом каждую секунду — в 99% случаев хватает обновления по кнопке. Не прятать журнал за платным тарифом — это инструмент, без которого интеграция страдает на любом тарифе, поэтому в BOTIX он доступен уже с триала. Как журнал сочетается с чтением данных и выбором polling vs webhook — в Чтение из API: cursor, polling vs webhook и audit-логи.
Первый день по шагам
Чтобы три темы выше сложились в рабочий первый день, порядок такой:
- Выпустите свой ключ, а не берите чужой «временно». Кабинет —
app.botix.pro/settings?tab=api-keys: «Создать ключ», имя сервиса в полеname(потом найдёте в журнале), список scopes. Полный ключ (btx_live_<40>) показывается один раз — в БД хранится только bcrypt-хеш (cost 12) и 12-символьный префикс для отображения. - Проверьте ключ пингом —
GET /public/v1/me.200сproject_idиscopes— цепочка работает. - Положите ключ в ENV сразу, не в исходник.
.env— в.gitignoreдо первого коммита. - Замаскируйте
Authorizationв логах — фильтр или штатный SDK. - Поставьте отдельный ключ с минимальным scope на каждый сервис.
- Откройте журнал после первого живого запроса и убедитесь, что видите свой
request_id. - Пробегитесь по статистике — доля 4xx/5xx, непривычные эндпоинты и IP настораживают в первую неделю особенно.
Всё вместе занимает меньше часа. Без этих шагов первая же утечка превращается в разбирательство на неделю; с ними — рутина интегратора.
Подводные камни
.env в .gitignore — строго ДО первого коммита с ключом. После коммита секрет остаётся в истории, простое удаление не помогает — только отзыв ключа в кабинете и выпуск нового. Docker-образы: если .env попал в слой и образ ушёл в публичный реестр, ключ виден всем — инжектим через docker run -e или Kubernetes Secret, не запекаем в образ. Slack-скриншот с консолью — тот же сценарий, проверяем перед отправкой. И помните про cross-project защиту: попытка достать чужой ресурс вернёт 404, а не 403 (существование объектов не утекает) — так что «пустой» ответ на отладке не всегда значит «нет данных», иногда значит «не ваш проект».
Попробуйте API BOTIX
Получите ключ, отправьте первый запрос и поймайте webhook — прямо в браузере, без боевого канала.
Попробовать API бесплатноЧитайте также
Первый запрос к API за 5 минут: SDK из OpenAPI и триал
Онбординг разработчика: quickstart, sandbox и API-ключи
API-ключи, audit-логи и OAuth 2.0: безопасность как часть UX
Как надёжно доставлять webhook: подпись, retry, симулятор
Zero-deps SDK BOTIX: подключение, ключи, open-source
Журнал для бизнеса — botix.pro/news
