Reference
The Papyra API
Everything the web app does, your scripts can do. 109 operations across 90 paths, described by an OpenAPI 3.1.1 document your own instance publishes.
Authenticating
Create a personal access token in Settings → API Keys. It is shown once, at creation, because Papyra stores only a hash of it.
Send it as X-API-Key, or as Authorization: Bearer if that suits your client better — the two are equivalent. It is an opaque token, nota JWT; there is nothing to decode.
# Settings → API Keys creates the token. It is shown once.
export PAPYRA=https://notes.example.com
export PAPYRA_TOKEN=pk_live_...
curl -s "$PAPYRA/api/notes" -H "X-API-Key: $PAPYRA_TOKEN"A token inherits the account that made it, including its folder. It can reach exactly that person's notes and nothing else.
Creating and updating a note
There is no POST /api/notes. A note's id is its filename on disk, and you choose it, so creating and updating are the same call:PUT /api/notes/{id}. Writing to an id that does not exist creates it; writing to one that does replaces it, snapshotting the previous revision first.
# There is no POST for notes. A new note is a PUT to an id you choose,
# and the same call updates an existing one.
curl -s -X PUT "$PAPYRA/api/notes/shopping-list" \
-H "X-API-Key: $PAPYRA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Shopping list",
"body": "- [ ] Coffee\n- [ ] Olive oil",
"tags": ["home"],
"kind": "todo"
}'Searching
# Keyword search (Lucene), then meaning-based search over embeddings.
curl -s "$PAPYRA/api/search?q=pricing" -H "X-API-Key: $PAPYRA_TOKEN"
curl -s "$PAPYRA/api/search/semantic?q=pricing" -H "X-API-Key: $PAPYRA_TOKEN"Semantic search needs an embedding model configured — seethe assistant. Keyword search always works.
Trying requests
This page is a reference, not a console — there is no public Papyra instance for it to call. Your own instance serves a live, interactive portal at/docs, where you paste your token and actually issue requests. That is deliberately reachable in production so self-hosters have real documentation for their own server.
The document behind this page is at /openapi/v1.json on your instance — point any OpenAPI tool at it.
Two things to know while reading
- Everything needs authentication except
/api/shared/{token}, which is the public share-link surface and is anonymous by design. - Only 26 of 109 operations carry a written summary today. The rest are listed with their real shape — method, path, parameters and request body — but no prose. That gap istracked upstream.
Auth17
POST
/api/auth/accept-inviteRedeem an invitation and create the accountRequest body — ResetPasswordRequest
tokenpassword
Responses
200
GET
/api/auth/avatarResponses
200
POST
/api/auth/avatarResponses
200
GET
/api/auth/avatar/{username}Parameters
username
Responses
200
POST
/api/auth/forgot-passwordRequest a password reset linkRequest body — ForgotPasswordRequest
usernameOrEmail
Responses
200
POST
/api/auth/loginRequest body — LoginRequest
usernamepassword
Responses
200
GET
/api/auth/login/ssoResponses
200
POST
/api/auth/logoutResponses
200
GET
/api/auth/meResponses
200
GET
/api/auth/notificationsResponses
200
PUT
/api/auth/notificationsRequest body — NotificationPrefsWrite
mentionshare
Responses
200
POST
/api/auth/passwordRequest body — PasswordRequest
currentnext
Responses
200
PUT
/api/auth/profileRequest body — ProfileRequest
nameemail
Responses
200
GET
/api/auth/providersResponses
200
POST
/api/auth/reset-passwordSet a new password from a reset linkRequest body — ResetPasswordRequest
tokenpassword
Responses
200
POST
/api/auth/setupRequest body — SetupRequest
usernamenameemailpassword
Responses
200
GET
/api/auth/token/{token}Parameters
token
Responses
200
Notes17
GET
/api/notesList notesReturns the caller's notes (metadata + body) from the in-memory vault. Optional from/to filter by last-modified date.
Parameters
fromto
Responses
200
PUT
/api/notes/{id}Parameters
id
Request body — NoteWrite
titletagscolorpinnedarchivedbodykindsecure
Responses
200
DELETE
/api/notes/{id}Parameters
id
Responses
200
GET
/api/notes/{id}/backlinksList backlinksNotes that reference this note through a [[Title]] wikilink, each with a highlighted snippet.
Parameters
id
Responses
200
GET
/api/notes/{id}/blocksList a note's block anchorsEvery `^id` anchor in the note, in document order, for building a block reference.
Parameters
id
Responses
200
GET
/api/notes/{id}/blocks/{blockId}Resolve one anchored blockReturns only the anchored block's text — never the surrounding note body.
Parameters
idblockId
Responses
200
POST
/api/notes/{id}/restore/{snapshotId}Parameters
idsnapshotId
Responses
200
GET
/api/notes/{id}/secureReveal a secure note's bodyRequires a valid X-Unlock-Token from a successful WebAuthn assertion; 401 otherwise.
Parameters
id
Responses
200
GET
/api/notes/{id}/snapshotsParameters
id
Responses
200
GET
/api/notes/{id}/snapshots/{snapshotId}Parameters
idsnapshotId
Responses
200
POST
/api/notes/{id}/trashParameters
id
Responses
200
POST
/api/notes/{id}/untrashParameters
id
Responses
200
GET
/api/notes/activityNote activity heatmapResponses
200
GET
/api/notes/orderResponses
200
PUT
/api/notes/orderRequest body — OrderWrite
entries
Responses
200
Search2
GET
/api/searchParameters
q
Responses
200
GET
/api/search/semanticParameters
qtake
Responses
200
AI7
POST
/api/ai/chatRequest body — AiChatRequest
questionsessionId
Responses
200
GET
/api/ai/modelsDownloadable local modelsResponses
200
GET
/api/ai/sessionsResponses
200
GET
/api/ai/sessions/{id}Parameters
id
Responses
200
PATCH
/api/ai/sessions/{id}Parameters
id
Request body — ChatSessionRename
title
Responses
200
DELETE
/api/ai/sessions/{id}Parameters
id
Responses
200
GET
/api/ai/statusWhether the assistant can answer, and why notResponses
200
Categories3
GET
/api/categoriesResponses
200
POST
/api/categoriesRequest body — CategoryWrite
namecolor
Responses
200
DELETE
/api/categories/{name}Parameters
name
Responses
200
Collections4
GET
/api/collectionsResponses
200
POST
/api/collectionsRequest body — SmartCollectionWrite
namerulesJson
Responses
200
DELETE
/api/collections/{id}Parameters
id
Responses
200
GET
/api/collections/{id}/notesParameters
id
Responses
200
Sharing9
Inbox3
GET
/api/inboxList inbox entriesEach entry is one anchored block another user pinged you with, already resolved.
Responses
200
DELETE
/api/inbox/{id}Dismiss an inbox entryParameters
id
Responses
200
POST
/api/inbox/readMark all inbox entries readResponses
200
Media2
GET
/api/media/{filename}Parameters
filename
Responses
200
POST
/api/media/uploadParameters
noteId
Responses
200
Import & export3
GET
/api/exportResponses
200
POST
/api/import/{provider}Parameters
provider
Responses
200
POST
/api/import/quickResponses
200
Backups2
POST
/api/backups/generateGenerate encrypted backupVerifies the account password, then streams an AES-GCM encrypted .papyra-vault of the caller's notes + media.
Request body — BackupRequest
password
Responses
200
POST
/api/backups/restoreRestore from encrypted backupDecrypts an uploaded .papyra-vault (multipart: password + file) and replaces the caller's notes + media, then rebuilds the cache.
Responses
200
Git3
GET
/api/gitResponses
200
PUT
/api/gitConfigure git backup of your own vaultSets the remote for a mirror of the caller's vault only. Papyra's own state (.papyra/, .trash/) is gitignored. Each account has its own repository and its own credentials; no account can configure or trigger another's.
Request body — GitConfigWrite
remoteUrlbranchtoken
Responses
200
POST
/api/git/syncBack up your vault nowStages, commits and pushes the caller's vault. Returns status 'pushed', 'clean', or 'conflict' — a diverged remote is never force-pushed; the conflict flag is raised instead and the remote is left untouched.
Responses
200
Webhooks3
GET
/api/webhooksResponses
200
POST
/api/webhooksRequest body — WebhookWrite
eventurlsecret
Responses
200
DELETE
/api/webhooks/{id}Parameters
id
Responses
200
Settings2
GET
/api/settingsResponses
200
PUT
/api/settingsRequest body — SettingsRequest
trashRetentionDays
Responses
200
Conflicts3
GET
/api/conflictsResponses
200
GET
/api/conflicts/{id}Parameters
id
Responses
200
POST
/api/conflicts/{id}/resolveParameters
id
Request body — ResolveConflictRequest
keep
Responses
200
API Keys3
GET
/api/keysResponses
200
POST
/api/keysCreate API keyGenerates a personal access token. The raw token is returned once — store it; only its hash is kept.
Request body — ApiKeyWrite
name
Responses
200
DELETE
/api/keys/{id}Parameters
id
Responses
200
Directory1
GET
/api/users/searchParameters
q
Responses
200
WebAuthn6
POST
/api/auth/webauthn/challengeResponses
200
GET
/api/auth/webauthn/credentialsResponses
200
DELETE
/api/auth/webauthn/credentials/{id}Parameters
id
Responses
200
POST
/api/auth/webauthn/register/challengeResponses
200
POST
/api/auth/webauthn/register/verifyRequest body — WebAuthnRegisterRequest
responsename
Responses
200
POST
/api/auth/webauthn/verifyRequest body — WebAuthnAssertRequest
response
Responses
200
Admin18
GET
/api/ai/configResponses
200
PUT
/api/ai/configConfigure the AI provider (admin)adminSelects the chat and embedding backends and stores their API keys. Takes effect immediately — AiClient re-reads its settings when the config version bumps.
Request body — AiConfigWrite
chatProviderembedProviderollamaBaseUrlollamaChatModelollamaEmbedModelopenAiBaseUrlopenAiChatModelopenAiEmbedModelopenAiKeyanthropicChatModelanthropicKey
Responses
200
POST
/api/ai/pullDownload a local model (admin)adminRequest body — AiPullRequest
model
Responses
200
GET
/api/auth/oidcResponses
200
PUT
/api/auth/oidcConfigure SSO (admin)adminStores the OIDC authority, client id and secret. Takes effect immediately — the cached authentication options for the `oidc` scheme are evicted on save.
Request body — OidcConfigWrite
enabledauthorityclientIdclientSecretdisplayName
Responses
200
GET
/api/auth/smtpResponses
200
PUT
/api/auth/smtpConfigure outbound email (admin)adminRequest body — SmtpConfigWrite
enabledhostportuseSslusernamepasswordfromAddressfromNamepublicUrl
Responses
200
POST
/api/auth/smtp/inviteInvite a user by email (admin)adminRequest body — InviteRequest
usernameemailrole
Responses
200
POST
/api/auth/smtp/testSend a test email (admin)adminRequest body — SmtpTestRequest
to
Responses
200
GET
/api/auth/usersResponses
200
POST
/api/auth/usersRequest body — ProvisionRequest
usernamenameemailpasswordrolesendEmail
Responses
200
DELETE
/api/auth/users/{id}Parameters
id
Responses
200
POST
/api/auth/users/{id}/recovery-linkParameters
id
Request body — RecoveryLinkRequest
sendEmail
Responses
200
POST
/api/auth/users/{id}/resetParameters
id
Request body — ResetRequest
passwordsendEmail
Responses
200
GET
/api/jobsResponses
200
POST
/api/jobs/{id}/runParameters
id
Responses
200
POST
/api/system/rebuild-embeddingsResponses
200
POST
/api/system/rebuild-indexResponses
200
System1
POST
/api/system/prune-mediaPrune unreferenced media now (admin)adminMoves media files that no live note references into the owning tenant's .trash and reports how many moved. Same sweep the nightly background service runs; nothing is deleted outright.
Responses
200