# API Specification — Accessible Video Processing Platform **Generated:** 2026-05-01 --- ## Quick Navigation - [Docs Hub](../README.md) - [Architecture](architecture.md) - [Database Schema](database_schema.md) - [Tech Stack](tech_stack.md) ## Agent Entry | Signal | Value | |--------|-------| | Purpose | Authoritative REST API contract for all backend routes | | Read When | You need endpoint paths, auth requirements, or request/response shapes | | Skip When | You need DB schema → database_schema.md; infrastructure → infrastructure.md | | Canonical | Yes | | Next Docs | [Architecture](architecture.md), [Database Schema](database_schema.md) | | Primary Sources | `backend/app/api/v1/routes_*.py` | --- ## Base URLs | Environment | URL | |-------------|-----| | Production | `https://optical-dev.oliver.solutions/video-accessibility-back` | | Local (Docker) | `http://localhost:8012` | | OpenAPI (Swagger) | `{base_url}/docs` | All routes are prefixed with `/api/v1/`. --- ## Authentication **Scheme:** Bearer token (access token in memory) + HttpOnly refresh cookie. | Header / Cookie | Description | |-----------------|-------------| | `Authorization: Bearer ` | Required for all protected endpoints | | `refresh_token` cookie (HttpOnly) | Used by `/auth/refresh` only | Roles: `client`, `linguist`, `reviewer`, `production`, `project_manager`, `admin`. ### Auth Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/auth/login` | None | Email/password login; returns access token + sets refresh cookie | | POST | `/auth/microsoft` | None | Microsoft OIDC token validation; returns access + refresh | | POST | `/auth/refresh` | Cookie | Exchange refresh cookie for new access token | | POST | `/auth/logout` | Bearer | Revoke refresh token, clear cookie | **Login request:** ```json { "email": "user@example.com", "password": "secret" } ``` **Login response:** ```json { "access_token": "eyJ...", "token_type": "bearer", "user": { "id": "...", "email": "...", "role": "admin" } } ``` --- ## Jobs ### Upload (resumable) | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/jobs/upload/init` | Bearer | Initialise resumable GCS upload; returns signed upload URL | | POST | `/jobs/upload/complete` | Bearer | Finalise upload; creates Job record and enqueues ingestion | **`/jobs/upload/init` request:** ```json { "filename": "video.mp4", "content_type": "video/mp4", "size_bytes": 104857600 } ``` **`/jobs/upload/complete` request:** ```json { "gcs_uri": "gs://accessible-video/jobs/{job_id}/source.mp4", "filename": "video.mp4", "source_language": "en", "requested_outputs": { "captions_vtt": true, "audio_description_vtt": true, "audio_description_mp3": true, "accessible_video_mp4": false, "languages": ["fr", "de"], "tts_preferences": { "provider": "gemini", "model": "flash", "default_voice": "Kore" } } } ``` ### Job CRUD & Listing | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/jobs` | Bearer | Create job directly (small upload, no resumable) | | GET | `/jobs` | Bearer | List jobs (paginated, filterable by status/org/client/project) | | GET | `/jobs/{job_id}` | Bearer | Get single job with full language output map | | PATCH | `/jobs/{job_id}` | Bearer | Update job metadata (title, notes, requested outputs) | | DELETE | `/jobs/{job_id}` | Bearer | Soft-delete job | | POST | `/jobs/{job_id}/clone` | Bearer | Clone a job (new upload, same config) | | POST | `/jobs/{job_id}/cancel` | Bearer | Cancel a running job | | POST | `/jobs/{job_id}/retry` | Bearer | Retry failed job from the failed step | | GET | `/jobs/{job_id}/validate` | Bearer | Validate all GCS assets exist for a completed job | **Job list query params:** `status`, `org_id`, `client_id`, `project_id`, `page`, `size`, `sort`. **Job list response:** ```json { "items": [ { "id": "...", "status": "pending_qc", "title": "...", "created_at": "..." } ], "total": 42, "page": 1, "size": 20 } ``` ### Job State Transitions (Actions) | Method | Path | Auth | Roles | Description | |--------|------|------|-------|-------------| | POST | `/jobs/{job_id}/actions/approve_source` | Bearer | reviewer, production, admin | Approve source language VTT | | POST | `/jobs/{job_id}/actions/approve_english` | Bearer | reviewer, production, admin | Approve English source specifically | | POST | `/jobs/{job_id}/actions/reject` | Bearer | reviewer, production, admin | Reject job, return to creator | | POST | `/jobs/{job_id}/actions/complete` | Bearer | project_manager, admin | Mark job fully complete | | POST | `/jobs/{job_id}/actions/reject_final` | Bearer | project_manager, admin | Reject at final review stage | | POST | `/jobs/{job_id}/actions/return_to_qc` | Bearer | project_manager, admin | Return from final review to QC | | POST | `/jobs/{job_id}/actions/blocked_on_source` | Bearer | production, admin | Flag source video issue | | POST | `/jobs/{job_id}/actions/promote_to_qc` | Bearer | production, admin | Manually advance to pending_qc | | POST | `/jobs/{job_id}/actions/retry_tts` | Bearer | production, admin | Retry TTS generation for a language | ### Bulk Job Actions | Method | Path | Auth | Description | |--------|------|------|-------------| | DELETE | `/jobs/bulk` | Bearer | Bulk delete jobs by IDs | | POST | `/jobs/bulk/approve` | Bearer | Bulk approve source for multiple jobs | | POST | `/jobs/bulk/return-to-qc` | Bearer | Bulk return jobs to QC | | POST | `/jobs/bulk/download` | Bearer | Generate signed download ZIP for multiple jobs | ### VTT Editing | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/jobs/{job_id}/vtt` | Bearer | Get VTT content for a language/kind (`?lang=en&kind=captions`) | | PATCH | `/jobs/{job_id}/vtt` | Bearer | Save edited VTT content (creates new version) | | POST | `/jobs/{job_id}/vtt/adjust-timing` | Bearer | Bulk shift cue timestamps | ### VTT Versions | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/jobs/{job_id}/vtt/versions` | Bearer | List VTT version history | | GET | `/jobs/{job_id}/vtt/versions/{version}` | Bearer | Get specific version content | | GET | `/jobs/{job_id}/vtt/versions/diff` | Bearer | Diff two versions | | POST | `/jobs/{job_id}/vtt/versions/restore` | Bearer | Restore an earlier version | ### Downloads & Accessible Video | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/jobs/{job_id}/downloads` | Bearer | Get signed GCS download URLs for all assets | | GET | `/jobs/{job_id}/accessible-video/{language}/edit-state` | Bearer | Get pause-point edit state | | PATCH | `/jobs/{job_id}/accessible-video/{language}/pause-points/{cue_index}` | Bearer | Adjust a pause point timestamp | --- ## Language QC | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/language-qc/jobs/{job_id}/language-qc` | Bearer | Get QC state map for all languages | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/assign` | Bearer | Assign linguist to language | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/reassign` | Bearer | Reassign linguist | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/assign-reviewer` | Bearer | Assign reviewer | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/reassign-reviewer` | Bearer | Reassign reviewer | | POST | `/language-qc/jobs/{job_id}/languages/bulk-assign` | Bearer | Bulk assign linguist/reviewer for multiple languages | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/start-work` | Bearer | Linguist starts working | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/submit` | Bearer | Linguist submits for review | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/open-review` | Bearer | Reviewer opens language for review | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/approve` | Bearer | Reviewer approves language | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/reject` | Bearer | Reviewer rejects language | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/reopen` | Bearer | Reopen after rejection | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/mark-cue-reviewed` | Bearer | Mark individual VTT cue reviewed | | GET | `/language-qc/jobs/{job_id}/languages/{lang}/comments` | Bearer | List reviewer comments | | POST | `/language-qc/jobs/{job_id}/languages/{lang}/comments` | Bearer | Add reviewer comment | | GET | `/language-qc/me/language-qc-queue` | Bearer | Get current user's QC queue | --- ## Files | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/files/signed-upload` | Bearer | Get a signed GCS upload URL for arbitrary file upload | --- ## TTS | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/tts/voices` | Bearer | List available voices per provider | | GET | `/tts/languages` | Bearer | List supported language codes | | GET | `/tts/options` | Bearer | Get provider/model options | | POST | `/tts/preview` | Bearer | Generate a short TTS audio preview for a voice | --- ## Briefs | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/briefs` | Bearer | List briefs (filtered by org/status) | | POST | `/briefs` | Bearer | Create a brief (draft) | | GET | `/briefs/{brief_id}` | Bearer | Get brief detail | | PATCH | `/briefs/{brief_id}` | Bearer | Update brief (draft only) | | POST | `/briefs/{brief_id}/submit` | Bearer | Submit brief for PM approval | | POST | `/briefs/{brief_id}/approve` | Bearer | PM approves brief | --- ## Glossaries | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/glossaries` | Bearer | List glossaries for current client | | POST | `/glossaries` | Bearer | Create glossary (XLSX upload) | | GET | `/glossaries/{glossary_id}` | Bearer | Get glossary detail | | GET | `/glossaries/{glossary_id}/terms` | Bearer | List terms (paginated) | | POST | `/glossaries/{glossary_id}/versions` | Bearer | Upload new version (XLSX) | | POST | `/glossaries/{glossary_id}/activate` | Bearer | Activate a glossary version | | POST | `/glossaries/{glossary_id}/versions/{version_id}/reembed` | Bearer | Re-trigger embedding for a version | | DELETE | `/glossaries/{glossary_id}` | Bearer | Archive/delete glossary | --- ## Organizations | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/organizations` | Bearer | List organisations | | POST | `/organizations` | Bearer (admin) | Create organisation | | GET | `/organizations/{org_id}` | Bearer | Get organisation | | PATCH | `/organizations/{org_id}` | Bearer (admin) | Update organisation | | GET | `/organizations/{org_id}/members` | Bearer | List members | | POST | `/organizations/{org_id}/members` | Bearer (admin) | Add member | | PATCH | `/organizations/{org_id}/members/{user_id}` | Bearer (admin) | Update member role | | DELETE | `/organizations/{org_id}/members/{user_id}` | Bearer (admin) | Remove member | | GET | `/organizations/me/memberships` | Bearer | Get current user's org memberships | --- ## Clients, Teams & Projects | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/clients` | Bearer | List clients | | POST | `/clients` | Bearer (admin) | Create client | | GET | `/clients/{client_id}` | Bearer | Get client | | PATCH | `/clients/{client_id}` | Bearer (admin) | Update client | | DELETE | `/clients/{client_id}` | Bearer (admin) | Delete client | | POST | `/clients/{client_id}/pm` | Bearer (admin) | Assign PM to client | | DELETE | `/clients/{client_id}/pm/{user_id}` | Bearer (admin) | Remove PM from client | | GET | `/clients/{client_id}/pm` | Bearer | List PMs for client | | GET | `/clients/{client_id}/teams` | Bearer | List teams | | POST | `/clients/{client_id}/teams` | Bearer (admin) | Create team | | PATCH | `/clients/{client_id}/teams/{team_id}` | Bearer (admin) | Update team | | DELETE | `/clients/{client_id}/teams/{team_id}` | Bearer (admin) | Delete team | | POST | `/clients/{client_id}/teams/{team_id}/members` | Bearer | Add team member | | DELETE | `/clients/{client_id}/teams/{team_id}/members/{user_id}` | Bearer | Remove team member | | GET | `/clients/all-projects` | Bearer | List all projects across clients | | GET | `/clients/{client_id}/projects` | Bearer | List client projects | | POST | `/clients/{client_id}/projects` | Bearer (admin) | Create project | | PATCH | `/clients/{client_id}/projects/{project_id}` | Bearer (admin) | Update project | | DELETE | `/clients/{client_id}/projects/{project_id}` | Bearer (admin) | Delete project | --- ## Share Tokens | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/share/jobs/{job_id}/share` | Bearer | Create share token for a job | | GET | `/share/jobs/{job_id}/share` | Bearer | List active share tokens | | DELETE | `/share/jobs/{job_id}/share/{token_id}` | Bearer | Revoke share token | | GET | `/share/public/share/{token}` | None | Public job preview (client portal) | --- ## Invitations | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/invitations/preview` | None | Preview invitation details from token | | POST | `/invitations/accept` | None | Accept invitation and create/link account | --- ## Review Notes | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/jobs/{job_id}/review-notes` | Bearer | List timestamped notes for a job asset | | POST | `/jobs/{job_id}/review-notes` | Bearer | Create note at a video timestamp | | GET | `/jobs/{job_id}/review-notes/{note_id}` | Bearer | Get single note | | PATCH | `/jobs/{job_id}/review-notes/{note_id}` | Bearer | Update note | | DELETE | `/jobs/{job_id}/review-notes/{note_id}` | Bearer | Delete note | --- ## Admin | Method | Path | Auth | Roles | Description | |--------|------|------|-------|-------------| | GET | `/admin/users` | Bearer | admin | List all users | | POST | `/admin/users` | Bearer | admin | Create user | | GET | `/admin/users/{user_id}` | Bearer | admin | Get user | | PATCH | `/admin/users/{user_id}` | Bearer | admin | Update user | | DELETE | `/admin/users/{user_id}` | Bearer | admin | Delete user | | POST | `/admin/users/{user_id}/reset-password` | Bearer | admin | Trigger password reset email | | POST | `/admin/users/{user_id}/password/reset` | Bearer | admin | Set new password directly | | GET | `/admin/stats` | Bearer | admin | Platform-wide stats | | GET | `/admin/jobs/stats` | Bearer | admin | Job pipeline stats | | GET | `/admin/health/detailed` | Bearer | admin | Detailed health check (DB, Redis, queues) | | POST | `/admin/maintenance/reprocess-job/{job_id}` | Bearer | admin | Force reprocess a stuck job | | GET | `/admin/audit-logs` | Bearer | admin | List audit log entries | | GET | `/admin/audit-logs/user/{user_id}` | Bearer | admin | Audit log for a specific user | | GET | `/admin/audit-logs/security` | Bearer | admin | Security-related audit events | | DELETE | `/admin/audit-logs/cleanup` | Bearer | admin | Purge old audit logs | --- ## Production / Admin Production | Method | Path | Auth | Roles | Description | |--------|------|------|-------|-------------| | GET | `/production/failures` | Bearer | production, admin | List failed jobs | | POST | `/production/bulk-retry` | Bearer | production, admin | Bulk retry failed jobs | | GET | `/production/queue-stats` | Bearer | production, admin | Celery queue depths and worker counts | | POST | `/production/jobs/{job_id}/upload-final-vtt` | Bearer | production, admin | Manually upload a corrected VTT file | --- ## WebSocket | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/ws/status` | Bearer | WebSocket endpoint for real-time job status updates | --- ## Standard Error Responses | Status | Meaning | |--------|---------| | 400 | Validation error — see `detail` field | | 401 | Missing or expired access token | | 403 | Insufficient role permissions | | 404 | Resource not found | | 409 | Conflict (e.g. duplicate email) | | 422 | Pydantic schema validation failure | | 500 | Internal server error | **Error body:** ```json { "detail": "Job not found" } ``` --- ## Maintenance **Last Updated:** 2026-05-01 **Update Triggers:** - New route file added (`routes_*.py`) - Existing endpoint path or method changes - New request/response fields in schemas - Auth requirements change for an endpoint **Verification:** - [ ] All `@router.*` decorators in `backend/app/api/v1/routes_*.py` reflected here - [ ] Auth column accurate for each endpoint - [ ] Base URL correct for production deployment