# API Specification — Accessible Video Processing Platform **Base URL (production):** `https://ai-sandbox.oliver.solutions/video-accessibility-back` **Base URL (local):** `http://localhost:8003` **OpenAPI docs:** `{base_url}/docs` (Swagger UI) All endpoints require `Authorization: Bearer ` except `/auth/login`, `/auth/refresh`, `/auth/microsoft/*`, and `/health`. --- ## Authentication | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/api/v1/auth/login` | None | Email/password login; returns access token + sets refresh cookie | | POST | `/api/v1/auth/refresh` | Cookie | Exchange refresh cookie for new access token | | POST | `/api/v1/auth/logout` | Bearer | Revoke refresh token, clear cookie | | POST | `/api/v1/auth/microsoft/callback` | None | Microsoft SSO callback; validates OIDC token | | GET | `/api/v1/auth/microsoft/login` | None | Redirect to Microsoft login | | POST | `/api/v1/auth/change-password` | Bearer | Change own password | **Login response fields:** | Field | Type | Notes | |-------|------|-------| | access_token | string | JWT, 15-minute TTL | | token_type | string | Always "bearer" | | user | object | User profile (id, email, role, org_id) | --- ## Jobs | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/jobs` | ALL | List jobs (role-filtered: client sees own, reviewer/admin see all) | | POST | `/api/v1/jobs` | CLIENT, ADMIN | Create job with MP4 upload | | GET | `/api/v1/jobs/{id}` | ALL | Job detail with current status + outputs | | DELETE | `/api/v1/jobs/{id}` | ADMIN | Delete job and GCS files | | GET | `/api/v1/jobs/{id}/downloads` | ALL | Signed download URLs for deliverables (24h expiry) | | POST | `/api/v1/jobs/{id}/actions/approve` | REVIEWER, ADMIN | Approve job at current QC stage | | POST | `/api/v1/jobs/{id}/actions/reject` | REVIEWER, ADMIN | Reject job with reason | | POST | `/api/v1/jobs/{id}/actions/feedback` | REVIEWER, ADMIN | Send QC feedback without rejection | | POST | `/api/v1/jobs/{id}/actions/retry` | ADMIN | Retry failed task (TTS_FAILED, RENDER_FAILED) | **Job object key fields:** | Field | Type | Notes | |-------|------|-------| | _id | string | MongoDB ObjectId | | status | string | JobStatus enum — see architecture.md | | org_id | string | Organisation that owns the job | | source_language | string | BCP-47 language code | | requested_outputs | array | Output language codes requested | | outputs | object | Per-language GCS paths | | language_qc | object | Per-language QC state | | created_at | datetime | ISO 8601 | | updated_at | datetime | ISO 8601 | | error | string | Last error message if failed | --- ## VTT Management | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/jobs/{id}/vtt/{lang}` | REVIEWER, ADMIN | Get VTT content for language | | PATCH | `/api/v1/jobs/{id}/vtt/{lang}` | REVIEWER, LINGUIST, ADMIN | Update VTT content (auto-snapshots before save) | | POST | `/api/v1/vtt/adjust-timing` | REVIEWER, ADMIN | Bulk shift all cue timings | --- ## VTT Version Control | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/jobs/{id}/vtt-versions/{lang}` | REVIEWER, ADMIN | List version history | | GET | `/api/v1/jobs/{id}/vtt-versions/{lang}/{version_id}` | REVIEWER, ADMIN | Get specific version content | | POST | `/api/v1/jobs/{id}/vtt-versions/{lang}/{version_id}/restore` | REVIEWER, ADMIN | Restore a previous version (creates new snapshot) | | GET | `/api/v1/jobs/{id}/vtt-versions/{lang}/diff` | REVIEWER, ADMIN | Diff two versions (`?from=v1_id&to=v2_id`) | --- ## Language QC | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/jobs/{id}/language-qc` | REVIEWER, PM, ADMIN | Get per-language QC status for all languages | | POST | `/api/v1/jobs/{id}/language-qc/{lang}/assign` | PM, ADMIN | Assign linguist to language | | POST | `/api/v1/jobs/{id}/language-qc/{lang}/approve` | LINGUIST (assigned), PM, ADMIN | Approve language | | POST | `/api/v1/jobs/{id}/language-qc/{lang}/reject` | LINGUIST (assigned), PM, ADMIN | Reject language with reason | | POST | `/api/v1/jobs/{id}/language-qc/{lang}/feedback` | LINGUIST (assigned), PM, ADMIN | Send feedback without rejection | --- ## Glossaries | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/glossaries` | ALL | List glossaries for current org | | POST | `/api/v1/glossaries` | ADMIN | Create glossary | | GET | `/api/v1/glossaries/{id}` | ALL | Get glossary with terms | | PUT | `/api/v1/glossaries/{id}` | ADMIN | Update glossary metadata | | DELETE | `/api/v1/glossaries/{id}` | ADMIN | Delete glossary | | POST | `/api/v1/glossaries/{id}/terms` | ADMIN | Add term | | DELETE | `/api/v1/glossaries/{id}/terms/{term_id}` | ADMIN | Delete term | --- ## Files | Method | Path | Roles | Description | |--------|------|-------|-------------| | POST | `/api/v1/files/upload-url` | CLIENT, ADMIN | Get signed GCS upload URL | | GET | `/api/v1/files/{job_id}/{path}` | ALL | Get signed download URL | --- ## Users and Organisations | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/users/me` | ALL | Current user profile | | GET | `/api/v1/organizations` | ADMIN | List organisations | | POST | `/api/v1/organizations` | ADMIN | Create organisation | | GET | `/api/v1/organizations/{id}/members` | PM, ADMIN | List org members | | POST | `/api/v1/organizations/{id}/invite` | PM, ADMIN | Invite member | | DELETE | `/api/v1/organizations/{id}/members/{user_id}` | PM, ADMIN | Remove member | --- ## Admin | Method | Path | Roles | Description | |--------|------|-------|-------------| | GET | `/api/v1/admin/users` | ADMIN | List all users | | PATCH | `/api/v1/admin/users/{id}` | ADMIN | Update user role or status | | GET | `/api/v1/admin/audit-log` | ADMIN, PM | Query audit log | --- ## WebSocket | Path | Auth | Description | |------|------|-------------| | `WS /api/v1/ws/jobs/{id}` | Query param `token=` | Real-time job status updates | | `WS /api/v1/ws/org/{org_id}` | Query param `token=` | Org-scoped event stream | **Message format:** | Field | Type | Notes | |-------|------|-------| | type | string | `job_status_update`, `notification`, `ping` | | job_id | string | Job ObjectId | | status | string | New JobStatus value | | updated_at | datetime | ISO 8601 | --- ## Health | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/health` | None | Returns `{"status":"healthy","version":"1.0.0"}` | | GET | `/metrics` | None (internal) | Prometheus metrics | --- ## Error Response Format All errors return: | Field | Type | Notes | |-------|------|-------| | detail | string | Human-readable error message (never internal exception text) | Common status codes: | Code | Meaning | |------|---------| | 400 | Bad request / validation error | | 401 | Unauthenticated or invalid token | | 403 | Forbidden — insufficient role | | 404 | Resource not found | | 422 | Pydantic validation error | | 429 | Rate limit exceeded | | 500 | Internal server error (details logged, not returned) | --- ## Maintenance **Update triggers:** New endpoint added, request/response schema changed, auth flow change. **Verification:** All endpoints listed here exist in `backend/app/api/v1/routes_*.py`. OpenAPI schema at `/docs` matches this table.