API reference
The REST API behind everything the app does.
Everything the web app does goes through a JSON API at /api/v1. Errors come back as
{ "error": { "code", "message" } } with a matching HTTP status; unknown paths return a
JSON 404.
Authentication
Two ways in.
Access tokens are the one to use for scripts, phone shortcuts, and bots. Mint one in Settings → Tokens (it's shown once — copy it then), and send it as a bearer header:
curl -H "Authorization: Bearer nm_yourtokenhere" \
-H "content-type: application/json" \
-d '{"content":"posted from a script #cli"}' \
https://your-reef.example/api/v1/memosTokens come in two scopes:
| Scope | Can do |
|---|---|
| Full access | Everything a signed-in you can do with memos — read, write, edit, delete |
| Create only | POST /memos and POST /attachments, nothing else — not even reading your reef back |
Give a capture script the create-only scope: if the device it lives on is lost, the token can add memos but can never read what you've written.
No token can manage tokens, change your account's email or password, or use admin endpoints — those need a real signed-in session, so a leaked token can never become a lost account. Revoke a token any time in Settings; it stops working immediately.
Session cookies are what the web app itself uses, from POST /auth/signin — for
scripts, keep a cookie jar (see the example below). Sessions last 30 days and
slide on use.
Auth
| Method | Path | Notes |
|---|---|---|
| POST | /auth/signup | First user becomes admin |
| POST | /auth/signin | Sets the session cookie |
| POST | /auth/signout | |
| GET | /auth/me | The signed-in viewer |
Access tokens
Session-only — a token can never manage tokens.
| Method | Path | Notes |
|---|---|---|
| GET | /tokens | Your tokens; never includes the secret |
| POST | /tokens | { name, scope?: "FULL"|"CREATE_ONLY", expiresIn?: "30d"|"90d"|"1y"|"never" } — the plaintext comes back exactly once |
| DELETE | /tokens/:id | Revoke; takes effect immediately |
Memos
| Method | Path | Notes |
|---|---|---|
| GET | /memos | scope=home|explore|profile, state, creator, filter, orderBy, dir, pageSize, pageToken |
| POST | /memos | { content, visibility?, dory?, attachmentUids? } |
| GET | /memos/:uid | |
| PATCH | /memos/:uid | Any of content, visibility, pinned, rowStatus, dory, attachmentUids |
| DELETE | /memos/:uid | Deletes comments too |
| GET / POST | /memos/:uid/comments | Comments are memos with a parent |
| POST | /memos/:uid/reactions | { emoji } — must be in the instance set |
| DELETE | /memos/:uid/reactions/:emoji | Your own reaction |
| POST / GET | /memos/:uid/shares | { expiresIn: "1d"|"7d"|"30d"|"never" } |
| GET | /shares/:token | Public — resolves a share link |
| DELETE | /shares/:token | Revoke |
The filter parameter accepts the same expression language as the UI.
Lists paginate with pageSize (default 20, max 200) and an opaque nextPageToken.
Users
| Method | Path | Notes |
|---|---|---|
| GET | /users/:username | Public profile |
| GET | /users/:username/stats | Heatmap timestamps, tag counts |
| GET | /users/-/tags | Your tag → count map |
| POST | /users/-/tags/rename | { from, to } |
| GET / PATCH | /users/-/settings | Preferences + saved views |
| PATCH | /users/-/account | Nickname, email, avatar, password |
| GET / POST | /users | Admin: list / create members |
| PATCH / DELETE | /users/:username/admin | Admin: role, archive, delete |
Inbox, attachments, instance
| Method | Path | Notes |
|---|---|---|
| GET | /inbox?status=UNREAD|ARCHIVED | Includes unreadCount |
| PATCH / DELETE | /inbox/:id | Archive or delete one |
| POST | /inbox/read-all | |
| POST | /attachments | Multipart file field, 32 MiB max |
| GET | /attachments | type=media|audio|document, unlinked=true |
| DELETE | /attachments/unused | Bulk-delete unlinked uploads |
| GET | /file/attachments/:uid/:filename | Range requests; ?thumbnail=1; ?share=token |
| GET | /instance/profile | Public: name, mode, needsSetup |
| GET / PATCH | /instance/settings | Admin |
Example
curl -s -c jar -X POST http://localhost:5230/api/v1/auth/signin \
-H 'content-type: application/json' \
-d '{"username":"david","password":"..."}'
curl -s -b jar -X POST http://localhost:5230/api/v1/memos \
-H 'content-type: application/json' \
-d '{"content":"Hello from the API! #dev","visibility":"PUBLIC","dory":true}'