- 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
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 categoryauthor_id: Filter by authorhas_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 pointlng: Longitude for center pointradius_km: Search radius in kilometers (default: 10)category: Filter by categorystatus: Filter by statusonly_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 offsetcategory: Filter by categoryis_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, 90dcategory: 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
GET /search
Search across the platform.
Query Parameters:
q: Search querytype: posts, users, groups, beaconslimit: 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 offsettype: Filter by notification typeunread_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 invalidFORBIDDEN: Insufficient permissionsNOT_FOUND: Resource not foundVALIDATION_ERROR: Invalid input dataRATE_LIMITED: Too many requestsINTERNAL_ERROR: Server errorSERVICE_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.