openapi: 3.0.3 info: title: Geutebruck Video Surveillance API description: | Complete RESTful API for Geutebruck GeViScope/GeViSoft video surveillance system control. **Features:** - JWT-based authentication with refresh tokens - Live video streaming from surveillance cameras - PTZ (Pan-Tilt-Zoom) camera control - Real-time event notifications via WebSocket - Video recording management - Video analytics configuration (VMD, NPR, OBTRACK) - Multi-camera management - System health monitoring **Authentication:** All endpoints except `/health` and `/docs` require Bearer token authentication. Obtain tokens via `/api/v1/auth/login` endpoint. **Rate Limiting:** - Authentication endpoints: 5 requests/minute per IP - All other endpoints: 500 requests/minute per user **Support:** - Documentation: https://docs.example.com - GitHub: https://github.com/example/geutebruck-api version: 1.0.0 contact: name: API Support email: api-support@example.com license: name: MIT url: https://opensource.org/licenses/MIT servers: - url: https://api.example.com description: Production server - url: https://staging-api.example.com description: Staging server - url: http://localhost:8000 description: Development server tags: - name: Authentication description: User authentication and session management - name: Cameras description: Camera management and live streaming - name: Events description: Real-time event subscriptions and history - name: Recordings description: Video recording management - name: Analytics description: Video analytics configuration - name: System description: System health and status paths: # ============================================================================ # AUTHENTICATION ENDPOINTS # ============================================================================ /api/v1/auth/login: post: tags: [Authentication] summary: Authenticate user and obtain JWT tokens description: | Validates user credentials and returns access + refresh JWT tokens. Access tokens expire in 1 hour, refresh tokens in 7 days. operationId: login requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' examples: operator_login: summary: Operator login value: username: operator1 password: SecurePass123! responses: '200': description: Authentication successful content: application/json: schema: $ref: '#/components/schemas/TokenPair' examples: success: value: access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... token_type: bearer expires_in: 3600 '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/TooManyRequests' /api/v1/auth/refresh: post: tags: [Authentication] summary: Refresh access token description: Obtain a new access token using a valid refresh token operationId: refreshToken requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RefreshTokenRequest' responses: '200': description: New access token issued content: application/json: schema: $ref: '#/components/schemas/AccessToken' '401': $ref: '#/components/responses/Unauthorized' /api/v1/auth/logout: post: tags: [Authentication] summary: Logout and invalidate tokens description: Invalidates current session and revokes all tokens operationId: logout security: - BearerAuth: [] responses: '204': description: Logout successful '401': $ref: '#/components/responses/Unauthorized' # ============================================================================ # CAMERA ENDPOINTS # ============================================================================ /api/v1/cameras: get: tags: [Cameras] summary: List all cameras description: | Returns all cameras the authenticated user has permission to view. Results include camera status, capabilities, and current recording state. operationId: listCameras security: - BearerAuth: [] parameters: - name: status in: query description: Filter by camera status schema: $ref: '#/components/schemas/CameraStatus' - name: location in: query description: Filter by location schema: type: string - name: has_ptz in: query description: Filter cameras with PTZ capability schema: type: boolean - name: limit in: query description: Maximum number of results schema: type: integer default: 50 minimum: 1 maximum: 1000 - name: offset in: query description: Pagination offset schema: type: integer default: 0 minimum: 0 responses: '200': description: List of cameras content: application/json: schema: type: object properties: cameras: type: array items: $ref: '#/components/schemas/Camera' total: type: integer description: Total number of cameras (before pagination) limit: type: integer offset: type: integer '401': $ref: '#/components/responses/Unauthorized' /api/v1/cameras/{camera_id}: get: tags: [Cameras] summary: Get camera details description: Returns detailed information about a specific camera operationId: getCamera security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' responses: '200': description: Camera details content: application/json: schema: $ref: '#/components/schemas/Camera' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /api/v1/cameras/{camera_id}/stream: post: tags: [Cameras] summary: Request live video stream description: | Returns an authenticated URL for accessing the live video stream. The URL includes a time-limited JWT token and expires in 1 hour. Clients connect directly to the GeViScope stream URL (no proxy). operationId: requestStream security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/StreamRequest' examples: default: summary: Default stream (H.264, max resolution) value: format: h264 quality: 90 low_bandwidth: summary: Low bandwidth stream value: format: mjpeg resolution: "640x480" fps: 15 quality: 60 responses: '200': description: Stream URL created content: application/json: schema: $ref: '#/components/schemas/StreamResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': description: Camera offline or unavailable content: application/json: schema: $ref: '#/components/schemas/Error' /api/v1/cameras/{camera_id}/ptz: post: tags: [Cameras] summary: Send PTZ control command description: | Controls pan, tilt, and zoom operations on PTZ-capable cameras. Commands execute with <500ms latency from request to camera movement. operationId: controlPTZ security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PTZCommand' examples: pan_left: summary: Pan camera left value: action: pan_left speed: 50 goto_preset: summary: Move to preset position value: action: goto_preset preset_id: 3 responses: '200': description: PTZ command accepted content: application/json: schema: type: object properties: message: type: string example: PTZ command executed camera_id: type: integer action: type: string '400': description: Invalid command or camera lacks PTZ content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /api/v1/cameras/{camera_id}/presets: get: tags: [Cameras] summary: List PTZ presets description: Returns all saved PTZ preset positions for this camera operationId: listPresets security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' responses: '200': description: List of presets content: application/json: schema: type: array items: $ref: '#/components/schemas/PTZPreset' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' post: tags: [Cameras] summary: Save PTZ preset description: Saves current camera position as a named preset operationId: savePreset security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' requestBody: required: true content: application/json: schema: type: object required: - name properties: name: type: string minLength: 1 maxLength: 50 example: "Main Entrance View" responses: '201': description: Preset created content: application/json: schema: $ref: '#/components/schemas/PTZPreset' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' # ============================================================================ # EVENT ENDPOINTS # ============================================================================ /api/v1/events/stream: get: tags: [Events] summary: WebSocket event stream description: | **WebSocket endpoint** for real-time event notifications. **Connection Flow:** 1. Client upgrades HTTP to WebSocket connection 2. Client sends subscription message with filters 3. Server sends matching events as they occur 4. Client sends heartbeat (ping) every 30s 5. Server responds with pong **Message Format:** ```json // Subscribe { "action": "subscribe", "filters": { "event_types": ["motion_detected", "alarm_triggered"], "camera_ids": [1, 2, 3], "severity": "warning" } } // Event notification { "subscription_id": "uuid", "event": { ...event object... }, "sequence_number": 42 } // Heartbeat {"action": "ping"} {"action": "pong"} ``` **Reconnection:** - Client implements exponential backoff - Server buffers critical events for 5 minutes - Reconnected clients receive missed critical events operationId: streamEvents security: - BearerAuth: [] parameters: - name: Connection in: header required: true schema: type: string enum: [Upgrade] - name: Upgrade in: header required: true schema: type: string enum: [websocket] responses: '101': description: Switching Protocols to WebSocket '401': $ref: '#/components/responses/Unauthorized' '426': description: Upgrade Required content: application/json: schema: $ref: '#/components/schemas/Error' /api/v1/events: get: tags: [Events] summary: Query event history description: Retrieve historical events with filtering and pagination operationId: queryEvents security: - BearerAuth: [] parameters: - name: event_type in: query schema: $ref: '#/components/schemas/EventType' - name: camera_id in: query schema: type: integer - name: start_time in: query description: Filter events after this time (ISO 8601) schema: type: string format: date-time - name: end_time in: query description: Filter events before this time (ISO 8601) schema: type: string format: date-time - name: severity in: query schema: $ref: '#/components/schemas/EventSeverity' - name: acknowledged in: query description: Filter by acknowledgment status schema: type: boolean - name: limit in: query schema: type: integer default: 50 minimum: 1 maximum: 1000 - name: offset in: query schema: type: integer default: 0 minimum: 0 responses: '200': description: Event list content: application/json: schema: type: object properties: events: type: array items: $ref: '#/components/schemas/Event' total: type: integer limit: type: integer offset: type: integer '401': $ref: '#/components/responses/Unauthorized' /api/v1/events/{event_id}: get: tags: [Events] summary: Get event details description: Retrieve detailed information about a specific event operationId: getEvent security: - BearerAuth: [] parameters: - name: event_id in: path required: true schema: type: string format: uuid responses: '200': description: Event details content: application/json: schema: $ref: '#/components/schemas/Event' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' patch: tags: [Events] summary: Acknowledge event description: Mark an event as acknowledged by current user operationId: acknowledgeEvent security: - BearerAuth: [] parameters: - name: event_id in: path required: true schema: type: string format: uuid responses: '200': description: Event acknowledged content: application/json: schema: $ref: '#/components/schemas/Event' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' # ============================================================================ # RECORDING ENDPOINTS # ============================================================================ /api/v1/recordings: get: tags: [Recordings] summary: Query recordings description: Search for recorded video segments by time range and camera operationId: queryRecordings security: - BearerAuth: [] parameters: - name: camera_id in: query schema: type: integer - name: start_time in: query required: true schema: type: string format: date-time - name: end_time in: query required: true schema: type: string format: date-time - name: trigger in: query schema: $ref: '#/components/schemas/RecordingTrigger' - name: limit in: query schema: type: integer default: 50 - name: offset in: query schema: type: integer default: 0 responses: '200': description: Recording list content: application/json: schema: type: object properties: recordings: type: array items: $ref: '#/components/schemas/Recording' total: type: integer '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /api/v1/recordings/{recording_id}: get: tags: [Recordings] summary: Get recording details operationId: getRecording security: - BearerAuth: [] parameters: - name: recording_id in: path required: true schema: type: string format: uuid responses: '200': description: Recording details content: application/json: schema: $ref: '#/components/schemas/Recording' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/v1/recordings/{recording_id}/export: post: tags: [Recordings] summary: Export recording description: | Request video export for a recording segment. Returns job ID for tracking export progress. Completed exports available for download via provided URL. operationId: exportRecording security: - BearerAuth: [] parameters: - name: recording_id in: path required: true schema: type: string format: uuid requestBody: required: false content: application/json: schema: type: object properties: format: type: string enum: [mp4, avi] default: mp4 include_metadata: type: boolean default: true responses: '202': description: Export job created content: application/json: schema: type: object properties: job_id: type: string format: uuid status: type: string enum: [pending, processing] estimated_completion: type: string format: date-time '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/v1/recordings/capacity: get: tags: [Recordings] summary: Get recording capacity description: Returns storage capacity metrics for the ring buffer operationId: getRecordingCapacity security: - BearerAuth: [] responses: '200': description: Capacity metrics content: application/json: schema: type: object properties: total_capacity_gb: type: number format: float used_capacity_gb: type: number format: float free_capacity_gb: type: number format: float percent_used: type: number format: float recording_depth_hours: type: number format: float oldest_recording: type: string format: date-time warnings: type: array items: type: string '401': $ref: '#/components/responses/Unauthorized' # ============================================================================ # ANALYTICS ENDPOINTS # ============================================================================ /api/v1/analytics/{camera_id}: get: tags: [Analytics] summary: Get analytics configuration description: Returns current analytics configuration for a camera operationId: getAnalyticsConfig security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' responses: '200': description: Analytics configurations content: application/json: schema: type: array items: $ref: '#/components/schemas/AnalyticsConfig' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' put: tags: [Analytics] summary: Update analytics configuration description: Configure video analytics for a camera operationId: updateAnalyticsConfig security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/CameraId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AnalyticsConfig' responses: '200': description: Configuration updated content: application/json: schema: $ref: '#/components/schemas/AnalyticsConfig' '400': description: Invalid configuration or unsupported analytics type content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' # ============================================================================ # SYSTEM ENDPOINTS # ============================================================================ /api/v1/health: get: tags: [System] summary: Health check description: | Returns API health status. No authentication required. Used by load balancers and monitoring systems. operationId: healthCheck responses: '200': description: System healthy content: application/json: schema: type: object properties: status: type: string enum: [healthy, degraded, unhealthy] checks: type: object properties: api: type: string sdk_bridge: type: string redis: type: string geviserver: type: string timestamp: type: string format: date-time '503': description: System unhealthy content: application/json: schema: type: object properties: status: type: string enum: [unhealthy] checks: type: object timestamp: type: string /api/v1/status: get: tags: [System] summary: System status description: Detailed system status and metrics (requires authentication) operationId: getStatus security: - BearerAuth: [] responses: '200': description: Detailed status content: application/json: schema: type: object properties: uptime_seconds: type: integer active_streams: type: integer active_websocket_connections: type: integer cameras_online: type: integer cameras_total: type: integer sdk_version: type: string api_version: type: string '401': $ref: '#/components/responses/Unauthorized' # ============================================================================== # COMPONENTS # ============================================================================== components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT token obtained from /auth/login endpoint parameters: CameraId: name: camera_id in: path required: true description: Camera channel ID schema: type: integer minimum: 1 responses: BadRequest: description: Bad request - invalid parameters content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized - missing or invalid authentication content: application/json: schema: $ref: '#/components/schemas/Error' example: error_code: UNAUTHORIZED message: Missing or invalid authentication token details: null timestamp: "2025-12-08T14:30:00Z" Forbidden: description: Forbidden - insufficient permissions content: application/json: schema: $ref: '#/components/schemas/Error' example: error_code: FORBIDDEN message: Insufficient permissions for this resource details: null timestamp: "2025-12-08T14:30:00Z" NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' example: error_code: NOT_FOUND message: Resource not found details: null timestamp: "2025-12-08T14:30:00Z" TooManyRequests: description: Too many requests - rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error' example: error_code: RATE_LIMIT_EXCEEDED message: Too many requests, please try again later details: retry_after_seconds: 60 timestamp: "2025-12-08T14:30:00Z" headers: Retry-After: description: Seconds until rate limit resets schema: type: integer schemas: # Authentication Schemas LoginRequest: type: object required: - username - password properties: username: type: string minLength: 3 maxLength: 50 example: operator1 password: type: string format: password minLength: 8 example: SecurePass123! TokenPair: type: object properties: access_token: type: string description: JWT access token (1 hour expiration) refresh_token: type: string description: JWT refresh token (7 days expiration) token_type: type: string enum: [bearer] expires_in: type: integer description: Seconds until access token expires example: 3600 RefreshTokenRequest: type: object required: - refresh_token properties: refresh_token: type: string AccessToken: type: object properties: access_token: type: string token_type: type: string enum: [bearer] expires_in: type: integer # Camera Schemas Camera: type: object properties: id: type: integer description: Camera channel ID example: 5 global_id: type: string format: uuid description: GeViScope global identifier name: type: string example: "Entrance Camera" description: type: string nullable: true example: "Main entrance monitoring" location: type: string nullable: true example: "Building A - Main Entrance" status: $ref: '#/components/schemas/CameraStatus' capabilities: $ref: '#/components/schemas/CameraCapabilities' recording_status: type: object properties: is_recording: type: boolean mode: $ref: '#/components/schemas/RecordingTrigger' start_time: type: string format: date-time created_at: type: string format: date-time updated_at: type: string format: date-time CameraStatus: type: string enum: [online, offline, error, maintenance] CameraCapabilities: type: object properties: has_ptz: type: boolean has_video_sensor: type: boolean has_contrast_detection: type: boolean has_sync_detection: type: boolean supported_analytics: type: array items: type: string enum: [vmd, npr, obtrack, gtect, cpa] supported_resolutions: type: array items: type: string example: "1920x1080" supported_formats: type: array items: type: string enum: [h264, mjpeg] # Stream Schemas StreamRequest: type: object properties: format: type: string enum: [h264, mjpeg] default: h264 resolution: type: string nullable: true example: "1920x1080" fps: type: integer minimum: 1 maximum: 60 nullable: true quality: type: integer minimum: 1 maximum: 100 default: 90 StreamResponse: type: object properties: stream_id: type: string format: uuid camera_id: type: integer stream_url: type: string format: uri description: Token-authenticated stream URL example: "http://localhost:7703/stream?channel=5&token=eyJhbGc..." format: type: string resolution: type: string fps: type: integer expires_at: type: string format: date-time # PTZ Schemas PTZCommand: type: object required: - action properties: action: type: string enum: [pan_left, pan_right, tilt_up, tilt_down, zoom_in, zoom_out, stop, goto_preset, save_preset] speed: type: integer minimum: 1 maximum: 100 default: 50 nullable: true preset_id: type: integer minimum: 1 maximum: 255 nullable: true PTZPreset: type: object properties: id: type: integer minimum: 1 maximum: 255 camera_id: type: integer name: type: string minLength: 1 maxLength: 50 pan: type: integer minimum: -180 maximum: 180 tilt: type: integer minimum: -90 maximum: 90 zoom: type: integer minimum: 0 maximum: 100 created_at: type: string format: date-time created_by: type: string format: uuid updated_at: type: string format: date-time # Event Schemas Event: type: object properties: id: type: string format: uuid event_type: $ref: '#/components/schemas/EventType' camera_id: type: integer nullable: true timestamp: type: string format: date-time severity: $ref: '#/components/schemas/EventSeverity' data: type: object description: Type-specific event data foreign_key: type: string nullable: true acknowledged: type: boolean acknowledged_by: type: string format: uuid nullable: true acknowledged_at: type: string format: date-time nullable: true EventType: type: string enum: - motion_detected - object_tracked - license_plate - perimeter_breach - camera_tamper - camera_online - camera_offline - recording_started - recording_stopped - storage_warning - alarm_triggered - alarm_cleared EventSeverity: type: string enum: [info, warning, error, critical] # Recording Schemas Recording: type: object properties: id: type: string format: uuid camera_id: type: integer start_time: type: string format: date-time end_time: type: string format: date-time nullable: true duration_seconds: type: integer nullable: true file_size_bytes: type: integer nullable: true trigger: $ref: '#/components/schemas/RecordingTrigger' status: type: string enum: [recording, completed, failed, exporting, exported] export_url: type: string format: uri nullable: true metadata: type: object properties: event_id: type: string format: uuid nullable: true pre_alarm_seconds: type: integer post_alarm_seconds: type: integer tags: type: array items: type: string notes: type: string nullable: true created_at: type: string format: date-time RecordingTrigger: type: string enum: [scheduled, event, manual, continuous] # Analytics Schemas AnalyticsConfig: type: object properties: camera_id: type: integer analytics_type: type: string enum: [vmd, npr, obtrack, gtect, cpa] enabled: type: boolean config: type: object description: Type-specific configuration updated_at: type: string format: date-time updated_by: type: string format: uuid # Error Schema Error: type: object properties: error_code: type: string description: Machine-readable error code message: type: string description: Human-readable error message details: type: object nullable: true description: Additional error details timestamp: type: string format: date-time security: - BearerAuth: []