sojorn/sojorn_docs/reference/API_REFERENCE.md
Patrick Britton 56a9dd032f feat: Add enhanced video moderation with frame extraction and implement placeholder UI methods
- Add VideoProcessor service to PostHandler for frame-based video moderation
- Implement multi-frame extraction and Azure OpenAI Vision analysis for video content
- Enhance VideoStitchingService with filters, speed control, and text overlays
- Add image upload dialogs for group avatar and banner in GroupCreationModal
- Implement navigation placeholders for mentions, hashtags, and URLs in sojornRichText
2026-02-17 13:32:58 -06:00

22 KiB

Sojorn API Reference

🚀 Complete REST API Documentation

Version: 3.0
Status: PRODUCTION READY
Last Updated: February 17, 2026


📋 Overview

The Sojorn API provides comprehensive REST endpoints for all platform features including authentication, posts, groups, beacons, E2EE chat, and more. All endpoints follow RESTful conventions and return JSON responses.

🔐 Authentication

Base URL

Production: https://api.sojorn.app
Development: http://localhost:8080

Authentication Methods

  • JWT Bearer Token: Authorization: Bearer <token>
  • API Key: X-API-Key: <api-key> (for service-to-service)
  • Session Cookie: Automatic cookie-based authentication for web

Token Endpoints

POST /auth/register

Register a new user account.

Request:

{
  "email": "user@example.com",
  "password": "securePassword123",
  "handle": "username",
  "display_name": "Display Name"
}

Response:

{
  "success": true,
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "handle": "username",
    "display_name": "Display Name",
    "created_at": "2026-02-17T12:00:00Z"
  },
  "token": "jwt_token_here",
  "refresh_token": "refresh_token_here"
}

POST /auth/login

Authenticate user and return tokens.

Request:

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

Response:

{
  "success": true,
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "handle": "username",
    "display_name": "Display Name"
  },
  "token": "jwt_token_here",
  "refresh_token": "refresh_token_here"
}

POST /auth/refresh

Refresh JWT access token.

Request:

{
  "refresh_token": "refresh_token_here"
}

Response:

{
  "success": true,
  "token": "new_jwt_token_here",
  "expires_at": "2026-02-18T12:00:00Z"
}

👤 User Management

GET /users/me

Get current user profile.

Headers:

Authorization: Bearer <token>

Response:

{
  "success": true,
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "handle": "username",
    "display_name": "Display Name",
    "bio": "User bio",
    "avatar_url": "https://cdn.sojorn.app/avatars/uuid.jpg",
    "follower_count": 150,
    "following_count": 75,
    "post_count": 42,
    "created_at": "2026-02-17T12:00:00Z",
    "is_verified": true,
    "is_premium": false
  }
}

PUT /users/me

Update current user profile.

Request:

{
  "display_name": "New Display Name",
  "bio": "Updated bio",
  "avatar_url": "https://cdn.sojorn.app/avatars/new.jpg",
  "location": "San Francisco, CA",
  "website": "https://example.com"
}

Response:

{
  "success": true,
  "user": {
    "id": "uuid",
    "display_name": "New Display Name",
    "bio": "Updated bio",
    "avatar_url": "https://cdn.sojorn.app/avatars/new.jpg",
    "location": "San Francisco, CA",
    "website": "https://example.com"
  }
}

GET /users/{handle}

Get user profile by handle.

Response:

{
  "success": true,
  "user": {
    "id": "uuid",
    "handle": "username",
    "display_name": "Display Name",
    "bio": "User bio",
    "avatar_url": "https://cdn.sojorn.app/avatars/uuid.jpg",
    "follower_count": 150,
    "following_count": 75,
    "post_count": 42,
    "is_following": false,
    "is_blocked": false,
    "created_at": "2026-02-17T12:00:00Z"
  }
}

📝 Posts & Content

GET /posts

Get posts with filtering and pagination.

Query Parameters:

  • limit: Number of posts to return (default: 20, max: 100)
  • offset: Pagination offset (default: 0)
  • category: Filter by category
  • author_id: Filter by author
  • has_video: Filter by video content (true/false)
  • algorithm: Use algorithmic ranking (true/false)

Response:

{
  "success": true,
  "posts": [
    {
      "id": "uuid",
      "author_id": "uuid",
      "author_handle": "username",
      "author_avatar": "https://cdn.sojorn.app/avatars/uuid.jpg",
      "body": "Post content here",
      "image_url": "https://cdn.sojorn.app/posts/uuid.jpg",
      "video_url": "https://cdn.sojorn.app/videos/uuid.mp4",
      "thumbnail_url": "https://cdn.sojorn.app/thumbnails/uuid.jpg",
      "category": "general",
      "visibility": "public",
      "like_count": 25,
      "comment_count": 8,
      "repost_count": 3,
      "share_count": 2,
      "is_liked": false,
      "is_reposted": false,
      "is_saved": false,
      "created_at": "2026-02-17T12:00:00Z",
      "updated_at": "2026-02-17T12:00:00Z"
    }
  ],
  "pagination": {
    "total": 1000,
    "limit": 20,
    "offset": 0,
    "has_more": true
  }
}

POST /posts

Create a new post.

Request:

{
  "body": "Post content here",
  "category": "general",
  "visibility": "public",
  "image_url": "https://cdn.sojorn.app/posts/uuid.jpg",
  "video_url": "https://cdn.sojorn.app/videos/uuid.mp4",
  "thumbnail_url": "https://cdn.sojorn.app/thumbnails/uuid.jpg",
  "is_nsfw": false,
  "tags": ["tag1", "tag2"]
}

Response:

{
  "success": true,
  "post": {
    "id": "uuid",
    "body": "Post content here",
    "category": "general",
    "visibility": "public",
    "image_url": "https://cdn.sojorn.app/posts/uuid.jpg",
    "video_url": "https://cdn.sojorn.app/videos/uuid.mp4",
    "thumbnail_url": "https://cdn.sojorn.app/thumbnails/uuid.jpg",
    "created_at": "2026-02-17T12:00:00Z"
  }
}

PUT /posts/{id}

Update an existing post.

Request:

{
  "body": "Updated post content",
  "category": "general",
  "visibility": "public",
  "is_nsfw": false
}

Response:

{
  "success": true,
  "post": {
    "id": "uuid",
    "body": "Updated post content",
    "updated_at": "2026-02-17T12:30:00Z"
  }
}

DELETE /posts/{id}

Delete a post.

Response:

{
  "success": true,
  "message": "Post deleted successfully"
}

💬 Comments

GET /posts/{post_id}/comments

Get comments for a post.

Query Parameters:

  • limit: Number of comments (default: 50)
  • offset: Pagination offset

Response:

{
  "success": true,
  "comments": [
    {
      "id": "uuid",
      "post_id": "uuid",
      "author_id": "uuid",
      "author_handle": "username",
      "author_avatar": "https://cdn.sojorn.app/avatars/uuid.jpg",
      "body": "Comment content",
      "like_count": 5,
      "reply_count": 2,
      "is_liked": false,
      "created_at": "2026-02-17T12:00:00Z"
    }
  ],
  "pagination": {
    "total": 100,
    "limit": 50,
    "offset": 0,
    "has_more": true
  }
}

POST /posts/{post_id}/comments

Create a comment on a post.

Request:

{
  "body": "Comment content",
  "parent_id": null
}

Response:

{
  "success": true,
  "comment": {
    "id": "uuid",
    "body": "Comment content",
    "created_at": "2026-02-17T12:00:00Z"
  }
}

🔄 Reposts & Amplification

POST /posts/{id}/repost

Create a repost.

Request:

{
  "type": "standard",
  "comment": "Optional comment for quote repost"
}

Response:

{
  "success": true,
  "repost": {
    "id": "uuid",
    "original_post_id": "uuid",
    "author_id": "uuid",
    "type": "standard",
    "comment": null,
    "created_at": "2026-02-17T12:00:00Z"
  }
}

POST /posts/{id}/boost

Boost a post for amplification.

Request:

{
  "boost_type": "amplify",
  "boost_amount": 1
}

Response:

{
  "success": true,
  "message": "Post boosted successfully"
}

GET /posts/{id}/amplification

Get amplification analytics for a post.

Response:

{
  "success": true,
  "analytics": {
    "post_id": "uuid",
    "total_amplification": 150,
    "amplification_rate": 0.75,
    "repost_counts": {
      "standard": 10,
      "quote": 5,
      "boost": 3,
      "amplify": 2
    },
    "metrics": [
      {
        "total_reach": 1000,
        "engagement_count": 75,
        "engagement_rate": 0.075,
        "new_followers": 5,
        "shares": 20,
        "comments": 15,
        "likes": 40,
        "last_updated": "2026-02-17T12:00:00Z"
      }
    ]
  }
}

📍 Beacons (Local Safety)

GET /beacons

Get beacons with geospatial filtering.

Query Parameters:

  • lat: Latitude for center point
  • lng: Longitude for center point
  • radius_km: Search radius in kilometers (default: 10)
  • category: Filter by category
  • status: Filter by status
  • only_official: Filter for official sources only

Response:

{
  "success": true,
  "beacons": [
    {
      "id": "uuid",
      "title": "Community Event",
      "description": "Description of the beacon",
      "category": "event",
      "status": "active",
      "lat": 37.7749,
      "lng": -122.4194,
      "author_id": "uuid",
      "author_handle": "username",
      "is_verified": true,
      "is_official_source": true,
      "organization_name": "City Hall",
      "vouch_count": 25,
      "report_count": 0,
      "confidence_score": 0.95,
      "image_url": "https://cdn.sojorn.app/beacons/uuid.jpg",
      "action_items": [
        "Volunteer at the event",
        "Share with neighbors"
      ],
      "neighborhood": "Downtown",
      "created_at": "2026-02-17T12:00:00Z"
    }
  ]
}

POST /beacons

Create a new beacon.

Request:

{
  "title": "Community Event",
  "description": "Description of the beacon",
  "category": "event",
  "lat": 37.7749,
  "lng": -122.4194,
  "image_url": "https://cdn.sojorn.app/beacons/uuid.jpg",
  "action_items": [
    "Volunteer at the event",
    "Share with neighbors"
  ],
  "neighborhood": "Downtown"
}

Response:

{
  "success": true,
  "beacon": {
    "id": "uuid",
    "title": "Community Event",
    "created_at": "2026-02-17T12:00:00Z"
  }
}

POST /beacons/{id}/vouch

Vouch for a beacon (positive interaction).

Response:

{
  "success": true,
  "vouch_count": 26,
  "confidence_score": 0.96
}

POST /beacons/{id}/report

Report a beacon (negative interaction).

Request:

{
  "reason": "inaccurate_information",
  "comment": "This information is not accurate"
}

Response:

{
  "success": true,
  "report_count": 1,
  "confidence_score": 0.90
}

👥 Groups

GET /groups

Get groups with filtering.

Query Parameters:

  • limit: Number of groups (default: 20)
  • offset: Pagination offset
  • category: Filter by category
  • is_private: Filter by privacy setting

Response:

{
  "success": true,
  "groups": [
    {
      "id": "uuid",
      "name": "Group Name",
      "description": "Group description",
      "category": "general",
      "is_private": false,
      "member_count": 150,
      "post_count": 500,
      "avatar_url": "https://cdn.sojorn.app/groups/uuid.jpg",
      "banner_url": "https://cdn.sojorn.app/groups/banner/uuid.jpg",
      "created_by": "uuid",
      "created_at": "2026-02-17T12:00:00Z",
      "is_member": false,
      "is_admin": false
    }
  ],
  "pagination": {
    "total": 100,
    "limit": 20,
    "offset": 0,
    "has_more": true
  }
}

POST /groups

Create a new group.

Request:

{
  "name": "Group Name",
  "description": "Group description",
  "category": "general",
  "is_private": false,
  "avatar_url": "https://cdn.sojorn.app/groups/uuid.jpg",
  "banner_url": "https://cdn.sojorn.app/groups/banner/uuid.jpg"
}

Response:

{
  "success": true,
  "group": {
    "id": "uuid",
    "name": "Group Name",
    "created_at": "2026-02-17T12:00:00Z"
  }
}

🔐 E2EE Chat

POST /e2ee/register-device

Register a device for E2EE chat.

Request:

{
  "device_name": "iPhone 14",
  "device_type": "mobile",
  "public_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}

Response:

{
  "success": true,
  "device": {
    "id": "device_uuid",
    "name": "iPhone 14",
    "type": "mobile",
    "public_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
    "created_at": "2026-02-17T12:00:00Z"
  }
}

POST /e2ee/send-message

Send an encrypted message.

Request:

{
  "recipient_id": "uuid",
  "message": "encrypted_message_here",
  "is_encrypted": true
}

Response:

{
  "success": true,
  "message": {
    "id": "uuid",
    "sender_id": "uuid",
    "recipient_id": "uuid",
    "message": "encrypted_message_here",
    "is_encrypted": true,
    "created_at": "2026-02-17T12:00:00Z"
  }
}

GET /e2ee/messages/{user_id}

Get encrypted messages with a user.

Response:

{
  "success": true,
  "messages": [
    {
      "id": "uuid",
      "sender_id": "uuid",
      "recipient_id": "uuid",
      "message": "encrypted_message_here",
      "is_encrypted": true,
      "created_at": "2026-02-17T12:00:00Z"
    }
  ]
}

🎬 Video (Quips)

POST /videos/upload

Upload and process a video.

Request (multipart/form-data):

video: [video file]
thumbnail: [thumbnail file]
title: "Video Title"
description: "Video description"
duration: 30

Response:

{
  "success": true,
  "video": {
    "id": "uuid",
    "url": "https://cdn.sojorn.app/videos/uuid.mp4",
    "thumbnail_url": "https://cdn.sojorn.app/thumbnails/uuid.jpg",
    "title": "Video Title",
    "description": "Video description",
    "duration": 30,
    "created_at": "2026-02-17T12:00:00Z"
  }
}

POST /videos/process

Process video with effects.

Request:

{
  "video_id": "uuid",
  "filter": "vintage",
  "speed": 1.5,
  "text_overlays": [
    {
      "text": "Hello World",
      "x": 100,
      "y": 100,
      "start_time": 5,
      "end_time": 10
    }
  ],
  "audio_track": {
    "url": "https://cdn.sojorn.app/audio/uuid.mp3",
    "volume": 0.5,
    "fade_in": true,
    "fade_out": true
  }
}

Response:

{
  "success": true,
  "processed_video": {
    "url": "https://cdn.sojorn.app/videos/processed/uuid.mp4",
    "processing_time": 15.5
  }
}

🤖 AI Moderation

POST /moderation/analyze

Analyze content for moderation.

Request:

{
  "content": "Content to analyze",
  "type": "text",
  "image_url": "https://cdn.sojorn.app/images/uuid.jpg",
  "video_url": "https://cdn.sojorn.app/videos/uuid.mp4"
}

Response:

{
  "success": true,
  "analysis": {
    "is_safe": true,
    "score": 0.95,
    "categories": {
      "hate": 0.1,
      "greed": 0.05,
      "delusion": 0.02
    },
    "recommendation": "approve",
    "confidence": 0.98
  }
}

🚫 Blocking System

GET /users/me/blocked

Get blocked users list.

Response:

{
  "success": true,
  "blocked_users": [
    {
      "id": "uuid",
      "handle": "blocked_user",
      "display_name": "Blocked User",
      "blocked_at": "2026-02-17T12:00:00Z"
    }
  ],
  "total": 5
}

POST /users/{id}/block

Block a user.

Response:

{
  "success": true,
  "message": "User blocked successfully"
}

DELETE /users/{id}/block

Unblock a user.

Response:

{
  "success": true,
  "message": "User unblocked successfully"
}

POST /users/me/blocked/import

Import blocked users from other platforms.

Request:

{
  "platform": "twitter",
  "data": "username1,username2,username3"
}

Response:

{
  "success": true,
  "imported": 3,
  "skipped": 0,
  "failed": 0
}

📊 Analytics & Metrics

GET /analytics/feed

Get feed analytics.

Query Parameters:

  • date_range: 7d, 30d, 90d
  • category: Filter by category

Response:

{
  "success": true,
  "analytics": {
    "total_posts": 1000,
    "total_engagement": 5000,
    "engagement_rate": 0.05,
    "top_categories": [
      {"category": "general", "count": 400},
      {"category": "hobby", "count": 300}
    ],
    "daily_stats": [
      {
        "date": "2026-02-17",
        "posts": 50,
        "engagement": 250
      }
    ]
  }
}

Search across the platform.

Query Parameters:

  • q: Search query
  • type: posts, users, groups, beacons
  • limit: Number of results (default: 20)
  • offset: Pagination offset

Response:

{
  "success": true,
  "results": {
    "posts": [
      {
        "id": "uuid",
        "body": "Post containing search term",
        "author_handle": "username",
        "created_at": "2026-02-17T12:00:00Z"
      }
    ],
    "users": [
      {
        "id": "uuid",
        "handle": "username",
        "display_name": "Display Name",
        "bio": "Bio containing search term"
      }
    ],
    "groups": [
      {
        "id": "uuid",
        "name": "Group Name",
        "description": "Description containing search term"
      }
    ],
    "beacons": [
      {
        "id": "uuid",
        "title": "Beacon Title",
        "description": "Description containing search term"
      }
    ]
  }
}

📱 Notifications

GET /notifications

Get user notifications.

Query Parameters:

  • limit: Number of notifications (default: 50)
  • offset: Pagination offset
  • type: Filter by notification type
  • unread_only: Filter for unread only

Response:

{
  "success": true,
  "notifications": [
    {
      "id": "uuid",
      "type": "like",
      "title": "New Like",
      "body": "username liked your post",
      "data": {
        "post_id": "uuid",
        "user_id": "uuid"
      },
      "is_read": false,
      "created_at": "2026-02-17T12:00:00Z"
    }
  ],
  "unread_count": 5,
  "pagination": {
    "total": 100,
    "limit": 50,
    "offset": 0,
    "has_more": true
  }
}

POST /notifications/{id}/read

Mark notification as read.

Response:

{
  "success": true,
  "message": "Notification marked as read"
}

⚙️ Settings & Preferences

GET /users/me/settings

Get user settings.

Response:

{
  "success": true,
  "settings": {
    "notifications": {
      "push_enabled": true,
      "email_enabled": true,
      "likes": true,
      "comments": true,
      "follows": true,
      "mentions": true
    },
    "privacy": {
      "profile_visibility": "public",
      "show_online_status": true,
      "allow_tagging": true,
      "nsfw_blur_enabled": true
    },
    "feed": {
      "algorithm_enabled": true,
      "show_suggested_posts": true,
      "hide_sensitive_content": true
    }
  }
}

PUT /users/me/settings

Update user settings.

Request:

{
  "notifications": {
    "push_enabled": true,
    "email_enabled": false,
    "likes": true,
    "comments": false,
    "follows": true,
    "mentions": true
  },
  "privacy": {
    "profile_visibility": "public",
    "show_online_status": false,
    "allow_tagging": true,
    "nsfw_blur_enabled": true
  }
}

Response:

{
  "success": true,
  "settings": {
    "notifications": {
      "push_enabled": true,
      "email_enabled": false,
      "likes": true,
      "comments": false,
      "follows": true,
      "mentions": true
    },
    "privacy": {
      "profile_visibility": "public",
      "show_online_status": false,
      "allow_tagging": true,
      "nsfw_blur_enabled": true
    }
  }
}

🏥 Health & Monitoring

GET /health

System health check.

Response:

{
  "status": "healthy",
  "timestamp": "2026-02-17T12:00:00Z",
  "uptime": "72h30m15s",
  "version": "3.0.0",
  "environment": "production",
  "checks": {
    "database": {
      "status": "healthy",
      "message": "Database connection successful",
      "duration": "5ms"
    },
    "redis": {
      "status": "healthy",
      "message": "Redis connection successful",
      "duration": "2ms"
    },
    "storage": {
      "status": "healthy",
      "message": "Cloudflare R2 accessible",
      "duration": "150ms"
    }
  }
}

GET /metrics

Application metrics.

Response:

{
  "success": true,
  "metrics": {
    "requests": {
      "total": 1000000,
      "per_second": 50,
      "error_rate": 0.01
    },
    "database": {
      "connections": 25,
      "queries_per_second": 100,
      "avg_response_time": "25ms"
    },
    "memory": {
      "used_mb": 512,
      "available_mb": 1536,
      "usage_percent": 25
    },
    "cpu": {
      "usage_percent": 15,
      "load_average": [0.5, 0.6, 0.7]
    }
  }
}

📚 Error Responses

All endpoints return consistent error responses:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": {
      "field": "email",
      "issue": "Invalid email format"
    }
  }
}

Common Error Codes

  • UNAUTHORIZED: Authentication required or invalid
  • FORBIDDEN: Insufficient permissions
  • NOT_FOUND: Resource not found
  • VALIDATION_ERROR: Invalid input data
  • RATE_LIMITED: Too many requests
  • INTERNAL_ERROR: Server error
  • SERVICE_UNAVAILABLE: Service temporarily unavailable

📝 Rate Limiting

API endpoints are rate limited to prevent abuse:

  • Authentication: 5 requests per minute
  • Content Creation: 10 requests per minute
  • Search: 30 requests per minute
  • General: 100 requests per minute

Rate limit headers are included in responses:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1645123200

🔑 API Keys

For service-to-service integration, generate API keys in the admin panel:

# Example API key usage
curl -H "X-API-Key: your-api-key-here" \
     -H "Content-Type: application/json" \
     https://api.sojorn.app/posts

📞 Support

Documentation

  • API Reference: Complete endpoint documentation
  • SDKs: Official SDKs for popular languages
  • Examples: Code examples and tutorials
  • Changelog: API version history and changes

Support Channels

  • Discord: API development discussion
  • GitHub: Issue tracking and feature requests
  • Email: api-support@sojorn.app
  • Status: api.sojorn.app/status

🚀 The Sojorn API provides comprehensive, production-ready endpoints for all platform features with proper authentication, rate limiting, and error handling.