Files
geutebruck-api/specs/001-surveillance-api/contracts/openapi.yaml
Geutebruck API Developer dd2278b39a Complete Phase 0 and Phase 1 design documentation
- Add comprehensive research.md with SDK integration decisions
- Add complete data-model.md with 7 entities and relationships
- Add OpenAPI 3.0 specification (contracts/openapi.yaml)
- Add developer quickstart.md guide
- Add comprehensive tasks.md with 215 tasks organized by user story
- Update plan.md with complete technical context
- Add SDK_INTEGRATION_LESSONS.md capturing critical knowledge
- Add .gitignore for Python and C# projects
- Include GeViScopeConfigReader and GeViSoftConfigReader tools

Phase 1 Design Complete:
 Architecture: Python FastAPI + C# gRPC Bridge + GeViScope SDK
 10 user stories mapped to tasks (MVP = US1-4)
 Complete API contract with 17 endpoints
 Data model with User, Camera, Stream, Event, Recording, Analytics
 TDD approach enforced with 80+ test tasks

Ready for Phase 2: Implementation

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-09 07:39:55 +01:00

1397 lines
38 KiB
YAML

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: []