Lingoborn API v1

A general REST API for your vocabulary, categories, stories and progress — the same data the app uses.

Overview

All requests are HTTPS and return JSON. Send and receive UTF-8. The base URL is:

# Base URL
https://api.lingoborn.com

Data endpoints live under /v1. Auth and profile endpoints are unversioned (/auth/*, /me). Every response is a JSON object; errors always carry an error string.

Authentication

The API uses Bearer token (JWT) authentication. Obtain a token from POST /auth/login (or /auth/register); it is valid for 7 days. Send it on every protected request:

Authorization: Bearer <your-token>

A caller only ever sees its own account's data. Requests without a valid token get 401.

In Postman: run POST /auth/login, copy token from the response, then set the collection Authorization → Bearer Token to that value and have each request inherit auth from parent. Or auto-capture it with a Login Test script: pm.collectionVariables.set("token", pm.response.json().token).

Conventions

TopicDetail
Content typeRequest bodies are application/json. Set Content-Type: application/json on POST/PUT.
PaginationList endpoints are cursor-based: pass limit (≤ 200) and the nextCursor from the previous response as cursor. nextCursor: null means the last page.
Projectionfields=compact (default) returns a light record; fields=full returns the whole card.
DeletesDeleted rows are hidden everywhere (soft-deleted); reads mirror exactly what the app shows.
TimestampsISO-8601 UTC (e.g. 2026-09-11T09:13:00.000Z).

Errors

Standard HTTP status codes; the body is always { "error": "…" }.

StatusMeaning
400Invalid input or a non-numeric :id.
401Missing, invalid, or expired bearer token — log in again.
404Resource not found (or not yours).
409Conflict (e.g. registering an email that already exists).
500Server error.

Auth

POST/auth/registerNo auth

Create an account and receive a token.

Body
FieldType
emailstringrequired
passwordstring (min 6)required
namestringrequired
country, gender, learningGoal, levelstringoptional
knownLanguages, interestsstring[]optional
dailyGoalintegeroptional
remindersbooleanoptional
Request
curl -X POST https://api.lingoborn.com/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"secret6","name":"Vlad"}'
Response 201
{ "token": "eyJhbGciOi…", "user": { "id": 1, "email": "you@example.com", "name": "Vlad" } }
POST/auth/loginNo auth

Verify credentials and receive a 7-day token. This is where you get the token for every other call.

Body
FieldType
emailstringrequired
passwordstringrequired
Request
curl -X POST https://api.lingoborn.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"secret6"}'
Response 200
{ "token": "eyJhbGciOi…", "user": { "id": 1, "email": "you@example.com", "name": "Vlad" } }
Invalid credentials return 401 { "error": "Invalid email or password." }.

Profile

GET/meBearer

The current user's profile.

Request
curl https://api.lingoborn.com/me -H "Authorization: Bearer $TOKEN"
Response 200
{ "user": { "id": 1, "email": "you@example.com", "name": "Vlad",
  "country": "", "learningGoal": "", "level": "", "dailyGoal": 0,
  "interests": [], "knownLanguages": [] } }
PUT/meBearer

Update editable profile fields. Send only what you want to change. email and password cannot be changed here.

Body (all optional)
FieldType
name, country, gender, learningGoal, levelstring
knownLanguages, interestsstring[]
dailyGoalinteger
remindersboolean
Request
curl -X PUT https://api.lingoborn.com/me \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"level":"B1","dailyGoal":10}'
Response 200
{ "user": { "id": 1, "level": "B1", "dailyGoal": 10, … } }
DELETE/meBearer

Permanently delete the account and all its data (words, stories, groupings, stats cascade).

Response 200
{ "ok": true }

Words

GET/v1/wordsBearer

List words with filtering, sorting and cursor pagination.

Query parameters (all optional)
ParamValuesDefault
fieldscompact · fullcompact
limit1–20050
cursorthe nextCursor from a prior page—
sortcreated · alpha · updatedcreated
categoryexact group name, e.g. Food—
stateto_learn · practiced · learned—
qsearch in english/russian (case-insensitive)—
partOfSpeechexact, e.g. verb (empty until enriched)—
createdAfter / createdBeforedate, e.g. 2026-01-01—
Request
curl "https://api.lingoborn.com/v1/words?fields=compact&limit=5&sort=alpha&state=to_learn" \
  -H "Authorization: Bearer $TOKEN"
Response 200 — compact items
{
  "items": [
    { "id": 12, "uuid": "…", "english": "apple", "russian": "яблоко",
      "category": "Food", "state": "to_learn", "partOfSpeech": "",
      "type": "word", "createdAt": "2026-09-01T…", "updatedAt": "2026-09-01T…" }
  ],
  "nextCursor": "12"
}
fields=full adds: pronunciation, example, exampleRu, imageEmoji, explanationEn, explanationRu, synonyms[], architecture{}, examples[], audioUrl, lastPracticedAt, practiceCount, correctCount.
GET/v1/words/summaryBearer

Aggregate counts only — answers "how many words / per stage / per category / per part of speech" without returning any rows.

Request
curl https://api.lingoborn.com/v1/words/summary -H "Authorization: Bearer $TOKEN"
Response 200
{
  "total": 42,
  "byState": { "to_learn": 20, "practiced": 15, "learned": 7 },
  "byCategory": [ { "category": "Food", "count": 12 }, { "category": "Travel", "count": 8 } ],
  "byPartOfSpeech": { "unknown": 42 }
}
GET/v1/words/:idBearer

One word by numeric id — always the full card. 404 if not found.

Request
curl https://api.lingoborn.com/v1/words/12 -H "Authorization: Bearer $TOKEN"
Response 200 (abridged)
{ "id": 12, "english": "apple", "russian": "яблоко", "category": "Food",
  "state": "to_learn", "type": "word", "pronunciation": "/ˈæp.əl/",
  "synonyms": [], "architecture": { "type": "word", … }, "examples": [ … ] }

Categories

GET/v1/categoriesBearer

All categories (derived from your words' groups), each with a word count and whether it has a synonym/antonym grouping and stories.

Request
curl https://api.lingoborn.com/v1/categories -H "Authorization: Bearer $TOKEN"
Response 200
{ "categories": [
  { "category": "Food", "wordCount": 12, "hasGrouping": true, "storyCount": 2 }
] }
GET/v1/categories/:name/groupingBearer

The synonym/antonym clusters for a category. URL-encode names with spaces (Phrasal%20Verbs). 404 if the category has no grouping.

Request
curl "https://api.lingoborn.com/v1/categories/Food/grouping" -H "Authorization: Bearer $TOKEN"
Response 200
{ "category": "Food", "clusters": [ … ], "updatedAt": "2026-09-…" }

Stories

GET/v1/storiesBearer

List stories, newest first. Optional ?category= filter.

Request
curl "https://api.lingoborn.com/v1/stories?category=Food" -H "Authorization: Bearer $TOKEN"
Response 200
{ "items": [
  { "id": 3, "uuid": "…", "category": "Food", "title": "Breakfast",
    "story": "…", "wordsUsed": ["apple","bread"], "createdAt": "…", "updatedAt": "…" }
] }
GET/v1/stories/:idBearer

One story by numeric id. 404 if not found.

Request
curl https://api.lingoborn.com/v1/stories/3 -H "Authorization: Bearer $TOKEN"
Response 200
{ "id": 3, "category": "Food", "title": "Breakfast", "story": "…", "wordsUsed": [ … ] }

Stats

GET/v1/statsBearer

Daily learning stats plus totals over the range.

Query parameters (optional)
ParamFormat
fromYYYY-MM-DD
toYYYY-MM-DD
Request
curl "https://api.lingoborn.com/v1/stats?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "days": [ { "date": "2026-09-10", "added": 3, "checked": 5,
            "toLearn": 1, "practiced": 2, "learned": 2 } ],
  "totals": { "added": 3, "checked": 5, "toLearn": 1, "practiced": 2, "learned": 2 }
}

Roadmap planned

Write endpoints are coming — the read API above is live today. Planned:

EndpointPurpose
POST /v1/wordsAdd a word (auto-enrich + dedupe, or explicit fields).
PATCH /v1/words/:idEdit a word (category, state, …).
DELETE /v1/words/:idRemove a word (soft delete).
POST /v1/words/bulkBulk create / reassign (e.g. recategorize many at once).
PATCH /v1/categories/:nameRename a category.
POST /v1/categories/:name/groupingGenerate synonym/antonym clusters.
POST /v1/storiesGenerate a story for a category.
POST /v1/practiceRecord a practice result and advance a word's stage.