VibeCode API Documentation
REST API for publishing bilingual posts, uploading media, comments, search, and analytics.
Contents
- Authentication
- Post conventions
- Posts
- Tags
- Media
- Comments
- Search
- Analytics tracking
- SEO and feeds
- Error codes
- Caching and rate limits
Authentication
The API supports two authentication methods:
- API key — for programmatic access to v1 endpoints. Pass it in the header:
Authorization: Bearer vc_your_api_key_hereCreate a key in the admin panel. The key is shown only once on creation. Format: vc_ prefix followed by 64 hex chars.
- Session cookie — for 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
| Parameter | Type | Default | Description |
|---|---|---|---|
locale | uk | en | "uk" | Post language |
page | number | 1 | Page number (≥ 1) |
limit | number | 10 | 1–50 |
Example
curl https://www.vaibecod.com/api/v1/posts?locale=uk&page=1&limit=5Response (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)
| Field | Type | Required | Description |
|---|---|---|---|
slug | string | yes | URL slug, 1–200 chars. Shared across locales. |
uk | { title, excerpt?, content } | one of uk/en | Ukrainian version (Markdown in content). |
en | { title, excerpt?, content } | one of uk/en | English version (Markdown in content). |
coverImage | string | no | Cover image URL (typically /api/media/{id}). |
published | boolean | no | Default false (draft). |
publishedAt | string (ISO 8601) | no | Publication date, can be backdated. |
tags | (string | { slug, nameUk?, nameEn? })[] | no | Slug 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.
| Parameter | Value |
|---|---|
| Format | multipart/form-data, field file |
| Allowed types | image/jpeg, image/png, image/webp, image/gif, image/avif |
| Max size | 10 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.
| Type | Formats | Max size |
|---|---|---|
| Images | JPEG, PNG, WebP, GIF, AVIF | 50 MB |
| Video | MP4, WebM, OGG | 50 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.
| Field | Type | Required | Description |
|---|---|---|---|
postId | string | yes | Post ID |
content | string | yes | 1–5000 chars |
parentId | string | no | Parent 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" }Search
GET /api/search
Full-text search across posts. No authentication.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | yes | Query, 2–100 chars |
locale | uk | en | no | Default 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
| Path | Description | Cache |
|---|---|---|
| /sitemap.xml | Full sitemap with uk/en hreflang alternates | 1h |
| /news-sitemap.xml | Google News sitemap (last 48 posts) | 10m |
| /robots.txt | Directives for search and AI bots | — |
| /llms.txt | Post index for LLMs (markdown with links) | 24h |
| /llms-full.txt | Last 50 posts in full markdown | 24h |
Error codes
| Code | Description |
|---|---|
| 400 | Invalid request data (often with a details field from Zod) |
| 401 | Missing or invalid API key / session |
| 403 | Insufficient permissions (e.g. not admin) |
| 404 | Resource not found |
| 500 | Internal 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.