NemoMemo

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/memos

Tokens come in two scopes:

ScopeCan do
Full accessEverything a signed-in you can do with memos — read, write, edit, delete
Create onlyPOST /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

MethodPathNotes
POST/auth/signupFirst user becomes admin
POST/auth/signinSets the session cookie
POST/auth/signout
GET/auth/meThe signed-in viewer

Access tokens

Session-only — a token can never manage tokens.

MethodPathNotes
GET/tokensYour 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/:idRevoke; takes effect immediately

Memos

MethodPathNotes
GET/memosscope=home|explore|profile, state, creator, filter, orderBy, dir, pageSize, pageToken
POST/memos{ content, visibility?, dory?, attachmentUids? }
GET/memos/:uid
PATCH/memos/:uidAny of content, visibility, pinned, rowStatus, dory, attachmentUids
DELETE/memos/:uidDeletes comments too
GET / POST/memos/:uid/commentsComments are memos with a parent
POST/memos/:uid/reactions{ emoji } — must be in the instance set
DELETE/memos/:uid/reactions/:emojiYour own reaction
POST / GET/memos/:uid/shares{ expiresIn: "1d"|"7d"|"30d"|"never" }
GET/shares/:tokenPublic — resolves a share link
DELETE/shares/:tokenRevoke

The filter parameter accepts the same expression language as the UI. Lists paginate with pageSize (default 20, max 200) and an opaque nextPageToken.

Users

MethodPathNotes
GET/users/:usernamePublic profile
GET/users/:username/statsHeatmap timestamps, tag counts
GET/users/-/tagsYour tag → count map
POST/users/-/tags/rename{ from, to }
GET / PATCH/users/-/settingsPreferences + saved views
PATCH/users/-/accountNickname, email, avatar, password
GET / POST/usersAdmin: list / create members
PATCH / DELETE/users/:username/adminAdmin: role, archive, delete

Inbox, attachments, instance

MethodPathNotes
GET/inbox?status=UNREAD|ARCHIVEDIncludes unreadCount
PATCH / DELETE/inbox/:idArchive or delete one
POST/inbox/read-all
POST/attachmentsMultipart file field, 32 MiB max
GET/attachmentstype=media|audio|document, unlinked=true
DELETE/attachments/unusedBulk-delete unlinked uploads
GET/file/attachments/:uid/:filenameRange requests; ?thumbnail=1; ?share=token
GET/instance/profilePublic: name, mode, needsSetup
GET / PATCH/instance/settingsAdmin

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}'