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

Каталог 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 і нижче за картки для виклику агентами зі статті про MCP, Agent Skills і A2A та використовує ту саму механіку, що й заголовки 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 — тип зв'язку для ресурсу зі статусом чи станом сервісу, він теж із 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.