Документація API VibeCode

REST API для публікації двомовних постів, завантаження медіа, коментарів, пошуку та аналітики.

Зміст


Автентифікація

API підтримує два способи автентифікації:

  1. API ключдля програмного доступу до v1-ендпоінтів. Передавайте у заголовку:
    Authorization: Bearer vc_your_api_key_here

    Створити ключ можна в адмін-панелі. Ключ показується лише раз при створенні. Префікс — vc_, далі 64 hex-символи.

  2. Сесія через cookieдля дій від імені залогіненого користувача (коментарі, адмін-аналітика). Видається після входу через /api/auth/sign-in.

Конвенції постів

  • Сайт двомовний: локалі uk та en. Один пост може мати обидві мовні версії, об'єднані спільним translationId.
  • slug спільний для обох мов. URL формуються як /uk/blog/{slug} та /en/blog/{slug}.
  • content — Markdown (не HTML).
  • При створенні поста має бути задана щонайменше одна локаль (uk або en); другу можна додати пізніше через адмінку.
  • Поле tags приймає рядки (slug існуючого тега) або об'єкти {slug, nameUk?, nameEn?} — відсутні теги створяться автоматично.

Пости

GET /api/v1/posts

Список опублікованих постів з пагінацією. Авторизація не потрібна.

Параметри запиту

ПараметрТипЗа замовч.Опис
localeuk | en"uk"Мова постів
pagenumber1Номер сторінки (≥ 1)
limitnumber101–50

Приклад

curl https://www.vaibecod.com/api/v1/posts?locale=uk&page=1&limit=5

Відповідь (200)

{
  "posts": [
    {
      "id": "abc123",
      "slug": "my-post",
      "locale": "uk",
      "title": "Мій пост",
      "excerpt": "Короткий опис…",
      "coverImage": "/api/media/9f3…",
      "published": true,
      "publishedAt": "2026-04-15T10:00:00.000Z",
      "viewCount": 42,
      "authorName": "Admin",
      "createdAt": "2026-04-15T10:00:00.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 5, "total": 12, "totalPages": 3 }
}

POST /api/v1/posts

Створити пост. Можна одразу обома мовами. Потребує API ключ.

Тіло запиту (JSON)

ПолеТипОбов'язковеОпис
slugstringтакURL-slug, 1–200 символів. Спільний для обох мов.
uk{ title, excerpt?, content }одна з uk/enУкраїнська версія (Markdown у content).
en{ title, excerpt?, content }одна з uk/enАнглійська версія (Markdown у content).
coverImagestringніURL обкладинки (зазвичай /api/media/{id}).
publishedbooleanніЗа замовч. false (чернетка).
publishedAtstring (ISO 8601)ніДата публікації, можна заднім числом.
tags(string | { slug, nameUk?, nameEn? })[]ніSlug існуючого тега або об'єкт для створення нового.

Приклад: пост одразу двома мовами

curl -X POST https://www.vaibecod.com/api/v1/posts \
  -H "Authorization: Bearer vc_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "getting-started-ai-coding",
    "uk": {
      "title": "Початок роботи з AI-кодингом",
      "excerpt": "Гайд по AI-розробці",
      "content": "## Вступ\n\nAI змінює спосіб писати код…"
    },
    "en": {
      "title": "Getting Started with AI Coding",
      "excerpt": "A guide to AI-assisted development",
      "content": "## Introduction\n\nAI is changing how we code…"
    },
    "coverImage": "/api/media/9f3a…",
    "published": true,
    "publishedAt": "2026-04-15T12:00:00Z",
    "tags": ["ai", { "slug": "next-js", "nameUk": "Next.js", "nameEn": "Next.js" }]
  }'

Відповідь (201)

{
  "translationId": "tr_8b2…",
  "slug": "getting-started-ai-coding",
  "published": true,
  "posts": [
    { "id": "abc-uk", "slug": "getting-started-ai-coding", "locale": "uk", "url": "/uk/blog/getting-started-ai-coding" },
    { "id": "abc-en", "slug": "getting-started-ai-coding", "locale": "en", "url": "/en/blog/getting-started-ai-coding" }
  ]
}

Після створення інвалідуються кеші: /uk, /en, /uk/blog, /en/blog, /sitemap.xml.


Теги

GET /api/v1/tags

Усі теги. Без авторизації.

curl https://www.vaibecod.com/api/v1/tags
{
  "tags": [
    { "id": 1, "slug": "react",  "nameUk": "React", "nameEn": "React" },
    { "id": 2, "slug": "ai",     "nameUk": "AI",    "nameEn": "AI" }
  ]
}

POST /api/v1/posts/tags

Додати теги до існуючого поста за slug (одразу до всіх його мовних версій). Потребує API ключ.

curl -X POST https://www.vaibecod.com/api/v1/posts/tags \
  -H "Authorization: Bearer vc_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "getting-started-ai-coding",
    "tags": [
      { "slug": "tutorial", "nameUk": "Туторіал", "nameEn": "Tutorial" },
      { "slug": "ai" }
    ]
  }'
{ "success": true, "added": 2, "posts": 2 }

Медіа

Файли зберігаються як BLOB у БД. Завантажений файл доступний за URL /api/media/{id} і дедуплікується за розміром та MIME-типом.

POST /api/upload (публічний)

Публічне завантаження зображень з фронтенду. Без авторизації.

ПараметрЗначення
Форматmultipart/form-data, поле file
Дозволені типиimage/jpeg, image/png, image/webp, image/gif, image/avif
Макс. розмір10 MB
curl -X POST https://www.vaibecod.com/api/upload -F "file=@photo.jpg"
{ "url": "/api/media/9f3a…" }

POST /api/v1/upload (з API ключем)

Завантаження зображень та відео для програмного використання. Потребує API ключ.

ТипФорматиМакс. розмір
ЗображенняJPEG, PNG, WebP, GIF, AVIF50 MB
ВідеоMP4, WebM, OGG50 MB
curl -X POST https://www.vaibecod.com/api/v1/upload \
  -H "Authorization: Bearer vc_your_key" \
  -F "file=@video.mp4"
{
  "url": "/api/media/4d8c…",
  "type": "video",
  "size": 8421376,
  "deduplicated": false
}

Якщо такий файл (за розміром і MIME) уже є — повертається статус 200 з тим самим URL і deduplicated: true.

GET /api/media/{id}

Віддає завантажений файл з заголовком Cache-Control: public, max-age=31536000, immutable.

GET /api/media/{id}/thumbnail

Для відео повертає SVG-плейсхолдер 640×360 з кнопкою play. Реальний кадр не витягується (немає ffmpeg).


Коментарі

GET /api/comments?postId={id}

Видимі коментарі до поста (без прихованих модератором), у порядку від нових до старих.

curl "https://www.vaibecod.com/api/comments?postId=abc123"
[
  {
    "id": "c_1",
    "content": "Дякую за статтю!",
    "parentId": null,
    "createdAt": "2026-04-20T08:30:00.000Z",
    "hidden": false,
    "authorId": "u_1",
    "authorName": "Ivan",
    "authorImage": "/api/media/…"
  }
]

POST /api/comments

Створити коментар. Потрібна сесія (cookie після входу).

ПолеТипОбов'язковеОпис
postIdstringтакID поста
contentstringтак1–5000 символів
parentIdstringніID батьківського коментаря (для відповідей)
{ "id": "c_42", "success": true }

PATCH /api/comments

Редагувати власний коментар (поле content) або приховати/показати (admin, поле hidden). Поля взаємовиключні.

{ "id": "c_42", "content": "оновлений текст" }

DELETE /api/comments

Видалити коментар. Дозволено власнику або адміну.

{ "id": "c_42" }

GET /api/search

Повнотекстовий пошук по постах. Без авторизації.

ПараметрТипОбов'язковеОпис
qstringтакЗапит, 2–100 символів
localeuk | enніЗа замовч. uk
curl "https://www.vaibecod.com/api/search?q=next.js&locale=uk"

Аналітика

POST /api/track

Клієнтський трекінг подій: перегляди сторінок, кліки, тривалість сесії. Без авторизації, з автодетектом ботів за User-Agent.

Тіло запиту залежить від поля type:

// pageview
{ "type": "pageview", "path": "/uk/blog/x", "sessionId": "uuid", "referrer": "https://google.com/", "utmSource": "google" }

// click
{ "type": "click", "sessionId": "uuid", "path": "/uk/blog/x", "element": "a.cta", "href": "https://...", "x": 100, "y": 200 }

// duration (мс)
{ "type": "duration", "sessionId": "uuid", "path": "/uk/blog/x", "duration": 45000 }

Усі типи повертають { ok: true } (200).


SEO та фіди

ШляхОписКеш
/sitemap.xmlПовний sitemap з hreflang-альтернативами uk/en1h
/news-sitemap.xmlGoogle News sitemap (останні 48 постів)10m
/robots.txtДирективи для пошукових і AI-ботів
/llms.txtСписок постів у форматі для LLM (markdown зі ссилками)24h
/llms-full.txtОстанні 50 постів повним текстом (markdown)24h

Коди помилок

КодОпис
400Невалідні дані запиту (часто з полем details від Zod)
401Немає або невалідний API ключ / сесія
403Недостатньо прав (наприклад, не admin)
404Ресурс не знайдено
500Внутрішня помилка сервера

Формат помилки:

{ "error": "Validation failed", "details": [ /* Zod issues */ ] }

Кешування та ліміти

  • Жорсткого rate-limit на API наразі немає. Просимо не перевищувати 60 запитів на хвилину.
  • /api/media/{id} і /api/media/{id}/thumbnail — immutable, кеш 1 рік.
  • Створення/оновлення поста інвалідує кеші /uk, /en, /uk/blog, /en/blog, /sitemap.xml.