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.
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
| Topic | Detail |
|---|---|
| Content type | Request bodies are application/json. Set Content-Type: application/json on POST/PUT. |
| Pagination | List endpoints are cursor-based: pass limit (≤ 200) and the nextCursor from the previous response as cursor. nextCursor: null means the last page. |
| Projection | fields=compact (default) returns a light record; fields=full returns the whole card. |
| Deletes | Deleted rows are hidden everywhere (soft-deleted); reads mirror exactly what the app shows. |
| Timestamps | ISO-8601 UTC (e.g. 2026-09-11T09:13:00.000Z). |
Errors
Standard HTTP status codes; the body is always { "error": "…" }.
| Status | Meaning |
|---|---|
400 | Invalid input or a non-numeric :id. |
401 | Missing, invalid, or expired bearer token — log in again. |
404 | Resource not found (or not yours). |
409 | Conflict (e.g. registering an email that already exists). |
500 | Server error. |
Auth
Create an account and receive a token.
| Field | Type | |
|---|---|---|
email | string | required |
password | string (min 6) | required |
name | string | required |
country, gender, learningGoal, level | string | optional |
knownLanguages, interests | string[] | optional |
dailyGoal | integer | optional |
reminders | boolean | optional |
curl -X POST https://api.lingoborn.com/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"secret6","name":"Vlad"}'
{ "token": "eyJhbGciOi…", "user": { "id": 1, "email": "you@example.com", "name": "Vlad" } }
Verify credentials and receive a 7-day token. This is where you get the token for every other call.
| Field | Type | |
|---|---|---|
email | string | required |
password | string | required |
curl -X POST https://api.lingoborn.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"secret6"}'
{ "token": "eyJhbGciOi…", "user": { "id": 1, "email": "you@example.com", "name": "Vlad" } }
401 { "error": "Invalid email or password." }.Profile
The current user's profile.
curl https://api.lingoborn.com/me -H "Authorization: Bearer $TOKEN"
{ "user": { "id": 1, "email": "you@example.com", "name": "Vlad",
"country": "", "learningGoal": "", "level": "", "dailyGoal": 0,
"interests": [], "knownLanguages": [] } }
Update editable profile fields. Send only what you want to change. email and password cannot be changed here.
| Field | Type |
|---|---|
name, country, gender, learningGoal, level | string |
knownLanguages, interests | string[] |
dailyGoal | integer |
reminders | boolean |
curl -X PUT https://api.lingoborn.com/me \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"level":"B1","dailyGoal":10}'
{ "user": { "id": 1, "level": "B1", "dailyGoal": 10, … } }
Permanently delete the account and all its data (words, stories, groupings, stats cascade).
{ "ok": true }
Words
List words with filtering, sorting and cursor pagination.
| Param | Values | Default |
|---|---|---|
fields | compact · full | compact |
limit | 1–200 | 50 |
cursor | the nextCursor from a prior page | — |
sort | created · alpha · updated | created |
category | exact group name, e.g. Food | — |
state | to_learn · practiced · learned | — |
q | search in english/russian (case-insensitive) | — |
partOfSpeech | exact, e.g. verb (empty until enriched) | — |
createdAfter / createdBefore | date, e.g. 2026-01-01 | — |
curl "https://api.lingoborn.com/v1/words?fields=compact&limit=5&sort=alpha&state=to_learn" \ -H "Authorization: Bearer $TOKEN"
{
"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.Aggregate counts only — answers "how many words / per stage / per category / per part of speech" without returning any rows.
curl https://api.lingoborn.com/v1/words/summary -H "Authorization: Bearer $TOKEN"
{
"total": 42,
"byState": { "to_learn": 20, "practiced": 15, "learned": 7 },
"byCategory": [ { "category": "Food", "count": 12 }, { "category": "Travel", "count": 8 } ],
"byPartOfSpeech": { "unknown": 42 }
}
One word by numeric id — always the full card. 404 if not found.
curl https://api.lingoborn.com/v1/words/12 -H "Authorization: Bearer $TOKEN"
{ "id": 12, "english": "apple", "russian": "яблоко", "category": "Food",
"state": "to_learn", "type": "word", "pronunciation": "/ˈæp.əl/",
"synonyms": [], "architecture": { "type": "word", … }, "examples": [ … ] }
Categories
All categories (derived from your words' groups), each with a word count and whether it has a synonym/antonym grouping and stories.
curl https://api.lingoborn.com/v1/categories -H "Authorization: Bearer $TOKEN"
{ "categories": [
{ "category": "Food", "wordCount": 12, "hasGrouping": true, "storyCount": 2 }
] }
The synonym/antonym clusters for a category. URL-encode names with spaces (Phrasal%20Verbs). 404 if the category has no grouping.
curl "https://api.lingoborn.com/v1/categories/Food/grouping" -H "Authorization: Bearer $TOKEN"
{ "category": "Food", "clusters": [ … ], "updatedAt": "2026-09-…" }
Stories
List stories, newest first. Optional ?category= filter.
curl "https://api.lingoborn.com/v1/stories?category=Food" -H "Authorization: Bearer $TOKEN"
{ "items": [
{ "id": 3, "uuid": "…", "category": "Food", "title": "Breakfast",
"story": "…", "wordsUsed": ["apple","bread"], "createdAt": "…", "updatedAt": "…" }
] }
One story by numeric id. 404 if not found.
curl https://api.lingoborn.com/v1/stories/3 -H "Authorization: Bearer $TOKEN"
{ "id": 3, "category": "Food", "title": "Breakfast", "story": "…", "wordsUsed": [ … ] }
Stats
Daily learning stats plus totals over the range.
| Param | Format |
|---|---|
from | YYYY-MM-DD |
to | YYYY-MM-DD |
curl "https://api.lingoborn.com/v1/stats?from=2026-09-01&to=2026-09-30" \ -H "Authorization: Bearer $TOKEN"
{
"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:
| Endpoint | Purpose |
|---|---|
POST /v1/words | Add a word (auto-enrich + dedupe, or explicit fields). |
PATCH /v1/words/:id | Edit a word (category, state, …). |
DELETE /v1/words/:id | Remove a word (soft delete). |
POST /v1/words/bulk | Bulk create / reassign (e.g. recategorize many at once). |
PATCH /v1/categories/:name | Rename a category. |
POST /v1/categories/:name/grouping | Generate synonym/antonym clusters. |
POST /v1/stories | Generate a story for a category. |
POST /v1/practice | Record a practice result and advance a word's stage. |