A Lexicon export is the whole dictionary - groups, types with their fields, items, links, alarms, cards, Boards, Study Plans and, optionally, the files that values refer to - as one UTF-8 JSON document. Every client writes and reads the same format:
| Where | Export | Import |
|---|---|---|
| Desktop | File -> Export... |
File -> Import... |
| Web | File -> Export... |
File -> Import... |
| Android | Settings, Export… | Settings, Import… |
| Server machine | LexiconServer export --output FILE [--with-files] |
LexiconServer import --input FILE |
| REST | GET /api/v1/export?blobs=true |
POST /api/v1/import |
The command line works on the database file directly, so it can export a
dictionary while the server runs; SQLite coordinates the two connections.
With --with-files, a missing, unreadable or damaged referenced file makes
the export fail instead of producing an incomplete document. When --output
names an existing file, the command writes and syncs a temporary file in the
same directory before replacing it; a failed write leaves the previous export
in place.
An export is for moving a dictionary, not for backing up a large one.
With files included, every file is read into memory, grows by a third as
base64 and becomes part of one JSON document, which is also built in memory;
the REST import accepts at most --max-blob-bytes (64 MB by default). That
is fine for notes and a few images, and wrong for gigabytes of PDFs or video.
To keep such a dictionary safe, use the server's automatic backups (see
server.md): they copy the database
and keep the files as files, shared between backups.
{
"format": "lexicon-export",
"version": 10,
"exportedAt": "2026-09-22T08:00:00Z",
"groups": [
{ "id": 1, "name": "Default", "description": "...", "position": 0 },
{ "id": 2, "name": "Maths", "description": "", "position": 1 }
],
"types": [
{
"id": 3, "groupId": 2, "groupName": "Maths", "name": "Concept", "description": "",
"fields": [
{ "id": 4, "itemTypeId": 3, "name": "Difficulty",
"description": "How difficult this concept is.", "dataType": "Enum",
"position": 0, "enumOptions": ["easy", "hard"] }
]
}
],
"items": [
{ "id": 7, "groupId": 2, "itemTypeId": 3, "title": "Monoid", "fieldValues": { "4": "hard" }, "...": "..." }
],
"links": [
{ "fromItemId": 7, "toItemId": 8, "linkType": "IsA", "position": 1, "customValue": "" }
],
"alarms": [
{ "id": 4, "title": "Dentist", "description": "Bring the card.", "firesAt": "2026-10-02T08:30:00Z",
"dismissedAt": null, "repeatDays": 7, "itemId": 7, "asap": true, "group": "Health" }
],
"cards": [
{ "id": 12, "itemId": 7, "question": "What is a monoid?", "answer": "A semigroup with a unit.",
"successCount": 4, "failureCount": 2, "lastAttempt": "2026-09-24T14:00:00Z" }
],
"studyPlans": [
{ "id": 1, "item": "Effective Modern C++", "group": "Programming", "type": "Book", "unitType": "Page",
"currentProgress": 50, "startDate": "2026-09-27", "endDate": "2026-11-30",
"note": "Study carefully", "firstUnit": 1, "lastUnit": 334,
"studyDaysMask": 127, "customUnit": "" }
],
"boards": [
{ "id": 1, "name": "Main", "content": "# Team Board\n\n- Ship it", "revision": 5 },
{ "id": 2, "name": "Work", "content": "# Work", "revision": 2 }
],
"blobs": [
{ "hash": "6c7dbba2...99d98ca", "data": "iVBORw0KGgo..." }
]
}-
formatandversioncome first in meaning: an import refuses a document whoseformatis notlexicon-exportor whoseversionit does not know, before anything is written. This document describes version 10. -
Versions. The version goes up whenever a reader of the previous one would import a new document by leaving part of it out - silently losing data - rather than refusing it:
Version Written by Adds 1 Lexicon before cards groups, types, fields, items, links, alarms, files 2 Lexicon with cards cards3 Lexicon with recurring alarms repeatDaysand linkeditemIdon alarms4 Lexicon with the Board singleton Markdown board5 Lexicon with described fields descriptionon item fields6 Lexicon with named Boards all named Markdown documents in boards7 Lexicon with organized alarms asapandgroupon alarms8 Lexicon with foreign-key fields ForeignKeyandtargetItemTypeIdon fields9 Lexicon with Study Plans studyPlanswith progress, calendar dates, unit ranges and weekdays10 Lexicon with Study Plan groups plain text grouponstudyPlansA reader takes every version up to its own and refuses a newer one: an older Lexicon says it does not know version 3 instead of importing it without the recurrence and item link. A version 1 document has no cards; the few that development builds wrote with cards have them read all the same.
-
Groups, types, fields, items and links have the shapes of the REST API, with the same symbolic enum names. Read-only parts such as
groupName,itemTypeNameandrevisionare written but ignored on import. -
The
ids are those of the exporting database. They only connect the records of one document:groupId,itemTypeId, the keys offieldValues,fromItemIdandtoItemIdrefer to them. AForeignKeyfield'stargetItemTypeIdand its stored item IDs are mapped to imported IDs. -
alarmsholds every alarm, withfiresAtanddismissedAtin UTC, so an alarm that was dismissed does not ring again after an import.repeatDaysis 0 for a one-time alarm or 1–365 for a recurring interval. An optionalitemIdlinks to an exported item and is mapped to its imported ID.asapmarks an alarm as needing attention as soon as possible;groupis an optional free-text grouping label. Documents written before alarms existed have noalarms; they import as before. -
cardsholds every card with itsitemId,question,answerand statistics -successCount,failureCountandlastAttempt(UTC, ornullfor a card never answered) - so a quiz history survives the move. The counts are whole numbers and never negative, andlastAttemptis given exactly when one of them is above zero. Documents written before cards existed have nocards; they import as before, with none. -
studyPlansholds each independent plan as plain UTF-8itemand optionalgrouptext, with symbolictypeandunitType.currentProgressis the last completed absolute unit, or 0 when none is complete. Date endpoints are inclusive local calendar dates; the Monday-to-Sunday bit mask defaults to 127. Older exports have no plans or no Group text. Reimporting an unchanged plan does not duplicate it. -
blobsis present when files were included.datais standard base64 with padding;hashis the SHA-256 of the decoded bytes, the valueBlobfields store and the part after the colon of anImagevalue (image/png:<hash>).
Exports contain the current dictionary, not prior item versions or Trash.
A server backup's lexicon.db and Blob files retain that history.
An import merges the document into the dictionary, in one unit of work: if any part fails, nothing is written.
- Groups are matched by name; a missing one is created.
- Types are matched by name, ignoring ASCII case, within their scope - all groups, or the matched group. Fields are matched by name within their type. A field that exists but holds another kind of value keeps its data type, and the values meant for it are left out with a warning. Existing types and fields are never changed, so an import cannot clear values.
- Items already present - the same group, title and disambiguation - are left exactly as they are. Every other item is created with its values, properties, aliases, tags, flags, status, understanding, pinned state and content. A value that does not fit its field here, or whose file is neither in the document nor in this database, is left out with a warning.
- Links are created where at least one end is an item this import created, unless the same link is already there. Links between two items that were already present are theirs to keep as they are.
- Files in
blobsare stored under their SHA-256; one whose bytes do not match its hash is left out. - Alarms are created unless an alarm with the same title already goes off at the same moment.
- Cards are created for the items this import created, attached to them
by their new IDs and with their statistics as exported. An item that was
already present keeps the cards it has and gets none from the file. Cards
are never merged: two cards asking the same question are two cards. A card
of an item missing from the file is left out with a warning; a card with a
blank question or answer, a negative count, a
lastAttemptthat is not a UTC time, or answers without alastAttempt(or the other way round) fails the import. - Boards are matched by name ignoring ASCII case. A missing named Board is
created; content fills an empty Board, while different existing content is
kept and reported as a warning. Import therefore never destroys a Board
already in use. A version 4 or 5 singleton
boardis imported asMain.
Importing the same document twice therefore changes nothing the second time. The import answers with a report:
{ "groupsCreated": 1, "typesCreated": 2, "fieldsCreated": 3, "itemsCreated": 3,
"itemsSkipped": 0, "linksCreated": 2, "blobsImported": 1, "alarmsCreated": 0,
"cardsCreated": 4, "boardsImported": 2, "warnings": [] }An import is a merge, not a restore: it never deletes, renames or edits what is already in the dictionary. To restore a dictionary exactly, import into an empty database - or back up and restore the database file itself (see server.md).