apihub

Тем, кто публикует свой API

Документация провайдера

Как опубликовать API, подключить свой апстрим, назначить тарифы, получать деньги и не сломать интеграции потребителей при обновлении.

Содержание14
  1. 1Публикация
  2. 2Апстрим: что мы шлём и чего не шлём
  3. 3Видимость и модель денег
  4. 4Версии и ломающие изменения
  5. 5Тарифы
  6. 6Отладка и данные клиентов
  7. 7Кэш ответов
  8. 8Потоковые вызовы
  9. 9Наблюдение за вашим API
  10. 10Свой домен
  11. 11Снятие с публикации, «устарел» и удаление
  12. 12Роли в организации
  13. 13Деньги и выплаты
  14. 14Что проверить перед публикацией

1Публикация

  1. 1Создайте API: имя, короткое имя в адресе, адрес вашего апстрима, таймаут, видимость.
  2. 2Загрузите спецификацию OpenAPI 3. Из неё берётся всё: список операций, мок, песочница, фрагменты кода, SDK и сравнение версий.
  3. 3Назначьте тарифы.
  4. 4Опубликуйте. До публикации API виден только вам.

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

  • Спецификация разбирается без обращений по внешним ссылкам: $ref внутри документа работают, ссылки на чужие адреса — нет. Скачивать по ссылке из чужого файла значит ходить в сеть от нашего имени по чужому указанию.
  • Короткое имя API становится первым сегментом пути: /v1/ваше-имя/…. Менять его после публикации нельзя — это сломало бы всех, кто уже вызывает.
  • Мок отвечает примерами из спецификации. Чем подробнее примеры, тем полезнее песочница: путь от «нашёл API» до первого ответа — это то, где вас выбирают или не выбирают.
Адрес апстрима должен быть внешним. Адреса внутренних сетей отклоняются: по этому адресу ходим мы, своими сетевыми правами, и это была бы дыра в нашем периметре, открытая вашими руками.

2Апстрим: что мы шлём и чего не шлём

ЧтоКак
Авторизация у васЗаголовок, параметр запроса или ничего. Секрет шифруется и в ответах API площадки не показывается
X-Forwarded-ForНастоящий адрес вызывающего, а не наш
X-Request-IdИдентификатор вызова — тот же, что видит потребитель и что стоит в нашей трассе
Заголовки потребителяПроходят как есть, кроме X-API-Key и Authorization: ваш ключ доступа к вам мы не пересылаем чужому
ТаймаутВаш, из настроек API. По его истечении потребитель получает upstream_timeout, и вызов не тарифицируется

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

Если вы ограничиваете доступ к апстриму по адресу, разрешите адреса нашего шлюза, а не адреса потребителей: вызывать вас будем мы. Настоящий адрес потребителя вы получите в X-Forwarded-For.

3Видимость и модель денег

ВидимостьКто видитКак идут деньги
PUBLICВсе, включая поискЧерез площадку: мы выставляем счёт потребителю и платим вам за вычетом комиссии
PARTNERТолько организации, которым вы выдали доступЧерез площадку
PRIVATEТолько выВаш собственный биллинг: мы считаем вызовы и берём плату за обслуживание

Модель выводится из видимости, и отдельно её не выбирают. Решение открытое и с симметричной ценой: ушли из публичного каталога — потеряли витрину, начали платить за инфраструктуру. Значения по умолчанию у денежного поля нет нигде: однажды такое умолчание сделало расчёт холостым, и обнаружилось это только на прогоне в миллион вызовов.

  • Доступ партнёру выдаётся по короткому имени его организации и сопровождается заметкой — через полгода по ней вспомнят, за что он выдан.
  • Отзыв доступа гасит подписки партнёра сразу. Гасит, а не удаляет: всё, что он уже отработал, останется в счёте. Иначе отзыв был бы способом не выставлять счёт.
  • Смена видимости с публичной на партнёрскую не отбирает доступ у тех, кто уже подписан: это решение о витрине, а не о действующих договорённостях.

4Версии и ломающие изменения

При загрузке новой версии мы сравниваем её с предыдущей по смыслу, а не по тексту: переписанное описание, другой порядок ключей и переформатирование изменением не считаются.

ИзменениеЛомаетПочему
Удалён путь или операцияДаВызовы упираются в 404
Новый обязательный параметр запросаДаЛомает всех, кто его не шлёт
Поле пропало из ответаДаЧитающий его получает пустоту
Сменился тип поляДаРазбор на стороне потребителя падает
Удалён код ответаДаОбработчик этого кода перестаёт вызываться
Удалён способ авторизацииДаНастроенная интеграция перестаёт проходить
Новое необязательное поле в ответеНетКто не знает о нём, тот его не читает
Новое значение перечисления в ответеДаswitch без ветки по умолчанию встретит незнакомое
Убрано значение перечисления из запросаДаТот, кто его слал, получит отказ
  • Ломающее изменение требует новой мажорной версии. Выпуск под минорной отклоняется с перечислением того, что именно сломается, — не общим «есть несовместимости».
  • Changelog собирается из сравнения, если вы не написали его сами. Написанное руками мы не трогаем.
  • Подписчикам уходит отдельное событие о поломке, а не признак внутри «вышла версия»: событие, которое надо разворачивать, чтобы понять его важность, читают вполглаза.
  • Сравнение двух любых версий доступно и потребителю на странице API — он видит то же, что вы, и до перехода, а не после.

5Тарифы

ПолеЧто задаёт
ЦенаАбонплата за месяц. Берётся, даже если вызовов не было
КвотаВключённые вызовы за месяц. Пусто — без ограничения
Лимит в секундуЧастота. Защищает ваш апстрим, а не наш
Цена перерасходаСколько стоит вызов сверх квоты на мягком тарифе
Жёсткий лимитДа — 429 на границе квоты; нет — пропускаем и тарифицируем перерасход
  • Правка тарифа не меняет условия задним числом: у тарифа есть версии, и период считается по той, что действовала.
  • Смена тарифа посреди месяца делит абонплату и квоту по дням.
  • Бесплатный тариф — обычный тариф с нулевой ценой. Он же ваш главный инструмент: в кабинете видно, сколько потребителей перешло с него на платный.
  • Тариф, на который есть подписки, нельзя удалить — только скрыть от новых подписок.

6Отладка и данные клиентов

Захват заголовков и тел выключен по умолчанию и включается вами. Это единственное место, где «выключено» и есть правильное умолчание: включённый захват означал бы, что площадка начала хранить данные ваших клиентов, никого не спросив.

  • Заголовки авторизации вырезаются всегда. Свои чувствительные поля перечислите отдельно — они не попадут в запись вовсе, а не будут замазаны при показе.
  • Записывается выборка успешных вызовов и все ошибочные: отлаживают именно неудачные.
  • Тела сжимаются и шифруются, именно в таком порядке: шифротекст неотличим от случайных байтов, а те не сжимаются.
  • Срок хранения задаётся вами, в тарифе: от часа до года, по умолчанию час. Это условие тарифа, а не настройка отладки — от него зависит и то, за что платит потребитель, и то, сколько данных ваших клиентов лежит у нас. Правка срока рождает новую версию тарифа, а действующие подписки остаются на прежней.

Дайте потребителю проверить обработку отказов

curl 'https://apihub.tw1.su/v1/ваш-api/метод' \
  -H 'X-API-Key: ahk_test_…' \
  -H 'X-Sandbox-Chaos: latency=2000,error=503,rate=0.3'

Заголовок действует только на моках — на тестовых ключах и анонимных пробах. Подмешивать ошибки в настоящий трафик нельзя никому: это был бы способ вредить.

7Кэш ответов

Площадка умеет отдавать повторный ответ из памяти, не тревожа ваш апстрим. Кэшируемость объявляете вы, в самой спецификации: мы не угадываем, что у вас меняется раз в сутки, а что каждую секунду.

Объявление в операции

/lookup:
  get:
    x-apihub-cache:
      ttl: 86400        # сколько секунд ответ можно отдавать повторно
      vary: [code]      # по каким параметрам строится ключ
  • Без объявления не кэшируется ничего. Два соседних пути одного сервиса ведут себя по-разному, и по виду ответа их не различить: справочник живёт сутками, «сколько сейчас времени» не живёт вовсе.
  • Кэшируется только GET и только ответ 200. Заголовок Cache-Control: no-store, no-cache или private в вашем ответе отменяет кэширование этого конкретного ответа: объявление — про операцию вообще, заголовок — про этот раз.
  • Без vary в ключ идут все параметры запроса. Лишний параметр в ключе даёт лишний промах, недостающий — чужой ответ; умолчание ошибается в безопасную сторону.
  • Ключ общий для всех потребителей — в этом и смысл. Поэтому кэшируемым объявляйте только то, что одинаково для всех: ответ, зависящий от вызывающего, объявлять нельзя.
  • Выпуск новой версии обесценивает кэш сам: версия входит в ключ, и старые ответы просто перестают находиться.
  • Попадание помечено заголовком X-Apihub-Cache: hit — и у потребителя, и в трассе.
За попадание вам не платят: апстрим не работал. Цену попадания вы задаёте в тарифе, и она целиком идёт площадке — за память, в которой лежит ответ. Ноль по умолчанию: пока вы не назначили цену, попадания бесплатны для потребителя. Квоту попадание не расходует.

8Потоковые вызовы

Площадка проксирует Server-Sent Events и WebSocket. У таких вызовов нет ответа, который когда-нибудь кончится, и почти всё, на чём держится обычный путь, к ним неприменимо — поэтому и правила у них свои.

ЧтоКак
SSEОпределяется по спецификации: у операции в ответе объявлен text/event-stream
WebSocketПо заголовку Upgrade в запросе; спецификация его не описывает
ТаймаутТаймаут вашего API к потоку не применяется: он про «сколько ждать ответа»
Захват телВыключен: запись собирается до конца ответа, а конца может не быть
КэшНе применяется
Единица счётаМинута соединения, цена задаётся в тарифе
  • Минуты считаются по ходу соединения, а не в конце: соединение на четыре часа, посчитанное в момент разрыва, теряется целиком при перезапуске шлюза. Первая минута начисляется сразу — минута начата, минута оплачена.
  • Само соединение тарифицируемым вызовом не является: за него платят минутами, иначе час потока стоил бы минуты плюс вызов сверху.
  • Квоту в вызовах минуты не расходуют. Час соединения — это не шестьдесят запросов.
  • В отличие от попадания в кэш минуты делятся с вами как обычный вызов: соединение обслуживает ваш сервис, работа настоящая.
  • Число одновременных соединений на подписку ограничено. Открытое соединение занимает память и дескриптор, и один потребитель с циклом переподключения иначе занимает шлюз целиком.
Потоковым ответам площадка добавляет заголовок X-Accel-Buffering: no. Он для прокси, а не для клиента: nginx по умолчанию копит ответ целиком, а у потока этот «целиком» не наступает — без заголовка клиент не получает ничего.

9Наблюдение за вашим API

Площадка сама проверяет ваш API пробами и показывает результат отдельным разделом, рядом с общей статистикой, но не смешиваясь с ней: одно — то, что видели пользователи, другое — то, что видели мы. Скрыть этот раздел нельзя, иначе он не значил бы ничего.

  • Путь пробы берётся из спецификации: первая операция без обязательных параметров. Если такой нет, задайте путь сами — выдумывать значения за вас мы не станем.
  • Ответы 4xx считаются признаком работающего сервиса: 401 у API с авторизацией — это ответ, а не отказ.
  • «Данных мало» — отдельный статус, а не пустое место: молчание читалось бы как «плохо».
  • Наблюдение можно обнулить, но только при выпуске новой версии. Факт выпуска мы видим сами, верить на слово не нужно, а число сбросов показывается рядом с показателем.
  • Бейдж для README отдаётся картинкой и кэшируется на пять минут.

10Свой домен

Каталог можно показывать под своим именем: api.вашакомпания.ru вместо страницы внутри площадки. Порядок повторяет порядок в жизни: заявить домен, опубликовать TXT-запись, подтвердить.

  • Кнопка подтверждения не прячется до появления записи в DNS: когда ваша зона обновилась, знаете вы, а не мы.
  • Название, логотип и основной цвет задаются там же. Пустое значение означает «берите оформление площадки», а не пустоту.
  • Автовыпуск сертификата на домены провайдеров пока не сделан — это записанный долг, и мы говорим об этом до того, как вы настроите DNS.

11Снятие с публикации, «устарел» и удаление

Три разных действия, и путать их дорого: у каждого своя судьба тех, кто уже подписан.

ДействиеЧто с каталогомЧто с подписчиками
Снять с публикацииУходит из каталога и из шлюзаВызовы прекращаются: у подписчиков api_not_found. Подписки остаются и продолжают тарифицироваться абонплатой
Пометить «устарел»Остаётся в каталоге с пометкойПродолжают работать как прежде
УдалитьИсчезает совсемОтказ, пока подписан хоть кто-то
Снятие с публикации останавливает вызовы у тех, кто уже подписан, — это не «убрать из списка». Если вы хотите закрыть вход новым, а работающих не трогать, нужна пометка «устарел». Мы пишем это здесь прямо, потому что кнопка называется мягче, чем действует.
«Устарел» — правильный способ уйти. Он говорит новым «не начинайте», а старым даёт время; удаление после него становится безболезненным само собой, когда последняя подписка кончится.

12Роли в организации

РольЧто может у провайдера
OWNERВсё, включая деньги: выплаты, доход, тарифы
ADMINВсё, кроме владения организацией
DEVELOPERAPI, версии, спецификации, отладка. Денег не видит

Позвать сотрудника — на экране «Сотрудники»: адрес и роль, дальше вы передаёте ссылку сами. Приглашение именное и работает только у того, кто вошёл под указанным адресом; подробности — в документации потребителя, раздел про приложения и роли: механизм общий для обеих сторон.

13Деньги и выплаты

  • Вам платят за вызовы, которые обслужил ваш апстрим. Отказ на нашей стороне, ваша же пятисотка, таймаут и ответ из мока не тарифицируются.
  • Ошибка в запросе потребителя (4xx от вас) тарифицируется: вы её обработали.
  • Расчёт помесячный и только после того, как сошлась сверка. Не сошлась — ничего не выставляется, пока не разберёмся.
  • Выплата разделена на намерение и факт: подготовленная выплата не двигает книгу, проводка появляется, когда деньги ушли и есть номер поручения.
  • Комиссия площадки видна рядом с заработанным. Показать доход без неё значило бы разойтись с тем, что вы увидите у себя в выручке, и объясняться потом.
  • В кабинете виден доход по дням, топ-потребители и переход с бесплатного тарифа на платный.

14Что проверить перед публикацией

  1. 1Спецификация содержит примеры ответов у основных операций: по ним работают мок и песочница, а это первое, что увидит потребитель.
  2. 2Есть операция без обязательных параметров — или задан путь пробы вручную, иначе наблюдение не запустится.
  3. 3Таймаут выставлен по вашему настоящему времени ответа, а не «на всякий случай побольше»: он определяет, когда вызов перестанет тарифицироваться.
  4. 4Бесплатный тариф решён осознанно: с ним пробуют, без него уходят к тому, у кого он есть.
  5. 5Чувствительные поля перечислены в настройках захвата — до того, как захват включён, а не после.
  6. 6Апстрим доступен снаружи и не ограничен по адресу так, что наш шлюз в него не попадёт.