API Documentation

Integrate OmniView into your event app. Submit fan-filmed videos via the API and get a synchronized multi-camera viewer automatically.

Base URL: https://holossystems.com/api/v1
Contents
Authentication Submit a Video (B2B Intake) Upload a Video File Events Videos Sync Jobs Errors & Rate Limits

Authentication

API requests are authenticated using an API key tied to a specific event. Include the key in the X-API-Key header.

You receive your API key when you create an event via the API or in the dashboard.

curl -H "X-API-Key: your-api-key" \
     https://holossystems.com/api/v1/events

Some read-only endpoints (manifest, video list, job status) do not require authentication.

Submit a Video (B2B Intake)

The primary integration endpoint. Submit a video URL from your event app, optionally with GPS coordinates from the user's phone. The video is automatically synchronized against the reference.

POST /api/v1/intake
Submit a fan video URL for synchronization
X-API-Key required

Request Body (JSON)

ParameterTypeDescription
video_urlstring requiredFull URL to the video (YouTube, TikTok, Instagram, SoundCloud)
platformstring optionalyoutube, tiktok, instagram, soundcloud. Default: youtube
latitudefloat optionalGPS latitude of the recording location (-90 to 90)
longitudefloat optionalGPS longitude of the recording location (-180 to 180)
titlestring optionalDisplay title for the video (max 300 chars)
user_labelstring optionalLocation label from the user, e.g. "Front row left" (max 200 chars)

Example Request

curl -X POST https://holossystems.com/api/v1/intake \
  -H "X-API-Key: 29c3d488-98fa-4ee9-a179-593754d7d0bf" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "platform": "youtube",
    "latitude": 52.3676,
    "longitude": 4.9041,
    "title": "Main stage opening",
    "user_label": "Front row center"
  }'

Response

{
  "job_id": "a1b2c3d4-...",
  "status": "pending",
  "video_id": "dQw4w9WgXcQ",
  "message": "Video submitted for synchronization"
}

The first video submitted to an event automatically becomes the reference video. All subsequent videos are synchronized against it. Duplicate submissions return status: "duplicate" instead of an error.

Upload a Video File

Upload a video file directly instead of submitting a URL. GPS coordinates and compass heading are automatically extracted from the video metadata when available. Use multipart/form-data.

POST /api/v1/events/{event_id}/upload
Upload a video file for synchronization
api_key form field required

Form Fields

ParameterTypeDescription
filefile requiredVideo file (mp4, mov, webm, avi, mkv, 3gp)
api_keystring requiredEvent API key
titlestring optionalDisplay title
latitudefloat optionalGPS latitude (overrides video metadata)
longitudefloat optionalGPS longitude (overrides video metadata)
compass_headingfloat optionalCompass heading in degrees (0-360)
device_timestampstring optionalISO 8601 timestamp from the recording device
location_namestring optionalLocation label, e.g. "Main Stage"

Example Request

curl -X POST https://holossystems.com/api/v1/events/{event_id}/upload \
  -F "file=@concert.mov" \
  -F "api_key=29c3d488-98fa-4ee9-a179-593754d7d0bf" \
  -F "title=Main stage opening"

Response

{
  "status": "ok",
  "video_id": "abc123",
  "job_id": "def456",
  "stage": "Main Stage",
  "session_date": "2026-04-27"
}

Events

POST /api/v1/events
Create a new event

Request Body (JSON)

{
  "name": "Kingsday 2026",
  "date": "2026-04-27",
  "location": {
    "name": "Museumplein",
    "city": "Amsterdam",
    "country": "NL",
    "lat": 52.3580,
    "lon": 4.8828
  }
}

Response

{
  "id": "9a869d24-...",
  "name": "Kingsday 2026",
  "date": "2026-04-27",
  "api_key": "29c3d488-...",
  "status": "active",
  "viewer_url": "/view/9a869d24-...",
  "videos": []
}

The api_key is only returned on creation. Store it — you'll need it for all subsequent requests.

GET /api/v1/events/{event_id}
Get event details including all videos
X-API-Key required
GET /api/v1/events/{event_id}/manifest
Get the sync manifest with timing data for all videos

Videos

GET /api/v1/events/{event_id}/videos
List all videos for an event with sync status
POST /api/v1/events/{event_id}/videos
Submit a video URL for synchronization (alternative to /intake)

Request Body (JSON)

ParameterTypeDescription
source_urlstring requiredFull URL to the video
platformstring optionalyoutube, tiktok, instagram, soundcloud
latfloat optionalLatitude
lonfloat optionalLongitude
location_namestring optionalLocation label
titlestring optionalDisplay title
DELETE /api/v1/events/{event_id}/videos/{video_id}
Remove a video from the event

Sync Jobs

When you submit a video, you get a job_id back. Poll this endpoint to check if synchronization is complete.

GET /api/v1/jobs/{job_id}
Get sync job status

Response

{
  "id": "a1b2c3d4-...",
  "status": "done",
  "result": {
    "offset_ms": -2340.5,
    "confidence": 0.92,
    "method": "audio_fingerprint"
  }
}

Job Statuses

StatusMeaning
pendingQueued, waiting for processing
doneSuccessfully synchronized
failedSync failed — check result.error

Errors & Rate Limits

HTTP Status Codes

CodeMeaning
200Success
201Created
400Invalid request (bad URL, unsupported platform)
401Missing or invalid API key
403Event inactive or tier limit reached
404Event or video not found
409Duplicate video
429Rate limit exceeded

Rate Limits

API intake requests are limited to 30 requests per minute per IP address. If you exceed this limit, you'll receive a 429 response. Wait and retry.

Error Response Format

{
  "detail": "Invalid API key"
}