Аутентификация
Каждый запрос к API несёт учётные данные в заголовке Authorization. Их три вида.
Секретный API-ключ (pd_…)
Полный доступ к одному приложению с ваших серверов. Создаётся в Приложения → Настройки → API-ключи; открытый текст показывается один раз при создании, далее хранится только хеш.
Authorization: ApiKey pd_ВАШ_СЕКРЕТНЫЙ_КЛЮЧРаботает и в REST API, и в GraphQL:
curl -X POST https://api.pushdata.ru/api/graphql \
-H "Authorization: ApiKey pd_ВАШ_СЕКРЕТНЫЙ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"query":"{ contacts(appId: \"APP_ID\") { id externalId } }"}'Безопасность: никогда не встраивайте секретный ключ в клиентский код и не публикуйте его в репозиториях. При утечке немедленно отзовите его (Приложения → Настройки → API-ключи → Отозвать) и создайте новый.
Публичный API-ключ (pk_…)
Можно безопасно встраивать в браузеры и мобильные приложения. Ограничен публичной поверхностью независимо от схемы заголовка:
| Эндпоинт | Назначение |
|---|---|
POST /api/v1/public/devices, DELETE /api/v1/public/devices | Регистрация / деактивация вызывающего устройства |
POST /api/v1/events | Отчёт о delivered / opened / clicked |
GET /api/v1/public/inbox, …/unread_count, POST …/:id/read, POST …/read_all | In-app inbox вызывающего пользователя |
GET /api/v1/public/apps/:id/vapid | Публичный VAPID-ключ приложения для Web Push |
Authorization: Public pk_ВАШ_ПУБЛИЧНЫЙ_КЛЮЧЭти эндпоинты отвечают на CORS для любого origin. Публичный ключ на любом другом эндпоинте отклоняется с 403 forbidden.
Проверка идентичности
При включённой опции Приложения → Настройки → Проверка идентичности публичный запрос, заявляющий user_id, должен также нести X-Identity-Hash: <hex HMAC-SHA256(user_id, secret)>, вычисленный на вашем сервере с секретом проверки идентичности приложения. Без этого любой владелец публичного ключа мог бы зарегистрировать устройство (или прочитать inbox) под чужим id. Новое приложение создаётся с включённой проверкой: секрет показывается один раз при создании (рядом с API-ключом), в настройках его можно перевыпустить. Официальные клиенты шлют заголовок, как только ваш бэкенд выдаёт хеш: браузерный SDK через identityHash, iOS-пакет через identityHash, Android-клиент через registerDevice(token, userId, identityHash). Пока бэкенд его не выдаёт, регистрируйте устройства без user_id (анонимным устройствам хеш не нужен) или выключите проверку в настройках приложения — там при этом показывается предупреждение (user_id становится публичной информацией).
Роли в организации
Доступ к приложению даёт участие в его организации. owner управляет всем, включая биллинг, участников и удаление; admin дополнительно управляет API-ключами, учётными данными провайдеров и вебхуками; member делает повседневную работу (рассылки, контакты, шаблоны, каналы, сценарии); viewer всё видит и ничего не меняет: любая запись для viewer отклоняется с 403. API-ключи — не участники: секретный ключ действует с полными правами над своим приложением, публичный — только на публичной поверхности SDK.
Сессия дашборда
Панель входит без пароля: одноразовый код на e-mail (requestLoginCode → verifyLoginCode, шесть цифр, 10 минут, пять попыток), VK ID или Яндекс ID (/api/auth/oauth/vk|yandex/start). Первый вход неизвестного адреса создаёт аккаунт и организацию на тарифе Free (пока регистрация открыта; в режиме «по приглашению» аккаунт создаёт только ссылка из раздела Команда, а страница входа об этом предупреждает). VK ID и Яндекс ID привязываются к аккаунту с тем же e-mail: Яндекс подтверждает адрес — привязка сразу; VK ID — после кода на этот адрес (страница входа запросит его сама). Привязанные входы видны в Настройки → Безопасность → Внешние входы, там же их можно отвязать — вход по коду остаётся всегда. Сервер отвечает JWT (7 дней) и ставит его HttpOnly-cookie pd_session (SameSite=Lax, Secure в production); сам дашборд токен в браузере не хранит и аутентифицируется только cookie. Скрипт или CLI могут вместо этого передавать полученный токен как Authorization: Bearer <token>. logout гасит cookie и отзывает сам токен сеанса (его копия перестаёт работать); signOutEverywhere — Настройки → Безопасность → «Выйти на всех устройствах» — завершает все сеансы аккаунта сразу. Сессии — для людей; интеграции должны использовать API-ключи.
Аккаунт принадлежит пользователю: exportMyAccountData возвращает всё, что платформа хранит о вошедшем пользователе (профиль без секретов, участие в организациях, внешние входы), а deleteAccount(code) удаляет его — код из письма, отправленного на адрес аккаунта (requestLoginCode для своего e-mail), почта здесь — второй фактор. Организации, которыми пользователь владеет, обрабатываются явно: есть другой владелец — пользователь просто выходит; пользователь единственный участник — организация удаляется с приложениями и данными; есть другие участники, но нет другого владельца — отказ 409 transfer_ownership_first; у организации есть платежи — отказ 409 organization_has_payments (финансовые записи сохраняются). Обе операции — в Настройки → Данные и Удалить аккаунт.
Лимиты запросов
Каждый ответ несёт X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset. Лимиты считаются на приложение (по классу ключа) и на IP для анонимных запросов; 429 rate_limited означает «подождите до момента сброса». У публичного ключа свой бюджет на приложение и IP клиента, так что один посетитель не исчерпает бюджет серверной интеграции, а регистрация устройств по публичному ключу (POST /api/v1/public/devices) сидит в отдельной, более узкой корзине. 503 rate_limiter_unavailable с Retry-After означает, что хранилище лимитера было недоступно и запрос отклонён, а не пропущен: повторите через указанное число секунд.