API Reference

Complete REST API documentation for programmatic access to Ebla. All endpoints use JSON for request and response bodies.

Overview

The Ebla API is organized around REST principles. It accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes and verbs.

Base URL

https://your-server:6333/api/v1

Content Type

All requests must include the Content-Type header:

Content-Type: application/json

API Versioning

The API version is included in the URL path. The current version is v1. When breaking changes are introduced, a new version will be released while maintaining backwards compatibility for existing versions.

Authentication

Most API endpoints require authentication. Ebla uses JWT (JSON Web Tokens) for authentication.

Obtaining a Token

Authenticate with your email and password to receive access and refresh tokens:

POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "your-password"
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Using the Token

Include the access token in the Authorization header for all authenticated requests:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Token Refresh

Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without re-authenticating:

POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}

API Keys

For server-to-server integrations, you can create long-lived API keys:

POST /api/v1/user/api-keys
Authorization: Bearer <token>

{
  "name": "CI/CD Integration",
  "scopes": ["library:read", "library:write", "sync:push"]
}

Use API keys with the X-API-Key header:

X-API-Key: ebla_key_abc123...

Error Handling

The API uses standard HTTP status codes to indicate success or failure:

Status Meaning
200 Success
201 Created - resource successfully created
204 No Content - success with no response body
400 Bad Request - invalid parameters
401 Unauthorized - missing or invalid token
403 Forbidden - insufficient permissions
404 Not Found - resource doesn't exist
409 Conflict - resource already exists or conflict detected
422 Unprocessable Entity - validation failed
429 Too Many Requests - rate limit exceeded
500 Internal Server Error

Error Response Format

All errors return a consistent JSON structure:

{
  "error": {
    "code": "validation_failed",
    "message": "The request body contains invalid fields",
    "details": [
      {
        "field": "email",
        "message": "must be a valid email address"
      }
    ]
  }
}

Common Error Codes

Code Description
invalid_token The access token is invalid or expired
permission_denied User lacks required permissions
not_found The requested resource was not found
validation_failed Request validation failed
conflict A sync conflict was detected
rate_limited Too many requests, slow down
quota_exceeded Storage or usage quota exceeded

Pagination

List endpoints support cursor-based pagination for efficient traversal of large datasets.

Request Parameters

Parameter Type Default Description
limit integer 50 Number of items to return (max 100)
cursor string - Cursor for next page (from previous response)
order string desc Sort order: asc or desc

Response Format

{
  "data": [...],
  "pagination": {
    "cursor": "eyJpZCI6IjEyMzQ1In0=",
    "has_more": true,
    "total": 1523
  }
}

Example: Paginating Through Results

# First page
GET /api/v1/libraries/abc123/files?limit=50

# Next page (use cursor from previous response)
GET /api/v1/libraries/abc123/files?limit=50&cursor=eyJpZCI6IjEyMzQ1In0=

Auth Endpoints

POST /auth/login

Authenticate a user and receive tokens.

POST /api/v1/auth/login

{
  "email": "user@example.com",
  "password": "your-password"
}

Response 200:
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "name": "John Doe",
    "is_admin": false
  }
}

POST /auth/refresh

Refresh an access token using a refresh token.

POST /api/v1/auth/refresh

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}

Response 200:
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 3600,
  "token_type": "Bearer"
}

POST /auth/logout

Invalidate tokens and end the session.

POST /api/v1/auth/logout
Authorization: Bearer <token>

Response 204: No Content

POST /auth/cli/initiate

Initiate browser-based CLI authentication. Returns a device code for the user to enter in the browser.

POST /api/v1/auth/cli/initiate

Response 200:
{
  "device_code": "abc123def456",
  "user_code": "ABCD-EFGH",
  "verification_url": "https://server:6333/app/cli-auth",
  "expires_in": 300,
  "poll_interval": 5
}

POST /auth/cli/poll

Poll for CLI authentication completion.

POST /api/v1/auth/cli/poll

{
  "device_code": "abc123def456"
}

Response 200 (pending):
{
  "status": "pending"
}

Response 200 (authorized):
{
  "status": "authorized",
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_in": 3600,
  "user": { ... }
}

User Endpoints

GET /user/me

Get the authenticated user's profile.

GET /api/v1/user/me
Authorization: Bearer <token>

Response 200:
{
  "id": "usr_abc123",
  "email": "user@example.com",
  "name": "John Doe",
  "is_admin": false,
  "created_at": "2026-01-01T00:00:00Z",
  "storage_used": 1073741824,
  "storage_quota": 10737418240
}

PATCH /user/me

Update the authenticated user's profile.

PATCH /api/v1/user/me
Authorization: Bearer <token>

{
  "name": "Jane Doe"
}

Response 200:
{
  "id": "usr_abc123",
  "email": "user@example.com",
  "name": "Jane Doe",
  ...
}

POST /user/me/password

Change the authenticated user's password.

POST /api/v1/user/me/password
Authorization: Bearer <token>

{
  "current_password": "old-password",
  "new_password": "new-strong-password"
}

Response 204: No Content

GET /user/api-keys

List the user's API keys.

GET /api/v1/user/api-keys
Authorization: Bearer <token>

Response 200:
{
  "data": [
    {
      "id": "key_abc123",
      "name": "CI/CD Integration",
      "scopes": ["library:read", "sync:push"],
      "last_used_at": "2026-01-10T15:30:00Z",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

POST /user/api-keys

Create a new API key.

POST /api/v1/user/api-keys
Authorization: Bearer <token>

{
  "name": "Backup Script",
  "scopes": ["library:read", "file:read"]
}

Response 201:
{
  "id": "key_def456",
  "name": "Backup Script",
  "key": "ebla_key_abc123def456...",
  "scopes": ["library:read", "file:read"],
  "created_at": "2026-01-11T10:00:00Z"
}

Note: The full key is only returned once at creation.

DELETE /user/api-keys/:id

Revoke an API key.

DELETE /api/v1/user/api-keys/key_abc123
Authorization: Bearer <token>

Response 204: No Content

Library Endpoints

GET /libraries

List all libraries accessible to the user.

GET /api/v1/libraries
Authorization: Bearer <token>

Query Parameters:
  - limit (int): Max items to return (default 50)
  - cursor (string): Pagination cursor
  - filter (string): "owned", "shared", or "all" (default)

Response 200:
{
  "data": [
    {
      "id": "lib_abc123",
      "name": "My Documents",
      "owner_id": "usr_abc123",
      "owner_email": "user@example.com",
      "team_id": null,
      "file_count": 1523,
      "total_size": 5368709120,
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-11T10:00:00Z",
      "permission": "owner"
    }
  ],
  "pagination": {
    "cursor": "...",
    "has_more": false,
    "total": 5
  }
}

POST /libraries

Create a new library.

POST /api/v1/libraries
Authorization: Bearer <token>

{
  "name": "Project Files",
  "team_id": "team_abc123"  // optional
}

Response 201:
{
  "id": "lib_def456",
  "name": "Project Files",
  "owner_id": "usr_abc123",
  "team_id": "team_abc123",
  "file_count": 0,
  "total_size": 0,
  "created_at": "2026-01-11T10:00:00Z"
}

GET /libraries/:id

Get library details.

GET /api/v1/libraries/lib_abc123
Authorization: Bearer <token>

Response 200:
{
  "id": "lib_abc123",
  "name": "My Documents",
  "owner_id": "usr_abc123",
  "owner_email": "user@example.com",
  "team_id": null,
  "file_count": 1523,
  "total_size": 5368709120,
  "block_count": 8432,
  "dedup_ratio": 1.34,
  "created_at": "2026-01-01T00:00:00Z",
  "updated_at": "2026-01-11T10:00:00Z",
  "last_commit": {
    "id": "cmt_xyz789",
    "message": "Sync from laptop",
    "created_at": "2026-01-11T09:45:00Z"
  },
  "permission": "owner",
  "settings": {
    "conflict_strategy": "lww",
    "indexing_enabled": true
  }
}

PATCH /libraries/:id

Update library settings.

PATCH /api/v1/libraries/lib_abc123
Authorization: Bearer <token>

{
  "name": "Renamed Library",
  "settings": {
    "conflict_strategy": "three-way"
  }
}

Response 200:
{
  "id": "lib_abc123",
  "name": "Renamed Library",
  ...
}

DELETE /libraries/:id

Delete a library. This removes all files and commits permanently.

DELETE /api/v1/libraries/lib_abc123
Authorization: Bearer <token>

Response 204: No Content
Danger Zone

Deleting a library is irreversible. All files, commits, and associated data will be permanently removed.

File Endpoints

GET /libraries/:id/files

List files in a library at a specific path.

GET /api/v1/libraries/lib_abc123/files
Authorization: Bearer <token>

Query Parameters:
  - path (string): Directory path (default "/")
  - recursive (bool): Include subdirectories (default false)
  - limit (int): Max items to return
  - cursor (string): Pagination cursor

Response 200:
{
  "data": [
    {
      "path": "/documents/report.pdf",
      "name": "report.pdf",
      "type": "file",
      "size": 1048576,
      "mime_type": "application/pdf",
      "checksum": "sha256:abc123...",
      "modified_at": "2026-01-10T14:30:00Z",
      "created_at": "2026-01-05T09:00:00Z"
    },
    {
      "path": "/documents/images",
      "name": "images",
      "type": "directory",
      "size": 0,
      "modified_at": "2026-01-10T12:00:00Z"
    }
  ],
  "pagination": { ... }
}

GET /libraries/:id/files/*path

Get metadata for a specific file.

GET /api/v1/libraries/lib_abc123/files/documents/report.pdf
Authorization: Bearer <token>

Response 200:
{
  "path": "/documents/report.pdf",
  "name": "report.pdf",
  "type": "file",
  "size": 1048576,
  "mime_type": "application/pdf",
  "checksum": "sha256:abc123def456...",
  "blocks": ["blk_001", "blk_002", "blk_003"],
  "modified_at": "2026-01-10T14:30:00Z",
  "created_at": "2026-01-05T09:00:00Z",
  "versions": 5,
  "indexed": true,
  "thumbnail_available": true
}

GET /libraries/:id/files/*path/download

Download a file's content.

GET /api/v1/libraries/lib_abc123/files/documents/report.pdf/download
Authorization: Bearer <token>

Response 200:
Content-Type: application/pdf
Content-Disposition: attachment; filename="report.pdf"
Content-Length: 1048576

[binary content]

GET /libraries/:id/files/*path/thumbnail

Get a thumbnail preview for supported file types (images, PDFs, videos).

GET /api/v1/libraries/lib_abc123/files/photos/vacation.jpg/thumbnail
Authorization: Bearer <token>

Query Parameters:
  - size (string): "small" (100px), "medium" (300px), "large" (600px)

Response 200:
Content-Type: image/webp
[binary content]

DELETE /libraries/:id/files/*path

Delete a file or directory.

DELETE /api/v1/libraries/lib_abc123/files/documents/old-report.pdf
Authorization: Bearer <token>

Response 204: No Content

POST /libraries/:id/files/*path/move

Move or rename a file.

POST /api/v1/libraries/lib_abc123/files/documents/report.pdf/move
Authorization: Bearer <token>

{
  "destination": "/archive/2025/report.pdf"
}

Response 200:
{
  "path": "/archive/2025/report.pdf",
  ...
}

POST /libraries/:id/files/*path/copy

Copy a file to a new location.

POST /api/v1/libraries/lib_abc123/files/documents/template.docx/copy
Authorization: Bearer <token>

{
  "destination": "/documents/new-doc.docx"
}

Response 201:
{
  "path": "/documents/new-doc.docx",
  ...
}

Sync Endpoints

GET /libraries/:id/commits

List commits (sync snapshots) for a library.

GET /api/v1/libraries/lib_abc123/commits
Authorization: Bearer <token>

Query Parameters:
  - limit (int): Max items to return
  - cursor (string): Pagination cursor
  - since (string): ISO timestamp to filter from

Response 200:
{
  "data": [
    {
      "id": "cmt_xyz789",
      "parent_id": "cmt_xyz788",
      "message": "Sync from laptop",
      "author_id": "usr_abc123",
      "author_email": "user@example.com",
      "device_name": "MacBook Pro",
      "file_count": 1523,
      "additions": 5,
      "modifications": 2,
      "deletions": 0,
      "created_at": "2026-01-11T09:45:00Z"
    }
  ],
  "pagination": { ... }
}

GET /libraries/:id/commits/:commit_id

Get details of a specific commit including changed files.

GET /api/v1/libraries/lib_abc123/commits/cmt_xyz789
Authorization: Bearer <token>

Response 200:
{
  "id": "cmt_xyz789",
  "parent_id": "cmt_xyz788",
  "message": "Sync from laptop",
  "author_id": "usr_abc123",
  "device_name": "MacBook Pro",
  "created_at": "2026-01-11T09:45:00Z",
  "changes": [
    {
      "path": "/documents/new-file.pdf",
      "action": "added",
      "size": 524288
    },
    {
      "path": "/documents/report.pdf",
      "action": "modified",
      "size": 1048576,
      "previous_size": 1024000
    }
  ]
}

POST /libraries/:id/sync/push

Push local changes to the server. Used by the sync client.

POST /api/v1/libraries/lib_abc123/sync/push
Authorization: Bearer <token>

{
  "parent_commit": "cmt_xyz788",
  "message": "Sync from laptop",
  "changes": [
    {
      "path": "/documents/new-file.pdf",
      "action": "add",
      "size": 524288,
      "checksum": "sha256:abc123...",
      "blocks": ["blk_001", "blk_002"]
    }
  ]
}

Response 200:
{
  "commit": {
    "id": "cmt_xyz789",
    "parent_id": "cmt_xyz788",
    ...
  },
  "blocks_needed": []  // blocks server doesn't have
}

Response 409 (conflict):
{
  "error": {
    "code": "conflict",
    "message": "Server has newer changes",
    "server_commit": "cmt_xyz790",
    "conflicts": [
      {
        "path": "/documents/report.pdf",
        "local_checksum": "sha256:abc...",
        "server_checksum": "sha256:def..."
      }
    ]
  }
}

POST /libraries/:id/sync/pull

Pull changes from the server. Used by the sync client.

POST /api/v1/libraries/lib_abc123/sync/pull
Authorization: Bearer <token>

{
  "from_commit": "cmt_xyz785",
  "limit": 100
}

Response 200:
{
  "commits": [
    {
      "id": "cmt_xyz786",
      "parent_id": "cmt_xyz785",
      "changes": [ ... ]
    },
    {
      "id": "cmt_xyz787",
      "parent_id": "cmt_xyz786",
      "changes": [ ... ]
    }
  ],
  "has_more": false,
  "head_commit": "cmt_xyz789"
}

POST /blocks/upload

Upload content blocks. Blocks are deduplicated by checksum.

POST /api/v1/blocks/upload
Authorization: Bearer <token>
Content-Type: multipart/form-data

Form fields:
  - block[0].id: blk_001
  - block[0].checksum: sha256:abc123...
  - block[0].data: [binary]
  - block[1].id: blk_002
  - block[1].checksum: sha256:def456...
  - block[1].data: [binary]

Response 200:
{
  "uploaded": ["blk_001", "blk_002"],
  "deduplicated": []  // blocks that already existed
}

GET /blocks/:id

Download a specific block.

GET /api/v1/blocks/blk_001
Authorization: Bearer <token>

Response 200:
Content-Type: application/octet-stream
[binary content]

Team Endpoints

GET /teams

List teams the user belongs to.

GET /api/v1/teams
Authorization: Bearer <token>

Response 200:
{
  "data": [
    {
      "id": "team_abc123",
      "name": "Engineering",
      "member_count": 12,
      "library_count": 5,
      "role": "admin",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

POST /teams

Create a new team.

POST /api/v1/teams
Authorization: Bearer <token>

{
  "name": "Design Team"
}

Response 201:
{
  "id": "team_def456",
  "name": "Design Team",
  "member_count": 1,
  "library_count": 0,
  "role": "owner",
  "created_at": "2026-01-11T10:00:00Z"
}

GET /teams/:id

Get team details.

GET /api/v1/teams/team_abc123
Authorization: Bearer <token>

Response 200:
{
  "id": "team_abc123",
  "name": "Engineering",
  "member_count": 12,
  "library_count": 5,
  "storage_used": 10737418240,
  "created_at": "2026-01-01T00:00:00Z",
  "settings": {
    "default_library_permission": "viewer"
  }
}

GET /teams/:id/members

List team members.

GET /api/v1/teams/team_abc123/members
Authorization: Bearer <token>

Response 200:
{
  "data": [
    {
      "user_id": "usr_abc123",
      "email": "alice@example.com",
      "name": "Alice",
      "role": "owner",
      "joined_at": "2026-01-01T00:00:00Z"
    },
    {
      "user_id": "usr_def456",
      "email": "bob@example.com",
      "name": "Bob",
      "role": "member",
      "joined_at": "2026-01-05T00:00:00Z"
    }
  ]
}

POST /teams/:id/members

Add a member to the team.

POST /api/v1/teams/team_abc123/members
Authorization: Bearer <token>

{
  "email": "newmember@example.com",
  "role": "member"
}

Response 201:
{
  "user_id": "usr_ghi789",
  "email": "newmember@example.com",
  "role": "member",
  "joined_at": "2026-01-11T10:00:00Z"
}

PATCH /teams/:id/members/:user_id

Update a member's role.

PATCH /api/v1/teams/team_abc123/members/usr_def456
Authorization: Bearer <token>

{
  "role": "admin"
}

Response 200:
{
  "user_id": "usr_def456",
  "role": "admin",
  ...
}

DELETE /teams/:id/members/:user_id

Remove a member from the team.

DELETE /api/v1/teams/team_abc123/members/usr_def456
Authorization: Bearer <token>

Response 204: No Content

POST /teams/:id/invitations

Create an invitation link.

POST /api/v1/teams/team_abc123/invitations
Authorization: Bearer <token>

{
  "role": "member",
  "expires_in": 604800,  // 7 days in seconds
  "max_uses": 10
}

Response 201:
{
  "id": "inv_abc123",
  "code": "TEAM-ABC-XYZ",
  "url": "https://server:6333/join/TEAM-ABC-XYZ",
  "role": "member",
  "expires_at": "2026-01-18T10:00:00Z",
  "max_uses": 10,
  "uses": 0
}

Search Endpoints

GET /search

Search files across all accessible libraries.

GET /api/v1/search
Authorization: Bearer <token>

Query Parameters:
  - q (string): Search query
  - library_id (string): Limit to specific library (optional)
  - type (string): Filter by file type (optional)
  - limit (int): Max results (default 20)

Response 200:
{
  "data": [
    {
      "library_id": "lib_abc123",
      "library_name": "My Documents",
      "path": "/documents/report.pdf",
      "name": "report.pdf",
      "type": "file",
      "mime_type": "application/pdf",
      "size": 1048576,
      "modified_at": "2026-01-10T14:30:00Z",
      "snippet": "...quarterly revenue increased by 15%...",
      "score": 0.89
    }
  ],
  "total": 42
}

POST /search/semantic

Perform semantic (AI-powered) search using embeddings.

POST /api/v1/search/semantic
Authorization: Bearer <token>

{
  "query": "documents about budget planning",
  "library_ids": ["lib_abc123"],
  "limit": 10,
  "threshold": 0.7
}

Response 200:
{
  "data": [
    {
      "library_id": "lib_abc123",
      "path": "/finance/2026-budget.xlsx",
      "name": "2026-budget.xlsx",
      "similarity": 0.92,
      "snippet": "Annual budget planning document with Q1-Q4 projections..."
    }
  ]
}

POST /search/ask

Ask a natural language question about your files (AI Q&A with citations).

POST /api/v1/search/ask
Authorization: Bearer <token>

{
  "question": "What was the Q3 revenue for the ACME project?",
  "library_ids": ["lib_abc123", "lib_def456"]
}

Response 200:
{
  "answer": "The Q3 revenue for the ACME project was $2.4 million, representing a 12% increase over Q2.",
  "citations": [
    {
      "library_id": "lib_abc123",
      "path": "/reports/acme-q3-summary.pdf",
      "name": "acme-q3-summary.pdf",
      "page": 3,
      "excerpt": "ACME Project Q3 Results: Total revenue reached $2.4M, up 12% from Q2's $2.14M..."
    }
  ],
  "confidence": 0.94
}

Share Endpoints

GET /libraries/:id/shares

List sharing settings for a library.

GET /api/v1/libraries/lib_abc123/shares
Authorization: Bearer <token>

Response 200:
{
  "users": [
    {
      "user_id": "usr_def456",
      "email": "colleague@example.com",
      "permission": "editor",
      "granted_at": "2026-01-05T00:00:00Z"
    }
  ],
  "teams": [
    {
      "team_id": "team_abc123",
      "name": "Engineering",
      "permission": "viewer",
      "granted_at": "2026-01-01T00:00:00Z"
    }
  ],
  "links": [
    {
      "id": "link_abc123",
      "url": "https://server:6333/s/abc123xyz",
      "permission": "viewer",
      "password_protected": true,
      "expires_at": "2026-02-01T00:00:00Z",
      "access_count": 5,
      "created_at": "2026-01-10T00:00:00Z"
    }
  ]
}

POST /libraries/:id/shares/users

Share a library with a user.

POST /api/v1/libraries/lib_abc123/shares/users
Authorization: Bearer <token>

{
  "email": "colleague@example.com",
  "permission": "editor"
}

Response 201:
{
  "user_id": "usr_def456",
  "email": "colleague@example.com",
  "permission": "editor",
  "granted_at": "2026-01-11T10:00:00Z"
}

POST /libraries/:id/shares/links

Create a share link for a library or file.

POST /api/v1/libraries/lib_abc123/shares/links
Authorization: Bearer <token>

{
  "path": "/documents/report.pdf",  // optional, omit for library link
  "permission": "viewer",
  "password": "secret123",  // optional
  "expires_in": 604800  // optional, seconds until expiry
}

Response 201:
{
  "id": "link_def456",
  "url": "https://server:6333/s/def456xyz",
  "path": "/documents/report.pdf",
  "permission": "viewer",
  "password_protected": true,
  "expires_at": "2026-01-18T10:00:00Z",
  "created_at": "2026-01-11T10:00:00Z"
}

DELETE /libraries/:id/shares/links/:link_id

Revoke a share link.

DELETE /api/v1/libraries/lib_abc123/shares/links/link_abc123
Authorization: Bearer <token>

Response 204: No Content

GET /s/:code

Access a shared resource via link (public endpoint).

GET /s/abc123xyz

Headers (if password protected):
  X-Share-Password: secret123

Response 200 (file):
Content-Type: application/pdf
[binary content]

Response 200 (directory/library):
{
  "type": "directory",
  "name": "Shared Files",
  "files": [
    {
      "path": "/report.pdf",
      "name": "report.pdf",
      "type": "file",
      "size": 1048576
    }
  ]
}

Admin Endpoints

These endpoints require admin privileges.

GET /admin/users

List all users.

GET /api/v1/admin/users
Authorization: Bearer <admin_token>

Response 200:
{
  "data": [
    {
      "id": "usr_abc123",
      "email": "user@example.com",
      "name": "John Doe",
      "is_admin": false,
      "storage_used": 1073741824,
      "storage_quota": 10737418240,
      "library_count": 3,
      "last_active_at": "2026-01-11T09:00:00Z",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ],
  "pagination": { ... }
}

POST /admin/users

Create a new user.

POST /api/v1/admin/users
Authorization: Bearer <admin_token>

{
  "email": "newuser@example.com",
  "password": "initial-password",
  "name": "New User",
  "is_admin": false,
  "storage_quota": 10737418240
}

Response 201:
{
  "id": "usr_new123",
  "email": "newuser@example.com",
  ...
}

PATCH /admin/users/:id

Update a user's settings.

PATCH /api/v1/admin/users/usr_abc123
Authorization: Bearer <admin_token>

{
  "storage_quota": 21474836480,
  "is_admin": true
}

Response 200:
{
  "id": "usr_abc123",
  "storage_quota": 21474836480,
  "is_admin": true,
  ...
}

DELETE /admin/users/:id

Delete a user and optionally their data.

DELETE /api/v1/admin/users/usr_abc123
Authorization: Bearer <admin_token>

Query Parameters:
  - delete_data (bool): Also delete user's libraries (default false)

Response 204: No Content

GET /admin/stats

Get server statistics.

GET /api/v1/admin/stats
Authorization: Bearer <admin_token>

Response 200:
{
  "users": {
    "total": 150,
    "active_today": 45,
    "active_week": 120
  },
  "libraries": {
    "total": 342,
    "personal": 280,
    "team": 62
  },
  "storage": {
    "total_bytes": 1099511627776,
    "used_bytes": 549755813888,
    "block_count": 2847561,
    "dedup_ratio": 1.42
  },
  "sync": {
    "commits_today": 1523,
    "active_connections": 78
  }
}

POST /admin/backup

Trigger a manual backup.

POST /api/v1/admin/backup
Authorization: Bearer <admin_token>

{
  "type": "full"  // or "incremental"
}

Response 202:
{
  "backup_id": "bak_abc123",
  "status": "running",
  "started_at": "2026-01-11T10:00:00Z"
}

GET /admin/backups

List backups.

GET /api/v1/admin/backups
Authorization: Bearer <admin_token>

Response 200:
{
  "data": [
    {
      "id": "bak_abc123",
      "type": "full",
      "status": "completed",
      "size_bytes": 5368709120,
      "started_at": "2026-01-11T02:00:00Z",
      "completed_at": "2026-01-11T02:45:00Z",
      "path": "/backups/ebla-full-20260111.tar.gz"
    }
  ]
}

POST /admin/gc

Trigger garbage collection.

POST /api/v1/admin/gc
Authorization: Bearer <admin_token>

{
  "dry_run": true
}

Response 200:
{
  "orphaned_blocks": 1523,
  "reclaimable_bytes": 1073741824,
  "dry_run": true
}

WebSocket API

Ebla uses WebSocket connections for real-time sync notifications.

Connection

wss://your-server:6333/api/v1/ws
  ?token=<access_token>

Message Format

All messages are JSON-encoded:

{
  "type": "message_type",
  "payload": { ... }
}

Client Messages

Subscribe to Library

{
  "type": "subscribe",
  "payload": {
    "library_id": "lib_abc123"
  }
}

Unsubscribe from Library

{
  "type": "unsubscribe",
  "payload": {
    "library_id": "lib_abc123"
  }
}

Ping (Keepalive)

{
  "type": "ping"
}

Server Messages

New Commit

{
  "type": "commit",
  "payload": {
    "library_id": "lib_abc123",
    "commit": {
      "id": "cmt_xyz789",
      "parent_id": "cmt_xyz788",
      "author_email": "colleague@example.com",
      "device_name": "Desktop PC",
      "additions": 3,
      "modifications": 1,
      "deletions": 0,
      "created_at": "2026-01-11T10:15:00Z"
    }
  }
}

File Changed

{
  "type": "file_changed",
  "payload": {
    "library_id": "lib_abc123",
    "path": "/documents/report.pdf",
    "action": "modified",
    "commit_id": "cmt_xyz789"
  }
}

Conflict Detected

{
  "type": "conflict",
  "payload": {
    "library_id": "lib_abc123",
    "path": "/documents/shared-doc.docx",
    "local_version": "cmt_abc123",
    "server_version": "cmt_def456"
  }
}

Pong (Response to Ping)

{
  "type": "pong"
}

Error

{
  "type": "error",
  "payload": {
    "code": "subscription_failed",
    "message": "You don't have access to this library"
  }
}

Example: WebSocket Client

// JavaScript example
const ws = new WebSocket('wss://server:6333/api/v1/ws?token=' + accessToken);

ws.onopen = () => {
  // Subscribe to library updates
  ws.send(JSON.stringify({
    type: 'subscribe',
    payload: { library_id: 'lib_abc123' }
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);

  switch (message.type) {
    case 'commit':
      console.log('New commit:', message.payload.commit.id);
      // Trigger sync pull
      break;
    case 'conflict':
      console.log('Conflict detected:', message.payload.path);
      // Show conflict resolution UI
      break;
  }
};

// Send keepalive every 30 seconds
setInterval(() => {
  ws.send(JSON.stringify({ type: 'ping' }));
}, 30000);

Rate Limits

API requests are rate-limited to ensure fair usage:

Endpoint Category Limit Window
Authentication 10 requests 1 minute
Read operations 1000 requests 1 minute
Write operations 100 requests 1 minute
Block uploads 50 MB 1 minute
Search / AI 30 requests 1 minute

Rate limit headers are included in responses:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1704967200

SDKs and Libraries

Official SDKs are planned for:

In the meantime, use standard HTTP libraries with the REST API documented above.