Перейти к основному содержимому
БЛОГ

Каталог API по RFC 9727: публикуем /.well-known/api-catalog

Лишь у 4 из 74 API-провайдеров настоящий каталог RFC 9727. Опубликуйте /.well-known/api-catalog как application/linkset+json, свяжите OpenAPI, проверьте в curl.

Автор: IMozzОбновлено 2026-10-17
Каталог API по RFC 9727 — aiSiteReady

Каталог API по RFC 9727 — это один JSON-файл по адресу /.well-known/api-catalog с типом application/linkset+json, который перечисляет ваши публичные API и для каждого даёт ссылку на машиночитаемое описание, чаще всего на документ OpenAPI. Инструмент или ИИ-агент, которому известен только ваш домен, может запросить этот файл, пройти по ссылке service-desc и понять, что можно вызывать, не разбирая сайт с документацией. RFC 9727 — документ Standards Track, опубликованный в июне 2025 года (IETF RFC 9727).

В стеке готовности это шаг обнаружения для API. Он стоит выше файла OpenAPI и ниже «вызываемых» карточек из статьи как сделать сайт вызываемым для агентов и использует ту же механику, что и заголовки Link и sitemap: well-known URL плюс HTTP-заголовок, который на него указывает. Публикация занимает минут пятнадцать, если ваш файл OpenAPI уже общедоступен. Именно на этом «если» тихо ломается большинство каталогов.

Ключевые выводы

  • RFC 9727 регистрирует well-known URI и тип связи api-catalog. Каталог обязан (MUST) быть application/linkset+json и ссылаться на эндпоинты API; параметр profile и ссылки на OpenAPI — это SHOULD и RECOMMENDED (RFC 9727).
  • Каталог полезен ровно настолько, насколько полезен ресурс, на который указывает его service-desc. Если этот URL закрыт, отдаёт 404, HTML-страницу или невалидный JSON, агент ничего не узнает.
  • Публикующая сторона SHALL отвечать и на HEAD, и на GET по /.well-known/api-catalog, причём ответ на HEAD несёт заголовок Link. Часть роутеров отвечает только на GET. Наш так и делал, пока я не проверил его для этой статьи.
  • Внедрение пока на ранней стадии. Одна независимая проверка в мае 2026 года нашла настоящий каталог у 4 из 74 API-провайдеров, а 68 вернули 200 с HTML-страницей (API Evangelist). Это выборка одного автора, а не перепись.
  • Проверка api_catalog в aiSiteReady засчитывается, только если валидный каталог ведёт к валидному документу OpenAPI или JSON Schema. Просканируйте свой домен, чтобы увидеть, на каком шаге цепочка рвётся.

Что определяет RFC 9727?

RFC 9727 определяет предсказуемое место, где можно спросить: «Какие API публикует этот хост?» Он регистрирует well-known URI api-catalog и тип связи api-catalog и требует, чтобы документ за ними был Linkset — форматом из RFC 9264. Linkset — это самостоятельный документ из типизированных ссылок, у каждой из которых есть контекст (anchor), тип связи и цель.

Где живут сами API, RFC 9727 не диктует. Расположение документа каталога выбирает публикующая сторона, а /.well-known/api-catalog — лишь удобная ссылка на него (RFC 9727, раздел 2).

Основной нормативный текст умещается в одну таблицу.

ТребованиеУровеньRFC 9727
Отвечать на HTTPS GET по /.well-known/api-catalog каталогомSHALLраздел 2
Отвечать на HTTPS HEAD заголовком Link с типом связи из раздела 3SHALLраздел 2
Публиковать каталог как application/linkset+jsonMUSTразделы 4.2, 6.2
Добавлять profile="https://www.rfc-editor.org/info/rfc9727"SHOULDраздел 4.2
Включать гиперссылки на эндпоинты APIMUSTраздел 4.1
Добавлять метаданные: политики использования, версии, ссылки на OpenAPIRECOMMENDEDраздел 4.1
Отдавать каталог только по HTTPSSHOULDраздел 8
Ограничивать частоту запросов к каталогуRECOMMENDEDраздел 8
Проверить каталог на утечки внутренних или чувствительных данныхSHOULDраздел 8

Два правила легко пропустить. Если вы уже перечисляете свои API в другом формате, но хотите занять этот URI, вы обязаны (MUST) дополнительно отдавать Linkset, а свой формат можете (MAY) возвращать через content negotiation. И каталог может ссылаться на другие каталоги через тип связи api-catalog — так большой набор API делят между доменами (разделы 4.2, 4.3 и 5.1).

Как выглядит минимальный каталог API?

Минимальный каталог — это один JSON-объект с массивом linkset, в котором одна запись про ваш API. Вот весь обмен для одного API на example.com.

GET /.well-known/api-catalog HTTP/1.1
Host: example.com
Accept: application/linkset+json

HTTP/1.1 200 OK
Content-Type: application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727"
Cache-Control: public, max-age=3600
{
  "linkset": [
    {
      "anchor": "https://api.example.com/v1",
      "service-desc": [
        {
          "href": "https://api.example.com/v1/openapi.json",
          "type": "application/json"
        }
      ],
      "service-doc": [
        { "href": "https://api.example.com/docs", "type": "text/html" }
      ],
      "item": [
        { "href": "https://api.example.com/v1/orders" }
      ]
    }
  ]
}

Форма — это JSON-сериализация из RFC 9264: у объекта верхнего уровня единственный член linkset, и его значение — массив, даже если запись одна. У каждой записи есть необязательный anchor и по одному массиву на каждый тип связи. Каждой цели нужен href (RFC 9264, раздел 4.2). Тот же RFC рекомендует всегда указывать явный абсолютный anchor, чтобы документ оставался понятным, если его вынули из HTTP-ответа.

Аннотированный JSON каталога API: семь пометок связывают linkset, anchor, service-desc, href, type, service-doc и item с тем, что узнаёт клиент.

Всю работу делают названия связей. service-desc ведёт на машиночитаемое описание API, обычно файл OpenAPI, а service-doc — на документацию для людей. Оба типа связи, как и service-meta, пришли из RFC 8631, а item помечает цель, которая сама является API-членом каталога (RFC 9727, раздел 3.1).

ЧленЧто узнаёт клиентПравило
linksetНабор ссылок; всегда массив записейОбязан (MUST) быть единственным членом верхнего уровня
anchorО каком ресурсе ссылки, здесь — об APIМожет (MAY) отсутствовать; не следует (SHOULD NOT) делать относительным
service-descМашиночитаемое описание, обычно OpenAPIТип связи из RFC 8631
service-docДокументация для людейТип связи из RFC 8631
service-metaМашиночитаемые метаданные: политики, меткиТип связи из RFC 8631
itemAPI-член каталога, сам эндпоинтRFC 9727, раздел 3.1
hrefURL целиОбязан (MUST) присутствовать; не следует (SHOULD NOT) делать относительным
typeНеобязательная подсказка о типе содержимого целиНеобязательный атрибут цели

Как перечислить несколько API и выбрать нужные связи?

Добавьте по одной записи на каждый API, привяжите её к базовому URL этого API и укажите те связи, что у него есть. Полный каталог из трёх записей выглядит так. Первый API публикует всё, второй отдаёт спецификацию и в YAML, и в JSON, а третья запись указывает на каталог партнёра.

{
  "linkset": [
    {
      "anchor": "https://api.example.com/orders/v2",
      "service-desc": [
        { "href": "https://api.example.com/orders/v2/openapi.json", "type": "application/json" }
      ],
      "service-doc": [
        { "href": "https://developer.example.com/orders", "type": "text/html" }
      ],
      "service-meta": [
        { "href": "https://developer.example.com/orders/policies.json", "type": "application/json" }
      ],
      "status": [
        { "href": "https://api.example.com/orders/v2/health", "type": "application/json" }
      ],
      "item": [
        { "href": "https://api.example.com/orders/v2" }
      ]
    },
    {
      "anchor": "https://api.example.com/inventory/v1",
      "service-desc": [
        { "href": "https://api.example.com/inventory/v1/openapi.yaml", "type": "application/yaml" },
        { "href": "https://api.example.com/inventory/v1/openapi.json", "type": "application/json" }
      ],
      "service-doc": [
        { "href": "https://developer.example.com/inventory", "type": "text/html" }
      ],
      "item": [
        { "href": "https://api.example.com/inventory/v1" }
      ]
    },
    {
      "anchor": "https://www.example.com/.well-known/api-catalog",
      "api-catalog": [
        { "href": "https://partners.example.com/.well-known/api-catalog" }
      ]
    }
  ]
}

В собственных примерах RFC 9727 показаны два основных паттерна. В одном каждая запись привязана к API и несёт service-desc, service-doc, service-meta и status. В другом anchor — это URL каталога, а каждый API — закладка item (RFC 9727, приложение A). Это иллюстрации, а не альтернативы, между которыми нужно выбирать. Я их совмещаю: записи по каждому API, привязанные к самому API, плюс ссылка item, чтобы требование «ссылки на эндпоинты API» выполнялось самым прямым способом.

status — тип связи для ресурса со статусом или состоянием сервиса (health check), он тоже из RFC 8631. А service-meta берите для всего, что машине стоит знать о сервисе помимо спецификации: политик, меток.

Как клиенты находят /.well-known/api-catalog?

Двумя способами: запрашивают /.well-known/api-catalog напрямую или читают заголовок Link с rel="api-catalog" на странице, которую вы и так отдаёте. RFC 9727 допускает тип связи в HTTP-заголовке, в содержимом или в обоих местах сразу, а целью может быть и well-known URI, и любой другой URI на выбор публикующей стороны (раздел 3). Синтаксис заголовка — обычный веб-линкинг по RFC 8288 (RFC 8288).

Цепочка обнаружения: агент находит каталог по заголовку Link или по well-known URL, а затем идёт по service-desc к публичному документу OpenAPI.

Именно такой заголовок сегодня отдаёт главная страница aiSiteReady — одна связь из четырёх ссылок обнаружения. Цель относительная, поэтому заголовок остаётся верным на любом домене развёртывания.

GET / HTTP/1.1
Host: www.ai-siteready.com
Accept: text/html

HTTP/2 200
content-type: text/html;charset=utf-8
link: </sitemap.xml>; rel="sitemap", </llms.txt>; rel="describedby", </site.webmanifest>; rel="manifest", </.well-known/api-catalog>; rel="api-catalog"

В примере самого RFC 9727 каталог объявлен заголовком Link и элементом a с rel="api-catalog". Наш сканер читает только HTTP-заголовок корневой страницы, а не HTML-теги, поэтому заголовок надёжнее. Синтаксис подробнее разобран в руководстве по заголовкам Link.

Как отдавать /.well-known/api-catalog?

Возвращайте JSON с правильным Content-Type и на GET, и на HEAD, только по HTTPS. Вот настоящий маршрут из Nuxt-сервера aiSiteReady (Nitro), сокращённый до важных строк. Чистый билдер собирает JSON, а обработчик выставляет три заголовка, включая Link, которого требует раздел 2. Раньше этот маршрут отвечал только на GET; к этому изъяну я вернусь ниже.

// frontend/server/routes/.well-known/api-catalog.ts (фрагмент)
export default defineReadOnlyHandler((event) => {
  const config = useRuntimeConfig(event);
  const siteUrl = typeof config.public.siteUrl === "string" ? config.public.siteUrl : "";

  setResponseHeader(event, "Content-Type", API_CATALOG_CONTENT_TYPE);
  setResponseHeader(event, "Link", buildApiCatalogLinkHeader()); // </.well-known/api-catalog>; rel="api-catalog"
  setResponseHeader(event, "Cache-Control", "public, max-age=3600");

  return buildApiCatalog({ siteUrl });
});

defineReadOnlyHandler — наша небольшая обёртка: она запускает один и тот же обработчик для GET и HEAD, на HEAD отдаёт пустое тело с тем Content-Length, что был бы у GET, а на остальные методы отвечает 405. Content-Type берётся из одной константы: media type linkset, charset=utf-8 и профиль RFC 9727. charset — наша привычка, а не требование, и сканер его принимает. То же самое на Express короче, и Express сам отвечает на HEAD из маршрута GET (документация Express).

app.get('/.well-known/api-catalog', (req, res) => {
  res
    .set('Content-Type', 'application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727"')
    .set('Cache-Control', 'public, max-age=3600')
    .set('Link', '</.well-known/api-catalog>; rel="api-catalog"')
    .send(JSON.stringify(CATALOG, null, 2));
});

Я запустил именно этот обработчик локально: GET и HEAD вернули одинаковые заголовки. Побочный эффект: res.send() со строковым телом дописывает к Content-Type параметр charset=utf-8, который таблица ниже называет лишним. RFC не показывает полный обмен для HEAD; разумное прочтение раздела 2 — заголовок Link с rel="api-catalog", который как раз и шлёт этот обработчик. Эти детали HTTP лучше решить до релиза.

ДетальЧто делатьОснование
Content-Typeapplication/linkset+json и параметр profileMUST и SHOULD из раздела 4.2; RFC 9264 регистрирует profile единственным необязательным параметром, так что charset не нужен (UTF-8 обязателен)
GET200 и тело каталогаSHALL, раздел 2
HEAD200, те же заголовки, что у GET, и заголовок LinkSHALL, раздел 2; RFC 9110 говорит, что сервер SHOULD слать те же заголовки
Cache-ControlКороткий публичный TTL; у нас часRFC лишь советует кэширование и сжатие для больших каталогов
CORSСерверным агентам не нужен; * разрешайте, только если каталог должен читать браузерный кодМоя рекомендация; RFC упоминает CORS только для внутренних каталогов
TLSТолько HTTPSSHOULD, раздел 8
Неизвестные путиНастоящий 404, а не HTML-страница с 200См. ошибки ниже
Другие доменыРедиректить их well-known URL на канонический каталогRECOMMENDED, раздел 5.1

Как каталог связан с OpenAPI, MCP Server Card и Agent Skills?

Они отвечают на разные вопросы, и ни один не заменяет другой. Каталог говорит, какие API существуют и где лежат их описания. OpenAPI описывает один HTTP API. MCP Server Card говорит, как подключиться к MCP-серверу. Навык (skill) объясняет, как хорошо пользоваться вашим продуктом.

СлойНа какой вопрос отвечаетГде живётСтатус на октябрь 2026
Каталог APIКакие API публикует этот хост?/.well-known/api-catalogRFC 9727, июнь 2025
OpenAPIКакие операции и схемы у одного API?Цель service-descСпецификация v3.2.1, 10 сентября 2026
MCP Server CardКак подключиться к этому MCP-серверу?Любой URI на выбор публикующей стороныЭкспериментальный репозиторий
Agent SkillsКак агенту пользоваться этим продуктом?/.well-known/agent-skills/index.jsonDiscovery RFC помечен как черновик

OpenAPI допускает документ и в JSON, и в YAML и рекомендует называть главный документ openapi.json или openapi.yaml (OpenAPI Specification v3.2.1). Каталог на него просто ссылается.

Что до MCP: экспериментальный репозиторий Server Card в README пишет, что это прототипная работа, а не принятое или официальное расширение MCP, и что клиенты находят карточки через записи AI Catalog. RFC 9727 там не упоминается, так что не ждите, что запись в api-catalog объявит MCP-сервер (экспериментальный репозиторий server-card, AI Catalog). Публикуйте карточку отдельно: где именно, разбирает материал MCP Server Card: что это и как опубликовать.

Agent Skills — ещё один слой. Навык — это папка с файлом SKILL.md с инструкциями (спецификация Agent Skills), а веб-индекс для него пока черновик (discovery RFC от Cloudflare). Вместе они сочетаются хорошо: SKILL.md самого aiSiteReady подсказывает агентам, где лежат файл OpenAPI, интерактивная документация и каталог. Формат индекса показывает руководство по Agent Skills.

Какие ошибки ломают каталог API?

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

ОшибкаЧто происходитИсправление
Content-Type: application/jsonНарушает MUST; наша проверка сообщает api_catalog_not_linkset, если до валидной спецификации не дошлиОтдавайте application/linkset+json с profile
SPA-заглушка возвращает HTML с 200 по этому путиПарсеры спотыкаются; сканирование не видит каталога (api_catalog_missing) или сообщает api_catalog_html_response, если HTML спрятан под JSON-типомВозвращайте 404 на неизвестные пути и исключите /.well-known/ из fallback
Цель service-desc закрыта, отдаёт 404 или требует логинКаталог никуда не ведётОпубликуйте спецификацию по публичному URL и проверьте без cookie
Обратный прокси не пропускает путь к спецификацииСтандартный /openapi.json FastAPI уходит на фронтенд и даёт 404Перенесите спецификацию под путь, который прокси пропускает
Объект там, где нужен массив"service-desc": { "href": "..." } нарушает RFC 9264Оборачивайте цели каждой связи в массив
Относительный href или anchorСсылка теряет контекст при копировании; RFC 9264: SHOULD NOTСтройте абсолютные URL от канонического origin
OpenAPI только в YAMLНаша проверка читает только JSON и сообщает api_catalog_documents_invalid_jsonПубликуйте ещё и openapi.json
GET работает, HEAD даёт 404Нарушает SHALL из раздела 2Маршрутизируйте оба метода
В спецификации или каталоге внутренние APIRFC просит проверку перед развёртываниемПроведите аудит; фреймворки часто выводят все маршруты

У правила про массивы есть сноска, о которой полезно знать. В примере из RFC 9727, раздел 5.1, "api-catalog": "https://..." записано простой строкой. Поданный к RFC erratum (ID 9009, статус «Reported», не подтверждён) утверждает, что там должен быть массив с объектом ссылки, как требует RFC 9264 (RFC Editor errata). Следуйте форме из RFC 9264.

Чему меня научил собственный каталог aiSiteReady?

Каталог был самой лёгкой частью. Проверка api_catalog уже существовала, когда я выпустил собственный каталог aiSiteReady; трудным оказалось сделать достижимым файл, на который он ссылается. Билдер лежит в frontend/src/shared/lib/seo/api-catalog.ts и выдаёт одну запись с anchor /api/v1, с service-desc на /api/v1/openapi.json, service-doc на /api/v1/docs и item на /api/v1/scans.

FastAPI по умолчанию отдаёт /openapi.json и /docs. В проде наш nginx направляет на бэкенд только /api/* (и /health), а всё остальное — на фронтенд Nuxt, так что значения по умолчанию уходили на фронтенд и давали 404. Решением стало перенести их через openapi_url="/api/v1/openapi.json" и docs_url="/api/v1/docs": это меняет только адреса, а не содержимое схемы.

Менять nginx не пришлось: существующий блок location /api/ в deploy/nginx/default.conf уже пропускает оба адреса. Строку Content-Security-Policy я оставил во фрагменте, потому что она важна для страницы документации ниже.

# сокращено из deploy/nginx/default.conf
location /api/ {
    add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'" always;
    proxy_pass http://fastapi_backend/api/;
}

Проверка 2 октября 2026 года: прод отдаёт каталог с application/linkset+json; charset=utf-8; profile="https://www.rfc-editor.org/info/rfc9727", а спецификация по /api/v1/openapi.json возвращает 200 как application/json. Старый корневой /openapi.json возвращает 404, как и задумано.

Проверяя собственный сайт для этой статьи, я нашёл один новый изъян, а один известный снова вернул в свой список.

Новым был HEAD. 2 октября 2026 года curl -I по /.well-known/api-catalog возвращал 404. Файл маршрута оканчивался на .get.ts, а такой маршрут Nitro привязывает к GET, и запасного пути для HEAD у его роутера нет; маршруты sitemap.xml и llms.txt без суффикса отвечали на HEAD кодом 200. Это нарушало SHALL из раздела 2, а наш сканер HEAD пока не проверяет, так что сам этого не заметил. Исправление — маршрут выше: файл без суффикса, один обработчик для GET и HEAD и заголовок Link. Остальные .well-known-документы получили то же изменение.

Известный — страница документации. /api/v1/docs возвращает 200, но страница в браузере пустая. Content-Security-Policy на ответах /api/ начинается с default-src 'none', а это блокирует CDN-скрипты, нужные Swagger UI. Наша проверка проходит, если хотя бы один связанный документ — валидный OpenAPI, поэтому на пустую страницу натыкается человек, который переходит по service-doc. Он на момент написания не исправлен.

Как проверить каталог с помощью curl?

Запросите его так, как это сделал бы агент, проверьте статус и Content-Type, затем пройдите по service-desc и убедитесь, что спецификация разбирается. Запускайте проверку по публичному домену, а не по localhost: большинство поломок живёт в слое прокси и CDN.

# GET: ждём 200 и application/linkset+json с параметром profile
curl -sS -D - -o /dev/null -H 'Accept: application/linkset+json' https://example.com/.well-known/api-catalog
# HEAD: ждём 200 и заголовок Link с rel="api-catalog"
curl -sSI https://example.com/.well-known/api-catalog
# Идём по service-desc и проверяем, что это похоже на OpenAPI
curl -sS https://example.com/.well-known/api-catalog | jq -r '.linkset[]."service-desc"[]?.href'
curl -sSL https://api.example.com/v1/openapi.json | jq '{openapi, title: .info.title, paths: (.paths | length)}'

Первая команда должна напечатать строку статуса 200 и заголовок content-type: application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727". Последняя — версию OpenAPI, название API и ненулевое число путей. Встройте те же проверки в CI, чтобы деплой не мог незаметно сломать обнаружение.

#!/usr/bin/env bash
set -euo pipefail
SITE="${1:?usage: check-catalog.sh https://example.com}"
CAT="$SITE/.well-known/api-catalog"

H="$(curl -sS -D - -o /dev/null -H 'Accept: application/linkset+json' "$CAT")"
grep -qiE '^HTTP/[0-9.]+ 200' <<<"$H" || { echo 'FAIL: GET is not 200'; exit 1; }
grep -qi '^content-type: application/linkset+json' <<<"$H" || { echo 'FAIL: Content-Type'; exit 1; }
grep -qi 'profile="https://www.rfc-editor.org/info/rfc9727"' <<<"$H" || echo 'WARN: no RFC 9727 profile'

HH="$(curl -sS -I "$CAT")"
grep -qiE '^HTTP/[0-9.]+ 200' <<<"$HH" || echo 'WARN: HEAD is not 200'
grep -qi '^link:.*rel="\?api-catalog' <<<"$HH" || echo 'WARN: HEAD has no Link rel=api-catalog'

curl -sS "$CAT" | jq -r '.linkset[]."service-desc"[]?.href' | while read -r SPEC; do
  curl -sSfL "$SPEC" | jq -e '(.openapi|type=="string") and (.info|type=="object") and (.paths|type=="object" and length>0)' >/dev/null \
    && echo "OK: $SPEC" || { echo "FAIL: $SPEC is not valid OpenAPI JSON"; exit 1; }
done

Я запустил этот скрипт на aiSiteReady 2 октября 2026 года: он вывел два предупреждения про HEAD из изъяна выше, а затем OK для спецификации. На чистом сайте выводятся только строки OK.

Как сканировать /.well-known/api-catalog при каждом релизе?

Один раз сделать это руками нормально. Делать это на каждом релизе и по другим протокольным слоям — как раз работа для сканирования. Проверка api_catalog в aiSiteReady работает внутри категории Protocol/API. Она запрашивает /.well-known/api-catalog и любой заголовок Link с rel="api-catalog" на корневой странице — всё через SSRF-безопасный загрузчик, — а затем проверяет не более пяти связанных документов описания, начиная с service-desc.

Каталог считается валидным, если даёт хотя бы одну ссылку, а сканирование отдельно фиксирует, отдан ли он как application/linkset+json и несёт ли профиль RFC 9727. Описание считается валидным, если это документ OpenAPI со строкой версии, объектом info и непустым объектом paths, либо JSON Schema со структурным ключевым словом. Каждое сканирование заканчивается одним из четырёх статусов.

СтатусКогда
PASSВалидный каталог ведёт хотя бы к одному валидному документу OpenAPI или JSON Schema
PARTIALКаталог есть, но это не linkset или он ведёт к невалидным документам; по пути отдаётся HTML-страница под не-HTML типом содержимого; либо заголовок Link указывает на каталог, который не удаётся загрузить
FAILНичего не найдено ни по well-known пути, ни в заголовке Link, или по пути отдаётся только страница text/html
UNKNOWNЗапрос заблокирован или не уложился в таймаут; 403 и 429 попадают сюда, а не в FAIL

Сегодня формат linkset фиксируется как сигнал и не влияет на PASS: каталог в обычном JSON, который ведёт к валидному файлу OpenAPI, всё равно проходит. Не рассчитывайте на это, потому что он не соответствует стандарту. Для профиля Контентный сайт (content_site) проверка не запускается, а в профиле API/приложение (api_application) она весит в 1,75 раза больше, чем в профиле Все проверки (all). Все проверки и веса перечислены на странице методологии.

Встройте это в рутину вместе с аудитом готовности за 30 минут, а весь стек смотрите в материале что такое готовность к ИИ-агентам. Запустите бесплатное сканирование, чтобы увидеть, может ли агент пройти от вашего домена до валидного файла OpenAPI и какое исправление сделать первым, — на английском, украинском или русском.

Если коротко: опубликуйте один документ application/linkset+json по адресу /.well-known/api-catalog, свяжите файл OpenAPI каждого API через service-desc, отвечайте и на GET, и на HEAD и проверьте, что спецификация общедоступна. Каталог, который указывает в тупик, хуже, чем его отсутствие, потому что выглядит законченным.

У IMozz 20 лет в разработке ПО, а последний год он строит продукты с помощью LLM. Он развивает aiSiteReady, сканер только для чтения, который проверяет, может ли ИИ-агент прочитать и вызвать сайт. Сканер серверно рендерит собственный контент как рабочий пример.

Частые вопросы

RFC 9727 — это официальный стандарт или всё ещё черновик?
Это опубликованный RFC, а не черновик. RFC 9727 вышел в июне 2025 года как документ IETF Standards Track. Он регистрирует well-known URI и тип связи api-catalog и требует, чтобы каталог был Linkset с типом application/linkset+json. Внедрение пока только начинается: независимая проверка в мае 2026 года нашла настоящий каталог лишь у 4 из 74 API-провайдеров.
Какой media type обязан отдавать /.well-known/api-catalog?
application/linkset+json, он определён в RFC 9264. RFC 9727 требует (MUST) использовать его и рекомендует (SHOULD) добавить параметр profile со значением https://www.rfc-editor.org/info/rfc9727. Обычный application/json не соответствует стандарту. HTML-страница со статусом 200, частая у одностраничных приложений, хуже, чем 404: агент не может её разобрать.
Заменяет ли каталог API мой файл OpenAPI?
Нет. Каталог — это указатель, а не описание. Он перечисляет ваши API и для каждого даёт ссылку на документ OpenAPI через service-desc, а на документацию для людей — через service-doc. Агент по-прежнему читает файл OpenAPI, чтобы узнать операции и схемы, поэтому этот файл должен быть публичным, валидным и доступным ровно по тому URL, который указан в каталоге.
Нужно ли отвечать на HEAD-запросы к /.well-known/api-catalog?
Да, по тексту RFC. Публикующая сторона SHALL отвечать на HTTPS HEAD по этому пути ответом с заголовком Link, где указан тип связи api-catalog. Многие фреймворки по умолчанию маршрутизируют только GET, поэтому проверьте через curl -I. Express отвечает на HEAD из маршрута GET автоматически, а некоторые файловые роутеры возвращают 404.