2026-09-14 · Прострочений план ще три дні відповідає, а не повертає 402
Коли оплачений період закінчується, запити тепер ще 3 дні обслуговуються за вашим тарифом. Раніше 402 SUBSCRIPTION_EXPIRED приходив у ту ж секунду, коли спливав expires_at, і інтеграція зупинялась, щойно оплата запізнилась.
Що робити з вашим кодом. Нічого обов’язкового. Якщо хочете дізнатися про прострочення раніше за своїх користувачів, логуйте X-Subscription-Status: grace і X-Subscription-Grace-Ends: вони приходять тільки в ці три дні. Після них відповідь, як і раніше, 402 SUBSCRIPTION_EXPIRED, а в details тепер є expired_at і grace_ended_at.
Те саме стосується add-on паків.
2026-09-13 · houseSystem відхиляє код, якого не існує
Раніше поле приймало будь-який рядок. Рушій читає з нього тільки першу літеру, тож невідома літера мовчки давала Placidus, а у відповіді houses.system повторював те, що ви надіслали. Тобто карта, порахована за Placidus, поверталася підписана системою, якої в розрахунку не було.
Тепер приймаються рівно ті 25 кодів, які вміє рушій: P K R C E W B M O A T V D F G H I i L N Q S U X Y. Регістр значущий, I та i це різні системи. Усе інше отримує 400 з переліком у тексті помилки.
Що робити з вашим кодом. Нічого, якщо ви надсилаєте код літерою або не надсилаєте поле зовсім: типове значення лишилось P. Якщо ви надсилали назву ("Placidus", "Koch"), запит почне повертати 400: раніше він працював випадково, бо збігалася перша літера, і так само випадково "Zodiac" давав Placidus. Замініть назву на код.
Перевірено на проді до зміни: houseSystem: "ZZZ" повертав куспід 2 на 192.865, байт у байт як "P", і при цьому system: "ZZZ".
2026-09-13 · allowlist більше не значиться в специфікації
origin_restriction.type: "allowlist" прибрано з openapi.json: у схемах CreateKey і UpdateKey лишилось два значення, server-only і public.
Що робити з вашим кодом. Нічого. Значення приймається й далі та означає те саме, що public, тож наявні виклики працюють без змін. Якщо ви генеруєте клієнт зі специфікації, після оновлення в ньому просто не буде третього варіанта, який дублював другий.
Це той анонсований крок, про який ішлося 12 вересня, коли обидва написання почали створювати однаковий ключ класу pk_. Прибрано саме з документа, а не з приймання: відмовляти у значенні, яке специфікація перелічувала ще вчора, гірше, ніж чесно сказати, що воно застаріле.
2026-09-13 · Ключ можна обмежити списком ендпоінтів
POST /v1/keys і PATCH /v1/keys/{id} приймають allowed_endpoints: список шляхів під /v1, точних (chart) або просторів імен (embed/*). Ключ зі списком викликає тільки те, що в списку, незалежно від тарифу.
Що робити з вашим кодом. Нічого, поки не задасте список: без нього поведінка не змінилась, і зараз списку немає в жодного ключа. Якщо задаєте, обробіть 403 ENDPOINT_NOT_IN_SCOPE, у details приходить сам список. null знімає обмеження, порожній список не приймається, бо читався б і як “усе”, і як “нічого”.
Обмеження діє на обидва класи ключів і на /v1/public/* та /v1/embed/* теж. Службові читання (/v1/auth/keys/me, /v1/keys/usage) з-під нього не звільнені, на відміну від кредитів: область це право, яке ви виписали ключу, а не тарифікація, і ключ у вихідному коді сторінки не повинен віддавати витрати акаунта.
2026-09-13 · Ключ для браузера став окремим класом pk_
POST /v1/keys з origin_restriction.type: "public" тепер видає ключ із префіксом pk_live_ або pk_test_ і вимагає непорожній allowed_origins: без списку приходить 400. Для цього класу правило походження зворотне до звичайного. Запит без Origin і без Referer отримує 403 ORIGIN_REQUIRED, а не проходить як серверний, і Origin: null з <iframe sandbox> рахується відсутнім походженням, а не дозволом.
Що робити з вашим кодом. Нічого, якщо ви користуєтесь звичайними ключами: aw_live_ і aw_test_ поводяться як раніше, режими server-only та allowlist не змінились, і жоден наявний ключ не переведено. Якщо ви розбираєте префікс ключа на своєму боці, додайте pk_. Якщо створюєте ключі через API з type: "public", додайте до запиту список доменів.
Раніше цей режим означав “будь-який origin”, тобто ключ, призначений лежати у вихідному коді сторінки, працював і з curl без жодного заголовка. Ключі, створені до 13 вересня, працюють як працювали; змінюється тільки те, що видається далі.
Заразом ключ, надісланий на /v1/public/* або /v1/embed/*, тепер справді перевіряється. Ці два префікси змонтовані до перевірки авторизації, тому політика походження на них не виконувалась зовсім: той самий server-only ключ отримував 403 на /v1/keys/usage і 200 на /v1/embed/moon-phase з тим самим чужим Origin. Запит без ключа на цих префіксах не змінився: він і далі анонімний, з лімітом за IP, бо саме так до нас приходить кожен віджет в <iframe>.
type: "allowlist" тепер видає той самий клас pk_, що й type: "public". Раніше цей режим створював секретний ключ, який приймався з перелічених доменів, але так само проходив і без походження взагалі, бо це звичайний серверний пропуск. Тобто саме той режим, який документація радила для віджетів, лишався слабшим. Значення нікуди не зникло і далі приймається, щоб не ламати згенерованих клієнтів: воно просто створює те саме, що public, і відповідь це показує - префікс ключа pk_ і origin_restriction.type: "public". Наявні ключі зі списком не переведені й працюють як працювали.
2026-09-13 · Ліміт запитів ділиться між дозволеними походженнями
Якщо у ключа є список дозволених походжень, кожен запис списку тепер отримує власну частку ліміту запитів: ліміт ключа поділений на кількість записів, округлено вгору. Раніше будь-яке одне походження могло вибрати весь ліміт ключа.
Що робити з вашим кодом. Нічого, якщо в списку один запис: частка дорівнює лімітові й поведінка не змінилась. Якщо записів кілька, врахуйте, що один ваш сайт більше не займе квоту решти. Додався 429 з поясненням, який саме запис вичерпано, і два заголовки, X-RateLimit-Origin-Limit та X-RateLimit-Origin-Remaining; вони приходять лише на запитах, пропущених за записом зі списку.
Лічильник ведеться за записом списку, а не за хостом, який прийшов: *.example.com це один лічильник на всі піддомени. Інакше обмеження обходилось би вигадуванням нового піддомена на кожен запит.
2026-09-13 · Модель сумісності перерахована, числа змінились
Бал сумісності рахується за новою моделлю. Поля ті самі (score, label, harmony, tension), типи ті самі, числа інші в кожній парі. Додалось поле top: п’ять найвагоміших аспектів, кожен зі своїм strength і знаком sign.
Зачеплені /v1/synastry, /v1/match/score, /v1/group-synastry, /v1/public/synastry і віджети на /v1/embed/*.
Що робити з вашим кодом. Якщо ви зберігали бал у себе, перерахуйте: та сама пара тепер дає інше число. Якщо ви порівнюєте бали між парами або сортуєте за ними, нічого робити не треба, шкала лишилась 0-100. Порогів ми не змінювали: challenging до 40, mixed до 52, balanced до 64, вище harmonious.
Чому. Попередня формула була harmony / (harmony + tension), і на краях вона ламалась: один трин давав рівно 100, один квадрат рівно 0, а з’єднання Сонця з Сатурном рахувалось гармонією. Нова модель зважує аспект і пару тіл, враховує мінорні аспекти, читає з’єднання по тілу, а не по парі, і нормує результат так, щоб середина шкали була серединою.
Разом з цим /v1/match/score перестав суперечити сам собі: його greenFlags і redFlags рахувались за старим правилом з’єднання, тож один і той самий аспект міг бути “зеленим прапорцем” і водночас входити в tension. Тепер прапорці й бал говорять одне й те саме.
Там же змінився порядок topAspects. Він рахувався окремою формулою ваги, тож “головні” аспекти не збігались з тими, з яких склався бал. Тепер це та сама вага, що й у top, і той самий порядок. Якщо ви берете topAspects[0] як головний аспект пари, це тепер інший аспект.
2026-09-12 · Бюджет кредитів на окремий ключ
Кредити спільні на весь акаунт, тож один ключ міг витратити їх усі. PATCH /v1/keys/{id} тепер приймає credits_cap_cycle: стеля саме для цього ключа, у кредитах за цикл.
Що робити з вашим кодом. Нічого, якщо бюджет не задавати: без нього поведінка не змінилась. Якщо задаєте, обробіть новий 429 KEY_BUDGET_EXHAUSTED. Він приходить, коли ключ вичерпав свій бюджет, навіть якщо на акаунті кредити ще лишились, і навіть на тарифі з overage: бюджет, який можна перевитратити за гроші, це не бюджет.
null знімає обмеження, 0 означає ключ, яким не можна витратити нічого. Лічильник той самий, що й у кредитів, і обнуляється разом із циклом тарифу. Поки бюджет заданий, кожна відповідь несе X-Key-Budget-Limit і X-Key-Budget-Remaining.
2026-09-12 · Список доменів браузерного ключа тепер редагується
PATCH /v1/keys/{id} приймав тільки name. Тепер він приймає й origin_restriction, тож домен до живого pk_ ключа додається без створення другого ключа.
Що робити з вашим кодом. Нічого, якщо список ваших доменів не змінюється. Якщо ви створювали новий ключ щоразу, коли треба було додати домен, тепер досить одного PATCH: старий ключ не доводиться лишати живим “про всяк випадок”.
Новий список діє з наступного запиту, id і сам рядок ключа лишаються ті самі, після rotate список зберігається. Клас ключа не редагується: pk_ не стає server-only, секретному aw_ список не додається, порожній список теж не приймається. Усі три випадки це 400 з поясненням, а не мовчазний перезапис.
Окремо: відповідь на невідомий ключ тепер називає обидва класи, aw_ для сервера і pk_ для браузера, замість самого лише “Invalid API key”.
2026-09-11 · BaZi: увесь класичний апарат, істинний сонячний час і одна депрекація
Вісім нових шляхів. POST /v1/bazi/chart віддає карту цілком; поруч односкладові hidden-stems (藏干), na-yin, life-stages (十二長生), symbolic-stars (神煞), interactions (刑冲合害) і strength. POST /v1/chinese/true-solar-time виправляє годинник під довготу місця народження.
Там, де традиції розходяться, віддаються обидва читання, а не одне: напрям іньських стовбурів у дванадцяти стадіях, обидві позиції іньського леза, вельможа для 庚, стихія з’єднання 午未. Пороги сили названі нашою конвенцією, а не каноном.
Депрекація. elementCounts, dominantElement і missingElements у відповіді /v1/bazi/element-balance рахують тільки рік і місяць, чотири ієрогліфи з восьми, і не відкривають гілки. Вони відповідають без змін до 2027-09-08 і несуть заголовки Deprecation і Sunset. Повний рахунок живе в новому об’єкті balance.
Що робити з вашим кодом. Якщо ви читаєте elementCounts, перейдіть на balance.visible (ті самі вісім ієрогліфів як цілі числа) або balance.weighted (з розподілом прихованих стовбурів). Якщо ні, нічого: усе інше додано, нічого не змінено. Опція trueSolarTime вимагає longitude, time і timezoneOffset разом, інакше 400.
2026-09-11 · Біоритми тепер віддаються даними, а не тільки картинкою
POST /v1/render/biorhythm малював три кривих, але прочитати числа з SVG було неможливо. З’явився POST /v1/wellness/biorhythm: ті самі цикли у вигляді JSON.
Що робити з вашим кодом. Нічого, якщо вам достатньо картинки: /render/biorhythm не змінився і малює те саме. Якщо ви розбирали SVG або рахували цикли самостійно, тепер можна брати готові значення.
На кожен день у відповіді приходить значення трьох циклів у діапазоні від -1 до 1 (помножте на 100, якщо потрібні відсотки) і номер дня життя. Окремо приходить список критичних днів: це дні, коли цикл перетинає нуль, а не дні з малим значенням. День народження критичний для всіх трьох циклів одразу, бо всі вони починаються з нуля.
birthDate обов’язковий, rangeStart і rangeEnd ні: без них береться сьогодні плюс 29 днів. Діапазон включає обидві межі, максимум 366 днів, перевернутий діапазон дає 400 замість порожньої відповіді.
Метод не має доказової бази: це нумерологічна конструкція кінця XIX століття. Відповідь несе про це окреме попередження, і ми не радимо будувати на ній нічого, крім розваги.
2026-09-10 · Сім опублікованих схем відповіді описували поля, яких API не віддає
Схема відповіді в /openapi.json будується з прикладу, який ми пишемо для кожного ендпоінта. Дванадцять таких прикладів описували поля, яких API не віддає. Тепер це захоплення живої відповіді.
| Ендпоінт | Було в схемі | Насправді |
|---|---|---|
POST /v1/keys | apiKey, rateLimit | api_key, api_key_masked, rate_limit, credits_limit, credits_used, origin_restriction |
GET /v1/keys/usage | topEndpoints[].total | topEndpoints[].requests і .credits, плюс блок credits |
GET /v1/status | uptime, ephemerisLoaded, wasmReady, epheFiles[] | key, usage, limits, endpoints, daily; без ключа 400 MISSING_KEY, а схема казала, що ключ не потрібен |
GET /v1/health | status, timestamp | плюс version і uptime_seconds, які й раніше приходили |
POST /v1/interpret/natal | summary, sections{} | interpretation, disclaimer, model, language, chart_summary |
POST /v1/interpret/synastry | summary, dynamics, outlook | interpretation, disclaimer, model, language |
POST /v1/interpret/element | element | interpretation, element_balance |
POST /v1/interpret/placement | placement | interpretation, requested_planet |
POST /v1/synastry | aspects[] з body1, chart1Planet | crossAspects[] з planet1, planet2, isApplying, плюс chart1, chart2, compatibility |
POST /v1/horoscope/daily | title, body, transitFacts[] | horoscope, disclaimer, model, language |
POST /v1/planets | planets[].speed | speedLong, speedLat, speedDist, плюс name, isRetrograde, declination |
80 шляхів /v1/numerology/* | lifepath, soulurge | lifePath, soulUrge: дефіс прибирали замість camelCase |
Самі відповіді не змінилися: помилка була в описі, а не в API.
Того ж роду було ще шістнадцять операцій, чия схема відповіді не називала
жодного поля: чотирнадцять каталогів Таро, чиї приклади читалися як
[...78 cards...] і не парсились узагалі, плюс /health і /version, які
несли приклад без схеми. Тепер усі приклади це захоплення живих відповідей.
Останні дві, POST /v1/mcp/streaming і POST /v1/mcp/tool-call-stream,
порожніми були не помилково: вони віддають text/event-stream, а не JSON, і
тепер спека так і каже, з описом форми кожного кадру. Генератор більше не
пообіцяє вам розбірний JSON там, де приходить потік.
Що зробити з вашим кодом. Якщо ви генерували типи з /openapi.json для цих ендпоінтів, перегенеруйте: поля, які ви типізували, у відповіді ніколи не приходили, і код, що на них покладався, отримував undefined. Найчастіший випадок: ключ, створений через POST /v1/keys, читається як api_key.
2026-09-09 · Ліліт малюється на всіх західних зображеннях, і з’явився bodies
Точка Ліліт (середній апогей Місяця) поверталася у відповіді /v1/chart під іменем mean Apogee, але не потрапляла на жодне зображення: гліф і зіставлення імен існували, а списки тіл у рендерерах її не несли. Тепер вона малюється гліфом ⚸ на wheel-western, bi-wheel, tri-wheel, composite, cosmogram і aspect-grid. На трьох ведичних форматах її свідомо немає: чорний Місяць не належить джйотішу.
Постер /v1/reports/stellaforge друкував сирий підпис ефемерид, ♆ ☌ mean Apogee, у списку аспектів. Тепер там гліф.
Що робити з вашим кодом. Якщо ви хочете попередній набір тіл, попросіть його явно: options.bodies приймає список і звужує типовий набір рендерера. Попередній вигляд це ["Sun","Moon","Mercury","Venus","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto","North Node","Chiron"]. Якщо поля немає, набір типовий, тож якщо ви нічого не міняєте, у вас нічого не ламається. Порожній список повертає 400, а не типовий набір: порожнім він майже завжди виходить випадково, і мовчазне повернення до типового приховало б це. Ім’я поза списком тіл, які ми вміємо малювати, теж повертає 400, а не зникає тихо.
2026-09-04 · Публічні ендпоінти: окрема квота для сайтів, які рендерять на сервері
Публічний доступ без ключа рахувався тільки за IP: 30 запитів на годину. Для браузера це правильно, бо кожен відвідувач приносить власний IP і власну квоту. Для сайту, який робить запити зі свого сервера, це не працює: одна машина ходить за всіх відвідувачів і вичерпує 30 запитів за кілька перезавантажень сторінки.
Тепер, якщо запит несе заголовок X-AstroWay-Site-URL з адресою сайту, діє інша квота: 300 запитів на годину на сайт і стеля 600 запитів на годину на IP. Без заголовка все як раніше, 30 на годину на IP.
Що робити з вашим кодом. Якщо ви ходите з браузера або вставляєте iframe-віджети /v1/embed/*, не робіть нічого: поведінка не змінилася, і квота, як і раніше, у кожного відвідувача своя. Якщо запити робить ваш сервер, додайте X-AstroWay-Site-URL: https://ваш-домен/ і кешуйте відповіді на своєму боці. Заголовок уже був дозволений у CORS, окремих налаштувань не потрібно.
Заголовок це декларація, а не облікові дані, тому стеля на IP залишається: скільки б доменів не назвали з однієї адреси, сумарно звідти піде не більше за 600 запитів на годину. Обидва лічильники потрібні одночасно: лічильник сайту спрацьовує, коли один сайт ходить з багатьох адрес (CDN, балансувальник), лічильник IP спрацьовує, коли з однієї адреси приходить багато імен.
У відповіді з’явився заголовок X-RateLimit-Scope зі значенням ip або site. Він каже, який лічильник зараз найтісніший, і решта заголовків X-RateLimit-* описують саме його.
2026-09-03 · Публічна синастрія без ключа, і планетарні години більше не брешуть про поточну годину
POST /v1/public/synastry віддає оцінку сумісності й сітку кросаспектів для двох карт народження без ключа. Відповідь навмисно маленька:
{ "score": 68, "label": "harmonious", "aspects": [{ "planetA": "Pluto", "planetB": "Saturn", "aspect": "Square", "orb": 0.0509, "applying": false }], "count": 45 }Аспекти відсортовані за орбом, найточніші перші, тож перші шість це ті самі шість, які показує віджет. За ключем лишається все інше, що віддає автентифікований POST /v1/synastry: тлумачення кожного аспекту, накладення по домах, композит і Davison, і самі дві натальні карти.
Виклик коштує 3 одиниці публічної корзини, а не 1. Це дві карти й матриця на 45 клітинок, тому рахувати його як фазу Місяця було б неправильно. Ліміти ті самі: 10 таких викликів на годину з незаявленої адреси, 100 на заявлений сайт, 200 на адресу. Відповідь несе заголовок X-RateLimit-Cost: 3.
POST /v1/planetary-hours: поле isCurrent більше не приходить порожньою обіцянкою. Воно публікувалося на всіх 24 годинах і на всіх 24 було false, бо роут ніколи не передавав рушію поточну годину. Тепер її передає той, хто питає: надішліть atLocalHour (десяткові години місцевого часу, наприклад 15.2), і година, що містить цей момент, отримає isCurrent: true. Без цього поля немає взагалі: відповідь кешується, і прапорець з годинника сервера все одно був би застарілим на момент рендеру.
Полярний день і полярна ніч більше не повертають 24 порожні години. Раніше відповідь казала, що планетарні години не визначені, і все одно віддавала таблицю, де всі проміжки нульові. Тепер hours порожній, а sunTimes.warning пояснює чому. Значення sunTimes.polarState описані в схемі: normal, polar-day, polar-night.
Що зробити з вашим кодом. Якщо ви читали isCurrent, він у вас завжди був false: додайте atLocalHour до запиту або рахуйте поточну годину самі з startHour і endHour. Якщо ви малюєте таблицю годин, перевірте порожній масив для полярних широт.
2026-08-31 · Аянамса тепер приймається за назвою, і десять сторінок документації виправлено
Ведичні ендпоінти завжди читали числовий ayanamsaId. Документація при цьому показувала "ayanamsa": "lahiri" на десятку сторінок, а схема була passthrough, тому рядок проходив валідацію і мовчки ігнорувався: відповідь рахувалась по Lahiri, що б ви не надіслали. Перевірено на /v1/vedic/dashas/vimshottari/maha: "lahiri" і "raman" давали ту саму сидеричну довготу Місяця, тоді як ayanamsaId 1 і 3 дають різні накшатри.
Тепер назва працює. Приймаються fagan-bradley, lahiri, de-luce, raman, krishnamurti (він же kp), yukteshwar, jn-bhasin, true-citra, djwhal-khul. Явний ayanamsaId має пріоритет, якщо надіслані обидва. Невідома назва тепер повертає 400 INVALID_FIELD зі списком допустимих замість того, щоб тихо відпрацювати по Lahiri. Обидва поля описані в /v1/openapi.json, тож потраплять у згенеровані клієнти.
Що зробити з кодом. Якщо ви надсилали ayanamsa з назвою, відмінною від lahiri, ваші відповіді досі рахувались по Lahiri, а тепер рахуватимуться по обраній школі: числа зміняться. Якщо надсилали назву поза списком вище, запит почне повертати 400.
Окремо виправлено десять сторінок документації, приклади на яких не працювали: POST /v1/keys приймає name, а не label; /v1/horoscope/daily це sign + date + language, а не натальна карта; /v1/transit-calendar чекає дані народження всередині natal; /v1/vedic/muhurat/* приймає search_window_start / search_window_end / topN. Ендпоінтів /v1/interpret/aspects і /v1/vedic/vargas не існує: замість другого є окремий шлях на кожну варгу, наприклад /v1/vedic/varga/D9.
Ще дві сторінки радили передавати zodiac=tropical заради тропічного Vedic. Такого поля немає, а ведичні ендпоінти в будь-якому разі рахують сайдерично і zodiacType у тілі ігнорують. Якщо ви це надсилали, воно нічого не робило; тропічні позиції дає західна гілка (/v1/chart).
2026-08-28 · Параметри запиту, які раніше не перевірялися, тепер описані й валідуються
Шість ендпоінтів читали поле, якого не було в їхній схемі. Таке поле не перевірялося й не потрапляло до /openapi.json, тож ішло в розрахунок як є і ламало відповідь трьома різними способами, і завжди під статусом 200.
Що робити з вашим кодом. Якщо ви надсилали коректні значення, не робіть нічого: поведінка не змінилася, числові рядки так само приводяться до чисел. Якщо ви надсилали щось некоректне, тепер прийде 400 з INVALID_INPUT замість тихо зіпсованої відповіді. Нові параметри тепер описані у /openapi.json, тож вони з’являться у SDK і MCP після наступної регенерації.
orb у /v1/parallel-aspects і /v1/midpoint-trees був найнебезпечнішим: нечислове значення робило хибним кожне порівняння, і замість шести аспектів чи десяти дерев поверталася порожня відповідь. Порожній масив читається як “нічого не знайдено”, а не як “поганий запит”, тож помітити це на своєму боці було неможливо.
targetDate у /v1/vedic/jaimini/dasha-summary і /v1/vedic/doshas/kp/sade-sati повертав усі даші як null. key і maxAge у /v1/primary-directions тихо псували вибірку: key тут це швидкість у градусах на рік, а не назва режиму, і тепер це число з межами.
Друга хвиля закрила клас повністю. parts у /v1/arabic-parts і maxYears у /v1/firdaria так само віддавали порожній результат замість 20 жеребів і 63 періодів. termsData і decansData у /v1/almuten, /v1/essential-dignities, /v1/hyleg і /v1/receptions роняли обробник у 500. /v1/hd/incarnation-cross узагалі не перевіряв номери воріт, а /v1/human-design/transits брав натальну дату, якої не було в схемі, і накладав транзити на хибну карту, і відповідь при цьому виглядала звичайною.
2026-08-27 · Специфікація тепер описує кожне тіло запиту й відповіді публічних роутів
/v1/openapi.json віддавав для 246 шляхів порожнє тіло запиту: {"type": "object"} без жодного поля. Ендпоінти працювали й перевіряли вхід як завжди, але специфікація про цей вхід не казала нічого, тому згенерований із неї клієнт не мав типу для запиту, а агент, який читає схему, не бачив назв полів. Тепер таких шляхів нуль.
Найбільші групи: нумерологія (50 шляхів), таро (47), китайський блок (14), тварини (14), варги (16), дзи-вей (13), бізнес (11), базі (11), фінанси (10), велнес (9), майя (8). Двадцять шість шляхів, чиї обробники не читають тіло взагалі (16 фігур геомантії, 5 ліній хіромантії, чотири стріми, каталог рун), тепер посилаються на EmptyBody: раніше специфікація казала “тіло може бути будь-яким”, тепер каже правду - його немає.
PATCH і PUT не мали тіла в специфікації взагалі. Генератор описував його тільки для POST, тому PATCH /v1/whitelabel/config не публікував нічого, хоча роут перевіряє кожен виклик. Тепер у нього є схема.
Вісім публічних роутів не мали схеми відповіді. По всій специфікації її мали 733 операції з 758, а без неї лишалися саме ті, які читають server-side без ключа: POST /v1/public/chart, POST /v1/public/human-design, три гороскопи, GET /v1/public/tarot/daily, /moon-phase і /planet-of-day. Тепер кожна віддає ok, data і _footer з полями всередині; обидва POST беруть схему в свого автентифікованого двійника, тож поле, додане в /v1/chart, не може зникнути з /v1/public/chart.
Заразом виправлені надруковані приклади, які були неправильні: у публічному таро position і card показувалися рядками, хоча це об’єкти, а /public/moon-phase і /public/planet-of-day не показували localized взагалі. Приклади /v1/chart і /v1/human-design не містили семи й шести полів верхнього рівня відповідно, а channels був надрукований у формі, якої рушій ніколи не повертав.
Що зробити з вашим кодом. Нічого, якщо ви пишете запити руками: жодне поле не перейменоване, не додане й не прибране, валідація та сама. Якщо ви генеруєте клієнт зі специфікації, перегенеруйте його: там, де раніше було unknown, тепер тип із полями, і публічні відповіді теж типізовані.
2026-08-24 · Документація ведичної сумісності виправлена, тіло запиту це chart1/chart2
Сторінка /vedic/compatibility/ показувала тіло запиту з полями person1 і person2. Жоден із шести ендпоінтів ніколи їх не приймав: схема весь час вимагала chart1 і chart2, тому кожен приклад із тієї сторінки повертав 400 INVALID_INPUT. Сама схема не змінювалась, змінилась документація.
Що ще було неточним на тій сторінці і тепер відповідає реальності:
/vedic/compatibility/fullповертаєdata.manglik.partner1,partner2іverdict. Ключівperson1.present,cancelledіbhriguтам немає, Bhrigu-Bindu це окремий ендпоінт/bhrigu-match.- Відповідь
/ashtakootмає виглядdata.{method, school, boy, girl, total, max, threshold, points{}, doshas[], recommendation}. Полівpassing_threshold,verdict,breakdown[]іwarnings[]не існує. - Вартість:
mangal-match,bhrigu-matchіfullце 50 кредитів кожен, а не 20, 30 і 100. Ціни не змінювались, у таблиці були неправильні числа.
Приклади на сторінці тепер запущені проти продакшна, а відповідь у JSON це дослівний захват для тієї самої пари, що в запиті вище.
2026-08-22 · Повтор запиту з кешу більше не списує кредити
Ми пишемо на сторінці вартості і в конвенціях, що відповідь із заголовком X-Cache: HIT не коштує кредитів. Насправді вона коштувала повну ціну: списання відбувається до кешу, і нічого його не скасовувало. Перевірено трьома однаковими викликами /v1/acg/by-category: два з них повернули X-Cache: HIT, і баланс щоразу зменшувався на 50.
Тепер повтор ідентичного запиту протягом 5 хвилин коштує 0, а X-Credits-Used і X-Credits-Remaining у відповіді показують це чесно.
Той самий запит іншою мовою міг повернути чужу мову. Ключ кешу враховував ?lang=, бо той їде в query, але не враховував заголовок Accept-Language. Два клієнти на одному тарифі з однаковим тілом і різними мовами ділили один запис. Запит із Accept-Language: de повертав українську відповідь, у якій ще й було написано requestedLanguage: "uk". Мова тепер входить у ключ.
/v1/interpret/* більше не проходить через цей кеш. У нього є власний AI-кеш із годинним TTL і 50% знижкою на повтор, і саме він мав відповідати, але список винятків містив interpret замість interpret/*, тому першим завжди спрацьовував загальний. /v1/horoscope/* навпаки залишається тут: його обробник звертається до моделі на кожен запит, і цей кеш - єдине, що стоїть між повтором і другою генерацією.
Що зробити з вашим кодом. Нічого. Якщо ви обходили кеш, додаючи в тіло щось випадкове, щоб не платити двічі за ту саму відповідь, тепер це не потрібно.