API
Memo answers to programs as well as to browsers. This page has every address the server serves, what opens each one, and what comes back.
Whatever the board list and the admin page do over HTTP, a program can do too.
It all hangs off /api/v1, and GET /api/v1 lists its own routes without
asking for anything.
A call carries who it is in an Authorization: Bearer header, never a cookie.
That is either a key from My account, or a token bought with a password.
Because no cookie is involved, none of it needs a CSRF token.
API keys
Another program can make boards for you, with no browser and without your password: a wiki extension that opens a board whenever somebody writes minutes, say. My account has an API keys section for that: say what the key is for, and it is shown to you once, the only time it can be read. Memo keeps a hash of it, the way it keeps passwords.
A key opens everything under /api/v1, described below, as the account that
made it:
curl -X POST https://memo.example/api/v1/boards \
-H 'Authorization: Bearer THE-KEY' \
-H 'Content-Type: application/json' \
-d '{"title": "Minutes of the 12th"}'
{
"name": "minutes-of-the-12th",
"room": "/minutes-of-the-12th",
"title": "Minutes of the 12th",
"kind": "board",
"notes": 0,
"visibility": "open",
"url": "https://memo.example/minutes-of-the-12th",
"relation": "owner"
}
The board belongs to the key's account: it shows up in that list, and that account can close it to others or delete it. A key made by an admin carries the admin role with it, so treat that one the way you treat the password.
The list says when each key last worked, and Take it back ends it there and then. A key is for one program: give one key per program, so that you can take back that one alone.
Getting in
| Call | What it does |
|---|---|
GET /api/v1 | the routes below, and which backend runs |
POST /api/v1/auth/login | a username and a password for a token |
POST /api/v1/auth/logout | ends the token now |
GET /api/v1/auth/me | who this bearer is, and whether it is a key |
curl -X POST https://memo.example/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username": "you", "password": "your password"}'
The answer holds a token good for a week of use. This works where Memo is the
one keeping passwords, so with --auth=local and --auth=ldap. Behind a
portal or an OIDC provider there is no password here to check, and a key is the
way in. A key cannot log itself out; Take it back in My account is what
ends a key.
Boards
| Call | What it does |
|---|---|
GET /api/v1/boards | the boards you made, were let into, or have opened |
POST /api/v1/boards | make one |
GET /api/v1/boards/NAME | one board, its columns and what is on it |
PATCH /api/v1/boards/NAME | change its title, or its visibility |
DELETE /api/v1/boards/NAME | take it away, notes and all |
GET /api/v1/boards/NAME/export | the whole board, as the export file holds it |
The body that makes a board takes title, and then kind set to mindmap for
a mind map, template naming a template (kanban, week, weather), and
visibility set to restricted to close it from the start. The board belongs
to the account behind the call.
Administration
| Call | What it does |
|---|---|
GET /api/v1/admin/overview | every board and every account at once |
GET /api/v1/admin/boards | every board, whoever made it |
DELETE /api/v1/admin/boards/NAME | delete any board |
GET /api/v1/admin/users | every account |
PATCH /api/v1/admin/users/ID | give the admin role or take it back |
DELETE /api/v1/admin/users/ID | delete an account |
GET /api/v1/admin/audit | what has been done here lately |
GET /api/v1/admin/backups | the snapshots on the server |
POST /api/v1/admin/backups | take one now |
GET /api/v1/admin/backups/NAME | download one |
DELETE /api/v1/admin/backups/NAME | remove one |
GET /api/v1/admin/appearance | everything the Appearance tab shows |
PUT /api/v1/admin/appearance | change them |
DELETE /api/v1/admin/appearance | put them back |
A key made by an admin carries the admin role, so a nightly script can take a backup with no password sitting in it. The other side of that is that such a key is worth as much as the password. Give a script an account of its own where you can, and take its key back the day the script stops.
Addresses that answer without a bearer
These are what a browser asks for, and a program can ask for them the same way.
Each answers to whoever may see the board behind it, through the session cookie
or a ?s=TOKEN share link.
| Call | What it does |
|---|---|
GET /NAME/export.json | a board as the export file holds it |
GET /images/board/NAME/ID | a picture on a board, ?t for the small one |
GET /images/shared/TOKEN/ID | the same picture, reached through a share link |
GET /account/avatar/ID | somebody's picture |
GET /css/instance.css | the colours this instance changed, empty if none |
GET /api/name?t=TITLE | the free board name a title would take |
GET /api/wiki?url=ADDRESS | the card decks sitting at an address |
The last two ask for a signed-in browser unless --allowAnonymousBoards is on,
since they are the home page working out what a new board would be called.
The older way to make a board
curl -X POST https://memo.example/api/boards \
-H 'Authorization: Bearer THE-KEY' \
-H 'Content-Type: application/json' \
-d '{"title": "Minutes of the 12th"}'
POST /api/boards does what POST /api/v1/boards does, takes the same body,
and still answers so that anything written before /api/v1 keeps working. New
programs should use /api/v1/boards, which also takes a session token and not
only a key.
What the pages call for themselves
The pages in the browser talk to a second set of addresses. They go by the
session cookie with a CSRF token in the body, they refuse a request from
another origin, and /api/v1 does all the same work for a program. They are
listed here so that nothing in a log comes as a surprise.
| Call | What it does |
|---|---|
POST /new | the form on the home page |
POST /login, POST /register | the sign-in and sign-up forms |
POST /logout | ends the session |
GET /account/api/boards | the account and its boards |
POST /account/api/boards/visits | remembers the boards this browser opened |
POST /account/api/boards/forget | drops one from that list |
POST /account/api/boards/favourite | stars one, or unstars it |
GET, POST /account/api/keys | the API keys, and a new one |
POST /account/api/keys/delete | takes a key back |
POST /account/api/password | changes the password |
POST /account/api/language | remembers the language chosen |
POST /account/api/avatar | sets or clears the picture |
GET /admin/api/overview | the boards and accounts the admin page shows |
POST /admin/api/boards/delete | deletes the ticked boards |
GET /admin/api/backups | the snapshots on the server |
POST /admin/api/backups/take | takes one |
POST /admin/api/backups/delete | removes one |
GET /admin/api/backups/NAME | downloads one |
POST /admin/api/users/admin | gives the admin role or takes it back |
POST /admin/api/users/delete | deletes the ticked accounts |
GET /admin/api/appearance | everything the Appearance tab shows |
POST /admin/api/appearance/save | saves them |
POST /admin/api/appearance/reset | puts them back |
POST /admin/api/appearance/logo | sends the logo, or takes it away |
Everything else the server answers to is a page or a file a page loads: /,
/NAME for a board, /s/TOKEN for a share link, /docs, /demo, /login,
/register, /account, /admin and its tabs /admin/boards,
/admin/users, /admin/backups, /admin/appearance, /auth/callback where
an OIDC provider comes back, /logo.webp for the instance's logo, and the
static /css/, /images/, /i18n/, /board/, /mind/, /shared/,
/vendor/, the page scripts and the font.
Everything that happens on an open board goes through the WebSocket at /ws
instead: notes, columns, boxes, the people present. It is not part of this API
and its messages change with the client that speaks them.
