Files
linsa/docs/api.md

13 KiB

Linsa API Documentation

This document describes the Linsa API endpoints and how to connect to them.

Base URL

  • Local Development: http://localhost:5625
  • Production: https://linsa.io (or your deployed domain)

Authentication

Linsa uses better-auth with email OTP for authentication.

Authentication Flow

  1. Request OTP: Send email to receive a one-time password
  2. Verify OTP: Submit the code to authenticate
  3. Session Cookie: A better-auth.session_token cookie is set on successful authentication

All authenticated endpoints require the session cookie to be included in requests.

Auth Endpoints

All auth endpoints are handled by better-auth at /api/auth/*:

POST /api/auth/email-otp/send-verification-otp
POST /api/auth/email-otp/verify-otp
GET  /api/auth/session
POST /api/auth/sign-out

API Key Authentication

For programmatic access (CLI tools, browser extensions), you can use API keys instead of session cookies.

Create API Key

POST /api/api-keys
Authorization: (session cookie required)
Content-Type: application/json

{
  "name": "My CLI Tool"
}

Response:

{
  "key": "lk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "id": "uuid",
  "name": "My CLI Tool",
  "created_at": "2024-01-15T10:00:00Z"
}

Important: The plain API key is only returned once on creation. Store it securely.

List API Keys

GET /api/api-keys
Authorization: (session cookie required)

Response:

{
  "keys": [
    {
      "id": "uuid",
      "name": "My CLI Tool",
      "last_used_at": "2024-01-15T12:00:00Z",
      "created_at": "2024-01-15T10:00:00Z"
    }
  ]
}

Delete API Key

DELETE /api/api-keys?id=<key_id>
Authorization: (session cookie required)

Using API Keys

Pass the API key in the X-API-Key header or in the request body as api_key:

GET /api/bookmarks
X-API-Key: lk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

User Profile

Get Current User Profile

GET /api/profile
Authorization: (session cookie required)

Response:

{
  "id": "uuid",
  "name": "John Doe",
  "email": "john@example.com",
  "username": "johndoe",
  "image": "https://...",
  "bio": "Developer and streamer",
  "website": "https://johndoe.com",
  "stream": {
    "id": "uuid",
    "title": "John's Stream",
    "is_live": false,
    "hls_url": "https://...",
    "webrtc_url": "https://...",
    "playback": { ... },
    "stream_key": "abc123..."
  }
}

Update Profile

PUT /api/profile
Authorization: (session cookie required)
Content-Type: application/json

{
  "name": "John Doe",
  "username": "johndoe",
  "image": "https://...",
  "bio": "Developer and streamer",
  "website": "https://johndoe.com"
}

Username Requirements:

  • Minimum 3 characters
  • Only lowercase letters, numbers, hyphens, and underscores
  • Must be unique

Streams

Get Stream by Username (Public)

GET /api/streams/{username}

Response:

{
  "user": {
    "id": "uuid",
    "name": "John Doe",
    "username": "johndoe",
    "image": "https://...",
    "bio": "Developer and streamer",
    "website": "https://johndoe.com",
    "location": "San Francisco",
    "joinedAt": "2024-01-01T00:00:00Z"
  },
  "stream": {
    "id": "uuid",
    "title": "Coding Session",
    "description": "Building cool stuff",
    "is_live": true,
    "viewer_count": 42,
    "hls_url": "https://...",
    "webrtc_url": "https://...",
    "playback": {
      "type": "cloudflare",
      "hlsUrl": "https://...",
      "webrtcUrl": "https://..."
    },
    "thumbnail_url": "https://...",
    "started_at": "2024-01-15T14:00:00Z"
  }
}

Get Current User's Stream

GET /api/stream
Authorization: (session cookie required)

Update Stream

PUT /api/stream
Authorization: (session cookie required)
Content-Type: application/json

{
  "title": "New Stream Title",
  "description": "Updated description",
  "hls_url": "https://...",
  "webrtc_url": "https://...",
  "is_live": true
}

Stream Settings

GET /api/stream/settings
Authorization: (session cookie required)

Response:

{
  "id": "uuid",
  "title": "My Stream",
  "description": "Stream description",
  "cloudflare_live_input_uid": "...",
  "cloudflare_customer_code": "...",
  "hls_url": "https://...",
  "stream_key": "abc123..."
}
PUT /api/stream/settings
Authorization: (session cookie required)
Content-Type: application/json

{
  "title": "My Stream",
  "description": "Stream description",
  "cloudflare_live_input_uid": "...",
  "cloudflare_customer_code": "..."
}

Check HLS Stream Status

GET /api/streams/{username}/check-hls
GET /api/check-hls?url={hls_url}

Stream Replays

List Replays

GET /api/stream-replays
Authorization: (session cookie required)

Response:

{
  "replays": [
    {
      "id": "uuid",
      "stream_id": "uuid",
      "title": "Stream Replay",
      "description": "...",
      "status": "ready",
      "playback_url": "https://...",
      "thumbnail_url": "https://...",
      "started_at": "2024-01-15T14:00:00Z",
      "ended_at": "2024-01-15T16:00:00Z",
      "duration_seconds": 7200,
      "is_public": true
    }
  ]
}

Create Replay

POST /api/stream-replays
Authorization: (session cookie or X-Stream-Key header)
Content-Type: application/json

{
  "title": "My Replay",
  "description": "Description",
  "status": "processing",
  "playback_url": "https://...",
  "thumbnail_url": "https://...",
  "started_at": "2024-01-15T14:00:00Z",
  "ended_at": "2024-01-15T16:00:00Z",
  "is_public": true
}

Status values: recording, processing, ready, failed

Get/Update/Delete Replay

GET    /api/stream-replays/{replayId}
PUT    /api/stream-replays/{replayId}
DELETE /api/stream-replays/{replayId}

Get Public Replays for User

GET /api/streams/{username}/replays

Bookmarks

API key authentication is required for bookmarks.

Add Bookmark

POST /api/bookmarks
Content-Type: application/json

{
  "api_key": "lk_xxx...",
  "url": "https://example.com/article",
  "title": "Interesting Article",
  "description": "A great read",
  "tags": ["tech", "programming"]
}

List Bookmarks

GET /api/bookmarks
X-API-Key: lk_xxx...

Response:

{
  "bookmarks": [
    {
      "id": "uuid",
      "url": "https://example.com/article",
      "title": "Interesting Article",
      "description": "A great read",
      "tags": ["tech", "programming"],
      "created_at": "2024-01-15T10:00:00Z"
    }
  ]
}

Browser Sessions

Save and sync browser tab sessions.

List Sessions

GET /api/browser-sessions?page=1&limit=50
Authorization: (session cookie required)

Response:

{
  "sessions": [
    {
      "id": "uuid",
      "name": "Research Session",
      "browser": "safari",
      "tab_count": 15,
      "is_favorite": false,
      "captured_at": "2024-01-15T10:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 100,
    "totalPages": 2
  }
}

Save Session

POST /api/browser-sessions
Authorization: (session cookie required)
Content-Type: application/json

{
  "action": "save",
  "name": "Research Session",
  "browser": "safari",
  "tabs": [
    {
      "title": "Page Title",
      "url": "https://example.com",
      "favicon_url": "https://example.com/favicon.ico"
    }
  ],
  "captured_at": "2024-01-15T10:00:00Z"
}

Get Session with Tabs

POST /api/browser-sessions
Authorization: (session cookie required)
Content-Type: application/json

{
  "action": "get",
  "session_id": "uuid"
}

Update Session

POST /api/browser-sessions
Authorization: (session cookie required)
Content-Type: application/json

{
  "action": "update",
  "session_id": "uuid",
  "name": "Updated Name",
  "is_favorite": true
}

Delete Session

POST /api/browser-sessions
Authorization: (session cookie required)
Content-Type: application/json

{
  "action": "delete",
  "session_id": "uuid"
}

Search Tabs Across Sessions

POST /api/browser-sessions
Authorization: (session cookie required)
Content-Type: application/json

{
  "action": "searchTabs",
  "query": "github",
  "limit": 100
}

AI Chat

Send Message (Streaming)

POST /api/chat/ai
Authorization: (session cookie required)
Content-Type: application/json

{
  "threadId": 123,
  "messages": [
    { "role": "user", "content": "Hello!" }
  ],
  "model": "anthropic/claude-sonnet-4"
}

Response: Server-sent events stream with AI response chunks.

Chat Mutations

POST /api/chat/mutations
Authorization: (session cookie required)
Content-Type: application/json

{
  "action": "createThread" | "createMessage" | "deleteThread",
  ...
}

Guest Chat

POST /api/chat/guest
Content-Type: application/json

{
  "messages": [
    { "role": "user", "content": "Hello!" }
  ]
}

Canvas (Collaborative Drawing)

List Canvases

GET /api/canvas
Authorization: (session cookie or guest cookie)

Create Canvas

POST /api/canvas
Authorization: (session cookie or guest cookie)
Content-Type: application/json

{
  "name": "My Canvas"
}

Update Canvas

PATCH /api/canvas
Authorization: (session cookie or guest cookie)
Content-Type: application/json

{
  "canvasId": "uuid",
  "name": "Updated Name",
  "width": 1920,
  "height": 1080,
  "defaultModel": "flux",
  "defaultStyle": "anime"
}

Canvas Images

GET  /api/canvas/images?canvasId={id}
POST /api/canvas/images
GET  /api/canvas/images/{imageId}
POST /api/canvas/images/{imageId}/generate

Context Items (Knowledge Base)

List Context Items

GET /api/context-items
Authorization: (session cookie required)

Create/Update Context Item

POST /api/context-items
Authorization: (session cookie required)
Content-Type: application/json

{
  "url": "https://docs.example.com",
  "name": "API Docs"
}

Archives

List Archives

GET /api/archives
Authorization: (session cookie required)

Create Archive

POST /api/archives
Authorization: (session cookie required)
Content-Type: application/json

{
  "url": "https://example.com/page",
  "title": "Page Title"
}

Get/Delete Archive

GET    /api/archives/{archiveId}
DELETE /api/archives/{archiveId}

Electric SQL Sync

These endpoints proxy to Electric SQL for real-time data sync:

GET /api/users              # Sync users table
GET /api/chat-threads       # Sync chat threads
GET /api/chat-messages      # Sync chat messages
GET /api/usage-events       # Sync usage events

Billing (Flowglad)

When billing is enabled:

GET  /api/flowglad/*        # Flowglad webhook handling
POST /api/stripe/checkout   # Create checkout session
POST /api/stripe/portal     # Customer portal
POST /api/stripe/webhooks   # Stripe webhooks
GET  /api/stripe/billing    # Billing status

Creator Subscriptions

GET  /api/creator/{username}/access  # Check access to creator content
POST /api/creator/subscribe          # Subscribe to creator
GET  /api/creator/tiers              # List subscription tiers

Error Responses

All endpoints return errors in a consistent format:

{
  "error": "Error message describing what went wrong"
}

Common HTTP Status Codes:

Code Description
200 Success
201 Created
400 Bad Request - Invalid input
401 Unauthorized - Authentication required
403 Forbidden - No permission
404 Not Found
409 Conflict - Resource already exists
429 Too Many Requests - Rate limited
500 Internal Server Error

Rate Limiting

AI chat endpoints have usage limits based on subscription tier. When limits are exceeded:

{
  "error": "Usage limit exceeded",
  "reason": "monthly_limit",
  "remaining": 0,
  "limit": 100
}

CORS

The API supports CORS for browser-based requests. Credentials (cookies) are allowed from trusted origins configured in the application.


WebSocket / Real-time

For real-time features like chat and stream viewer counts, the application uses:

  1. Electric SQL - For syncing database changes to clients
  2. Server-Sent Events (SSE) - For AI chat streaming responses

Example: Complete Authentication Flow

# 1. Request OTP
curl -X POST http://localhost:5625/api/auth/email-otp/send-verification-otp \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com"}'

# 2. Verify OTP (check terminal for code in dev mode)
curl -X POST http://localhost:5625/api/auth/email-otp/verify-otp \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"email": "user@example.com", "otp": "123456"}'

# 3. Access authenticated endpoint
curl http://localhost:5625/api/profile \
  -b cookies.txt

# 4. Create API key for programmatic access
curl -X POST http://localhost:5625/api/api-keys \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"name": "My CLI Tool"}'

# 5. Use API key
curl http://localhost:5625/api/bookmarks \
  -H "X-API-Key: lk_xxxxxxxx..."

Development Notes

  • In development mode, OTP codes are logged to the terminal instead of being emailed
  • The dev server runs on port 5625
  • Database: PostgreSQL (Neon in production)
  • Authentication: better-auth with email OTP plugin