Skip to content

API Reference

This document is the authoritative V1 API contract. The mobile client is the source of truth: the Go server must match it exactly (method + path + response envelope).

Reference file: docs/api-v1.md in the repository.

  • Base URL: http://<host>:8080/api/v1
  • Format: JSON everywhere, except POST /files/upload (multipart).
  • Success envelope: { "data": T, "meta"?: { "page", "pageSize", "total" } } (meta is present on paginated lists).
  • Error envelope: { "error": { "code", "message" } } + proper HTTP status.
  • The device registers first: POST /devices with a client-generated identifier (32-hex). The response contains { "deviceId" } only — no token.
  • Then POST /auth/login (username, password, device_id) returns a PASETO v4-local token.
  • Subsequent requests use Authorization: Bearer <token>.
  • Token TTL is 7 days, no refresh. Signing secret: AUTH_SECRET.
  • Identifiers are opaque 32-hex lowercase strings (^[0-9a-f]{32}$).
MethodPathAuthDescription
GET/healthStatus probe, { "status": "healthy" }.
POST/devicesRegister a device (client-generated deviceId).
POST/auth/loginLogin, returns { token, expires_at, user }.
GET/shares/links/:tokenPublic share-link resolution.
GET/users/resolve?username=Resolve a username (exact match only).
PATCH/users/me/passwordChange own password.
GET/filesList files (folderId?, page?, pageSize?, sort?).
GET/files/:idGet one file.
DELETE/files/:idDelete a file (owner-only for shared resources).
GET/files/search?q=Search files.
GET/files/foldersList root folders.
POST/files/uploadUpload (multipart file, optional folderId).
POST/ocr/jobsCreate an OCR job ({ "fileId" }).
GET/ocr/jobs/:idPoll an OCR job.
POST/sync/opsApply client outbox operations (sequential).
GET/sync/permissions?after=Permission snapshot / delta.

The client sends a batch of operations. Each operation:

{
"operations": [
{
"operation_id": "…32-hex",
"ref_type": "resource",
"ref_id": null,
"resource_id": "…32-hex",
"resource_type": "folder",
"operation": "create_resource",
"payload": {}
}
]
}

Supported operations: create_resource, update_metadata, delete_resource, move_resource, share, revoke_share, update_share, create_link, revoke_link.

Key rules:

  • Operations are applied sequentially; the server stops at the first non-idempotent error and returns the index reached.
  • Idempotence is scoped per device: UNIQUE(device_id, operation_id). Replayed operations are no-ops.
  • delete_resource is idempotent (deleting a missing resource succeeds).
  • Sharing requires the resource to be owned by the caller.

Response:

{ "applied": 3, "failed": { "operation_id": "", "code": "NAME_CONFLICT", "message": "" } }

Snapshot — GET /sync/permissions?after=<cached_at_ms>

Section titled “Snapshot — GET /sync/permissions?after=<cached_at_ms>”

Returns the effective permissions for the current user. The server is the source of truth for effective_access resolution (viewer < commenter < editor < owner). Ancestors propagate only when inherit = true; expired relations are ignored. A after delta filters on resource.updated_at and the winning relation’s updated_at. After 24h without reseed, the client downgrades its cache to read-only (viewer).

CodeHTTPMeaning
UNAUTHORIZED401Missing/invalid/expired token, or bad login credentials (indistinguishable by design).
INVALID_DEVICE_ID400Device not registered.
NOT_FOUND404Resource, user or link not found.
NAME_CONFLICT409Same name in the same parent (or root).
FILE_TOO_LARGE413Upload exceeds MAX_FILE_SIZE_MB.
GRANTEE_NOT_FOUND404Share target user does not exist.
INVALID_PASSWORD403Wrong current password on password change.
NETWORK_ERROR / INVALID_RESPONSE / HTTP_<status>Client-only errors.
  • POST /ocr/jobs { "fileId" }{ "id", "status": "queued" }, processed asynchronously in a goroutine.
  • Poll GET /ocr/jobs/:id every 3s until done/failed (queued → processing → done | failed).
  • Engine: Tesseract system call (OCR_LANG). PDFs get their text layer extracted; scanned PDFs yield empty text (out of V1 scope).