Export
Boards as files you can keep.
Two downloads, both zips. One board, for anybody who can see it; the whole instance, for its administrators. A board's JSON is the same from either, so a script written against one reads the other.
GET /api/data/board-export
One board: its JSON, and the files its cards hold. Needs read access to the board.
Archived areas and cards are included, each with its archivedAt. An export is
what somebody keeps, and a card archived last week is still work that happened.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
boardId | integer | yes | The board to export. |
What comes back
A zip named <id>-<name>-<date>.zip:
12-website-relaunch.json
attachments/
41-Quartalszahlen.xlsx
images/3f9c0b7d4e2a8c1f6b5d9e0a7c3b2f1e.webp
Attachments keep their own names, prefixed with their id so two files called
Screenshot.png stay two files. Pictures pasted into a description or a comment
are in images/, under the name they were stored with.
curl "https://boards.example.com/api/data/board-export?boardId=12" \
-H "X-API-Key: $LOKALBOARDS_KEY" \
-o website-relaunch.zip
GET /api/data/export
Everything on the instance. Needs an administrator: a session of one, or an API
key one of them issued. Anybody else is answered 403.
What comes back
A zip named lokalboards-export-<date>.zip:
boards/
12-website-relaunch.json
13-personal.json
attachments/
12-website-relaunch/
41-Quartalszahlen.xlsx
images/3f9c0b7d4e2a8c1f6b5d9e0a7c3b2f1e.webp
database.sql
boards/ holds every board, archived ones included, each in the
board file format. attachments/ holds each board's files in
a folder named like its JSON, and the paths inside that JSON point there.
database.sql is the whole database: each table's CREATE TABLE, then its rows.
It restores into an empty database the way any dump does:
mysql -u lokalboards -p lokalboards < database.sql
The rows of session and verification are left out — the tables are created,
empty — so everybody signs in again after a restore rather than finding logins
brought back that had ended. Everything else is in it, password hashes
included: keep the zip the way you keep the database.
The response begins straight away and the zip is written while it is sent, so a large instance takes longer to finish, not longer to start. Give the request a generous timeout rather than a short one.
# Nightly, from cron: one dated zip per day.
curl "https://boards.example.com/api/data/export" \
-H "X-API-Key: $LOKALBOARDS_ADMIN_KEY" \
--fail -o "lokalboards-$(date +%F).zip"
The board file
One JSON object per board.
| Field | Type | Description |
|---|---|---|
format | string | Always lokalboards.board. |
formatVersion | integer | 1. Raised if a field changes what it means; new fields can appear without it. |
lokalboardsVersion | string | The version that wrote the file. |
exportedAt | string | When it was written. |
board | object | id, name, style, status, color, image, owner and archivedAt. |
members | array | Everyone invited to the board, each a person with a permission of read or edit. The owner is board.owner. |
labels | array | The words in use on the board: id and name. |
areas | array | In the board's order: id, name, sort, archivedAt, and its cards. |
images | object | Every uploaded picture named in a description, a comment or the board's cover, from its address to its path in the zip. |
Each card:
| Field | Type | Description |
|---|---|---|
id, name | As on the board. | |
content | string | The description as it is stored, in HTML. Pictures keep their addresses; look them up in images. |
done | boolean | Whether it is ticked off. |
sort | integer | Its position in its area. |
dueDate | string | null | When it is due. |
assignee | person | null | Who it is assigned to. |
labels | array | The names of the labels it wears. |
archivedAt | string | null | When it was archived, if it was. |
reminders | array | Minutes before the due date that a reminder goes out. |
attachments | array | id, filename, filetype, filesize, createdAt, and file: its path in the zip, or null when the file was missing on the server. A linked rather than uploaded attachment has a url instead. |
comments | array | id, author (a person), content in HTML, createdAt. |
activity | array | The card's history: type, actor (a person), data with the detail of that kind of event, createdAt. |
A person is { "id": "…", "name": "…" } — no email addresses. Dates are ISO
8601 in UTC, and anything unset is null.
{
"format": "lokalboards.board",
"formatVersion": 1,
"lokalboardsVersion": "0.38.0",
"exportedAt": "2026-09-13T08:00:00.000Z",
"board": {
"id": 12,
"name": "Website Relaunch",
"style": "kanban",
"status": "private",
"color": null,
"image": "/images/board_placeholder_03.webp",
"owner": { "id": "8b1c…", "name": "Florian" },
"archivedAt": null
},
"members": [{ "id": "2f0a…", "name": "Anna", "permission": "edit" }],
"labels": [{ "id": 7, "name": "Webdesign" }],
"areas": [
{
"id": 31,
"name": "Todo",
"sort": 0,
"archivedAt": null,
"cards": [
{
"id": 501,
"name": "Contact form",
"content": "<p>Spam filter first.</p><img src=\"/api/uploads/3f9c…e1.webp\">",
"done": false,
"sort": 0,
"dueDate": "2026-09-20T15:00:00.000Z",
"assignee": { "id": "2f0a…", "name": "Anna" },
"labels": ["Webdesign"],
"archivedAt": null,
"reminders": [60],
"attachments": [
{
"id": 41,
"filename": "Quartalszahlen.xlsx",
"filetype": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"filesize": 18233,
"createdAt": "2026-09-01T09:12:00.000Z",
"file": "attachments/41-Quartalszahlen.xlsx"
}
],
"comments": [
{
"id": 90,
"author": { "id": "8b1c…", "name": "Florian" },
"content": "<p>Looks good.</p>",
"createdAt": "2026-09-02T10:00:00.000Z"
}
],
"activity": [
{
"type": "assigned",
"actor": { "id": "8b1c…", "name": "Florian" },
"data": { "assigneeName": "Anna" },
"createdAt": "2026-09-01T09:00:00.000Z"
}
]
}
]
}
],
"images": {
"/api/uploads/3f9c…e1.webp": "attachments/images/3f9c…e1.webp"
}
}