VibeCode API Documentation

REST API for publishing bilingual posts, uploading media, comments, search, and analytics.

Contents


Authentication

The API supports two authentication methods:

  1. API keyfor programmatic access to v1 endpoints. Pass it in the header:
    Authorization: Bearer vc_your_api_key_here

    Create a key in the admin panel. The key is shown only once on creation. Format: vc_ prefix followed by 64 hex chars.

  2. Session cookiefor actions on behalf of a logged-in user (comments, admin analytics). Issued after login via /api/auth/sign-in.

Post conventions

  • The site is bilingual: locales uk and en. One post can have both language versions linked by a shared translationId.
  • The slug is shared between both languages. URLs are /uk/blog/{slug} and /en/blog/{slug}.
  • content — Markdown (not HTML).
  • When creating a post at least one locale (uk or en) must be provided; the other can be added later via admin UI.
  • The tags field accepts strings (slug of an existing tag) or objects {slug, nameUk?, nameEn?} — missing tags are auto-created.

Posts

GET /api/v1/posts

Published posts list with pagination. No authentication required.

Query parameters

ParameterTypeDefaultDescription
localeuk | en"uk"Post language
pagenumber1Page number (≥ 1)
limitnumber101–50

Example

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

Response (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

Create a post. Both languages can be supplied at once. Requires an API key.

Request body (JSON)

FieldTypeRequiredDescription
slugstringyesURL slug, 1–200 chars. Shared across locales.
uk{ title, excerpt?, content }one of uk/enUkrainian version (Markdown in content).
en{ title, excerpt?, content }one of uk/enEnglish version (Markdown in content).
coverImagestringnoCover image URL (typically /api/media/{id}).
publishedbooleannoDefault false (draft).
publishedAtstring (ISO 8601)noPublication date, can be backdated.
tags(string | { slug, nameUk?, nameEn? })[]noSlug of an existing tag, or object to create a new one.

Example: post in both languages at once

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" }]
  }'

Response (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" }
  ]
}

On creation the following caches are revalidated: /uk, /en, /uk/blog, /en/blog, /sitemap.xml.


Tags

GET /api/v1/tags

All tags. No authentication.

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

Attach tags to an existing post by slug (applied to all its locale versions). Requires an API key.

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 }

Media

Files are stored as BLOBs in the DB. Uploaded files are served at /api/media/{id} and deduplicated by size and MIME type.

POST /api/upload (public)

Public image upload from the frontend. No authentication.

ParameterValue
Formatmultipart/form-data, field file
Allowed typesimage/jpeg, image/png, image/webp, image/gif, image/avif
Max size10 MB
curl -X POST https://www.vaibecod.com/api/upload -F "file=@photo.jpg"
{ "url": "/api/media/9f3a…" }

POST /api/v1/upload (with API key)

Upload images and videos for programmatic use. Requires an API key.

TypeFormatsMax size
ImagesJPEG, PNG, WebP, GIF, AVIF50 MB
VideoMP4, 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
}

If a file with the same size and MIME already exists — status 200 is returned with the same URL and deduplicated: true.

GET /api/media/{id}

Serves the uploaded file with Cache-Control: public, max-age=31536000, immutable.

GET /api/media/{id}/thumbnail

For videos returns a 640×360 SVG placeholder with a play button. No real frame extraction (ffmpeg unavailable).


Comments

GET /api/comments?postId={id}

Visible comments for a post (excludes moderator-hidden), newest first.

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

Create a comment. Requires an active session cookie.

FieldTypeRequiredDescription
postIdstringyesPost ID
contentstringyes1–5000 chars
parentIdstringnoParent comment ID (for replies)
{ "id": "c_42", "success": true }

PATCH /api/comments

Edit own comment (content) or hide/unhide (admin, hidden). Fields are mutually exclusive.

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

DELETE /api/comments

Delete a comment. Allowed for the owner or an admin.

{ "id": "c_42" }

GET /api/search

Full-text search across posts. No authentication.

ParameterTypeRequiredDescription
qstringyesQuery, 2–100 chars
localeuk | ennoDefault uk
curl "https://www.vaibecod.com/api/search?q=next.js&locale=uk"

Analytics tracking

POST /api/track

Client-side event tracking: pageviews, clicks, session duration. No authentication; bots are auto-detected via User-Agent.

The request body depends on the type field:

// 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 }

All types return { ok: true } (200).


SEO and feeds

PathDescriptionCache
/sitemap.xmlFull sitemap with uk/en hreflang alternates1h
/news-sitemap.xmlGoogle News sitemap (last 48 posts)10m
/robots.txtDirectives for search and AI bots
/llms.txtPost index for LLMs (markdown with links)24h
/llms-full.txtLast 50 posts in full markdown24h

Error codes

CodeDescription
400Invalid request data (often with a details field from Zod)
401Missing or invalid API key / session
403Insufficient permissions (e.g. not admin)
404Resource not found
500Internal server error

Error format:

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

Caching and rate limits

  • No hard rate limit is enforced. Please keep requests under 60 per minute.
  • /api/media/{id} and /api/media/{id}/thumbnail — immutable, cached 1 year.
  • Creating/updating a post revalidates /uk, /en, /uk/blog, /en/blog, /sitemap.xml.