This document describes the FBC Uploader REST API endpoints. All endpoints return JSON responses unless otherwise specified. The API implements token-based authentication and TUS protocol for resumable file uploads.
Note: All API routes are prefixed with
/api. For deployment and environment configuration, see README.md.
- HTTP API Documentation
- Table of Contents
- Authentication
- Global Notes
- Endpoints
- GET /health
- POST /api/tokens/
- GET /api/tokens/
- GET /api/tokens/{token_value}
- PATCH /api/tokens/{token_value}
- DELETE /api/tokens/{token_value}
- GET /api/tokens/{token_value}/info
- GET /api/tokens/{token_value}/uploads
- GET /api/tokens/{download_token}/uploads/{upload_id}
- GET /api/tokens/{download_token}/uploads/{upload_id}/stream
- GET /api/tokens/{download_token}/uploads/{upload_id}/subtitles
- GET /api/tokens/{download_token}/uploads/{upload_id}/subtitles/{source_format}
- GET /api/tokens/{download_token}/uploads/{upload_id}/preview.mp4
- GET /api/tokens/{download_token}/uploads/{upload_id}/thumbnail
- GET /api/tokens/{download_token}/uploads/{upload_id}/download
- POST /api/tokens/{download_token}/uploads/{upload_id}/watch
- DELETE /api/watch/{room_id}
- WS /api/watch/{room_id}/ws
- POST /api/uploads/initiate
- OPTIONS /api/uploads/tus
- HEAD /api/uploads/{upload_id}/tus
- PATCH /api/uploads/{upload_id}/tus
- DELETE /api/uploads/{upload_id}/tus
- DELETE /api/uploads/{upload_id}/cancel
- POST /api/uploads/{upload_id}/complete
- GET /api/metadata/
- POST /api/metadata/extract
- POST /api/metadata/validate
- GET /api/notice/
- GET /api/admin/validate
- DELETE /api/admin/uploads/{upload_id}
- TUS Protocol
- Upload Flow
- Error Codes Reference
- Notes
Admin-only endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <admin_api_key>The admin API key is configured via FBC_ADMIN_API_KEY environment variable or auto-generated and stored in {config_path}/secret.key.
The following endpoints are always public:
GET /healthGET /api/tokens/{token}/infoPOST /api/uploads/initiate?token=...- All TUS protocol endpoints (
/api/uploads/{id}/tus) GET /api/metadata/POST /api/metadata/validateGET /api/notice/
When FBC_ALLOW_PUBLIC_DOWNLOADS=1, download endpoints also become public without requiring admin authentication.
-
Content-Type
- Requests with JSON body should include
Content-Type: application/json - TUS protocol PATCH requests require
Content-Type: application/offset+octet-stream - Responses typically include
Content-Type: application/json, unless returning a file
- Requests with JSON body should include
-
Status Codes
200 OK- Request successful201 Created- Resource created successfully204 No Content- Request successful, no content to return4xx- Client errors (bad request, unauthorized, forbidden, not found)5xx- Server errors
-
Error Responses When an error occurs, responses follow this structure:
{ "detail": "Error description" }or for validation errors:
{ "detail": { "field": "field_name", "message": "Error message" } } -
Datetime Format All datetime values are in UTC with ISO 8601 format including timezone info.
Health check endpoint to verify the service is running.
Authentication: None
Response (200):
{
"status": "ok"
}Create a new upload token.
Authentication: Required (Admin)
Request Body:
{
"max_uploads": 5,
"max_size_bytes": 104857600,
"expiry_datetime": "2025-12-24T00:00:00Z",
"allowed_mime": ["application/pdf", "video/*"]
}Fields:
max_uploads(integer, optional): Maximum number of uploads allowed (default: 1; min: 1)max_size_bytes(integer, required): Maximum file size in bytes (> 0)expiry_datetime(datetime, optional): Token expiration date (defaults to current time +FBC_DEFAULT_TOKEN_TTL_HOURS)allowed_mime(array of strings, optional): Allowed MIME types with wildcard support (e.g.,video/*). Empty = all types allowed
Response (201):
{
"token": "upload-token-string",
"download_token": "fbc_download-token-string",
"upload_url": "http://localhost:8000/api/uploads/initiate?token=upload-token-string",
"expires_at": "2025-12-24T00:00:00Z",
"max_uploads": 5,
"max_size_bytes": 104857600,
"allowed_mime": ["application/pdf", "video/*"]
}List all upload tokens.
Authentication: Required (Admin)
Query Parameters:
skip(integer, optional): Number of records to skip (default: 0)limit(integer, optional): Maximum records to return (default: 10)
Response (200):
{
"tokens": [
{
"token": "upload-token-string",
"download_token": "fbc_download-token-string",
"expires_at": "2025-12-24T00:00:00Z",
"uploads_used": 2,
"max_uploads": 5,
"max_size_bytes": 104857600,
"allowed_mime": ["application/pdf"],
"disabled": false,
"created_at": "2025-12-23T00:00:00Z",
"remaining_uploads": 3
}
],
"total": 1
}Get detailed information about a specific token.
Authentication: None
Path Parameters:
token_value(string): Upload token or download token
Response (200):
{
"token": "upload-token-string",
"download_token": "fbc_download-token-string",
"expires_at": "2025-12-24T00:00:00Z",
"uploads_used": 0,
"max_uploads": 1,
"max_size_bytes": 104857600,
"allowed_mime": ["application/pdf"],
"disabled": false,
"created_at": "2025-12-23T00:00:00Z",
"remaining_uploads": 1
}Error Responses:
404 Not Found- Token does not exist
Update an existing token.
Authentication: Required (Admin)
Path Parameters:
token_value(string): Upload token or download token
Request Body:
{
"max_uploads": 10,
"max_size_bytes": 209715200,
"expiry_datetime": "2025-12-31T23:59:59Z",
"allowed_mime": ["image/*", "video/*"],
"disabled": false
}Fields (all optional):
max_uploads(integer): Update maximum uploadsmax_size_bytes(integer): Update maximum file sizeexpiry_datetime(datetime): Update expiration dateallowed_mime(array): Update allowed MIME typesdisabled(boolean): Enable or disable the token
Response (200):
{
"token": "upload-token-string",
"download_token": "fbc_download-token-string",
"expires_at": "2025-12-31T23:59:59Z",
"uploads_used": 0,
"max_uploads": 10,
"max_size_bytes": 209715200,
"allowed_mime": ["image/*", "video/*"],
"disabled": false,
"created_at": "2025-12-23T00:00:00Z",
"remaining_uploads": 10
}Error Responses:
404 Not Found- Token does not exist
Delete a token and its associated uploads.
Authentication: Required (Admin)
Path Parameters:
token_value(string): Upload token or download token
Query Parameters:
delete_files(boolean, optional): Whether to delete physical files (default: false)
Response (204): No content
Error Responses:
404 Not Found- Token does not exist
Notes:
- Deletes the token and all associated upload records
- Physical files are deleted from storage if
delete_files=true - This operation cannot be undone
Get public token information including uploads.
Authentication: None
Path Parameters:
token_value(string): The upload token
Response (200):
{
"token": "upload-token-string",
"download_token": "fbc_download-token-string",
"remaining_uploads": 1,
"max_uploads": 1,
"max_size_bytes": 104857600,
"max_chunk_bytes": 94371840,
"allowed_mime": ["application/pdf"],
"expires_at": "2025-12-24T00:00:00Z",
"allow_public_downloads": false,
"uploads": [
{
"public_id": "rT72ZKGMPdldiEmA9eDI7kik",
"filename": "document.pdf",
"ext": "pdf",
"mimetype": "application/pdf",
"size_bytes": 1024000,
"meta_data": {"title": "My Document"},
"upload_length": 1024000,
"upload_offset": 1024000,
"recommended_chunk_bytes": 94371840,
"status": "completed",
"created_at": "2025-12-23T12:00:00Z",
"completed_at": "2025-12-23T12:01:00Z",
"stream_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/stream",
"thumbnail_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/thumbnail",
"download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik",
"upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus"
}
]
}Fields:
remaining_uploads: Number of uploads still availablemax_chunk_bytes: Maximum chunk size for TUS uploads (fromFBC_MAX_CHUNK_BYTES)allow_public_downloads: Whether public downloads are enableduploads: Array of upload recordsrecommended_chunk_bytes: Server-selected TUS chunk size to reuse for checksum-verified resume operationsstream_url: Inline media URL for browser playback when the upload is completedthumbnail_url: Preview image URL for embeds and thumbnail-first media UIs
Upload Status Values:
initiated- Upload created but no data uploaded yetin_progress- Upload in progresscompleted- Upload complete
Error Responses:
404 Not Found- Token does not exist
List all uploads for a specific token.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
token_value(string): Upload token or download token
Response (200):
[
{
"public_id": "rT72ZKGMPdldiEmA9eDI7kik",
"filename": "document.pdf",
"ext": "pdf",
"mimetype": "application/pdf",
"size_bytes": 1024000,
"meta_data": {"title": "My Document"},
"upload_length": 1024000,
"upload_offset": 1024000,
"recommended_chunk_bytes": 94371840,
"status": "completed",
"created_at": "2025-12-23T12:00:00Z",
"completed_at": "2025-12-23T12:01:00Z",
"stream_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/stream",
"thumbnail_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/thumbnail",
"download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik",
"upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus"
}
]Error Responses:
404 Not Found- Token does not exist
Get metadata information about a completed upload.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)
Response (200):
{
"public_id": "rT72ZKGMPdldiEmA9eDI7kik",
"filename": "document.pdf",
"ext": "pdf",
"mimetype": "application/pdf",
"size_bytes": 1024000,
"meta_data": {
"title": "My Document"
},
"upload_length": 1024000,
"upload_offset": 1024000,
"recommended_chunk_bytes": 94371840,
"status": "completed",
"created_at": "2025-01-01T12:00:00Z",
"completed_at": "2025-01-01T12:05:00Z",
"stream_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/stream",
"thumbnail_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/thumbnail",
"upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus",
"download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/download"
}Error Responses:
404 Not Found- Download token or upload not found409 Conflict- Upload not yet completed
HEAD is also supported.
Stream a completed file inline for browser playback.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)
Response (200): Returns the file with headers:
Content-Type: Original file MIME type orapplication/octet-streamContent-Disposition:inline; filename="original-filename.ext"Content-Length: File size in bytes
Error Responses:
404 Not Found- Download token or upload not found409 Conflict- Upload not yet completed
Notes:
- Use this endpoint for HTML
<video>and<audio>playback - Use
/downloadwhen you want attachment-style download behavior
List external subtitle tracks that match a completed upload.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)
Response (200):
{
"subtitles": [
{
"source_format": "vtt",
"delivery_format": "vtt",
"renderer": "native",
"url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/subtitles/vtt"
},
{
"source_format": "ass",
"delivery_format": "ass",
"renderer": "assjs",
"url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/subtitles/ass"
}
]
}Notes:
- This endpoint only returns tracks discovered under
FBC_SUBTITLE_PATH. - A subtitle filename containing the upload ID is preferred over name-based matches. No particular prefix or bracket format is required around the ID.
- Name-based discovery first uses the uploaded filename, then optionally tries
titleandbroadcast_datemetadata when those values are available. - Discovery results are cached per upload for
FBC_SUBTITLE_CACHE_TTL_SECONDS, including cases where no subtitles are found. - Results are ordered by renderer preference:
.vtt, then.srt, then.ass. .srtfiles are exposed here withsource_format: "srt",delivery_format: "vtt", andrenderer: "native".- Duplicate matches for the same stem and extension are treated as ambiguous and omitted.
Error Responses:
404 Not Found- Download token or upload not found409 Conflict- Upload not yet completed
HEAD is also supported.
Return the selected subtitle content for a completed upload.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)source_format(string): One ofvtt,srt, orass
Response (200):
vtt:Content-Type: text/vttsrt: Converted on the fly and returned asContent-Type: text/vttass: Returned asContent-Type: text/x-ssa
Error Responses:
404 Not Found- Download token, upload, or subtitle not found409 Conflict- Upload not yet completed
Notes:
- The share page uses
vttand convertedsrtas native browser subtitle tracks. .asssubtitles are intended for ASS.js-based browser rendering.- Subtitle discovery is cached per upload, so newly added or removed subtitle files may take up to
FBC_SUBTITLE_CACHE_TTL_SECONDSto appear.
HEAD is also supported.
Return a short MP4 preview clip for bot embeds when one has been generated.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)
Response (200): Returns the file with headers:
Content-Type:video/mp4Content-Disposition:inline; filename="..."
Error Responses:
404 Not Found- Download token, upload, or preview not found409 Conflict- Upload not yet completed
Notes:
- Bot embed metadata may use this endpoint instead of the full stream for large videos that meet the preview size threshold.
- Setting
FBC_EMBED_PREVIEW_MIN_SIZE_BYTES=0disables preview sidecars and keeps embed metadata on the original/streamURL. - If no preview sidecar exists, embed metadata falls back to the original
/streamURL.
HEAD is also supported.
Return a preview image for a completed upload.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)
Response (200): Returns an image with headers:
Content-Type:image/jpegContent-Disposition:inline; filename="..."
Error Responses:
404 Not Found- Download token or upload not found409 Conflict- Upload not yet completed
Notes:
- Video uploads return a generated sidecar thumbnail when available
- The same endpoint is used by social embeds and the thumbnail-first share page UI
HEAD is also supported.
Download a completed file.
Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)
Path Parameters:
download_token(string): The download tokenupload_id(string): The upload record public ID (random string)
Response (200): Returns the file with headers:
Content-Type: Original file MIME type orapplication/octet-streamContent-Disposition:attachment; filename="original-filename.ext"Content-Length: File size in bytes
Error Responses:
404 Not Found- Download token or upload not found409 Conflict- Upload not yet completed
Create a temporary Watch Party for a completed video or audio upload.
Authentication: Valid download token in the path; requires FBC_ALLOW_PUBLIC_DOWNLOADS=1
Path Parameters:
download_token(string): Download token for the shared fileupload_id(string): Public ID of the completed upload
Request: No request body.
Response (200):
HTTP/1.1 200 OK
Content-Type: application/json{
"room_id": "room-id-string",
"host_key": "creator-only-secret",
"invite_path": "/f/fbc_download-token?upload=upload-id-string&room=room-id-string"
}Fields:
room_id(string): Temporary room identifier used by the WebSocket URLhost_key(string): One-time creator key; keep it private and never include it in an invite URLinvite_path(string): Relative share-page path for guests
Error Responses:
403 Forbidden- Public downloads are disabled404 Not Found- Download token or upload not found409 Conflict- Upload is not completed415 Unsupported Media Type- Upload is not video or audio503 Service Unavailable- In-memory room capacity has been reached
Notes:
- The download token must be valid, enabled, unexpired, belong to the upload, and reference an existing file.
- Rooms are stored in the current server process and expire after inactivity.
- Connect to the returned room with WS /api/watch/{room_id}/ws.
Discard an unclaimed Watch Party room when no WebSocket participant has joined.
Authentication: Send the creator key in the X-Watch-Host-Key request header. The key is never accepted in the query string or path.
Response (204): The room was discarded.
Invalid, already-claimed, or occupied rooms return 404 Not Found without changing the room.
Connect to a Watch Party and synchronize playback for its upload.
Authentication: Send the download token and upload public ID in the first JSON join message. The creator also sends host_key.
Path Parameters:
room_id(string): Room ID returned by the Watch Party creation endpoint
Request:
No request body. Send the join JSON message immediately after the WebSocket connection is established.
Connection:
GET /api/watch/room-id-string/ws HTTP/1.1
Connection: Upgrade
Upgrade: websocketThe creator sends this message after connecting:
{
"type": "join",
"download_token": "fbc_download-token",
"upload_id": "upload-id-string",
"host_key": "creator-only-secret"
}Guests omit host_key:
{
"type": "join",
"download_token": "fbc_download-token",
"upload_id": "upload-id-string"
}Server Messages:
| Type | Fields | Meaning |
|---|---|---|
ready |
participant_id, role, participant_count, participant_version, version, sync_required, host host_key |
Join accepted; role is host or guest; a host receives its rotated key. A reconnecting host has sync_required: true and must apply the state at or above version before controlling playback |
state |
version, anchor_position, paused, playback_rate, server_time, play_at, participant_count, participant_version |
Current server playback state. play_at is a Unix timestamp in server seconds for a scheduled play, or null otherwise. |
participants |
participant_count, participant_version |
Participant count changed. Clients ignore messages with an older participant_version |
promotion |
participant_id, version, host_key |
The indicated guest became host and receives its rotated key |
host_status |
status, version |
waiting means the host disconnected and authoritative playback is paused; connected means the host recovered or a guest was promoted. Lifecycle version values are monotonic, so clients ignore older status events. Guests joining during reconnect grace receive the current waiting status |
synced |
version |
The server accepted the recovering or promoted host's synchronization acknowledgement |
pong |
client_time, server_time |
Response to a valid ping |
error |
message |
Request or party error |
Client Messages:
- Host
playcommands are scheduled by the server 0.5 seconds in the future. The returned state is authoritative; clients use their server-clock offset to begin atplay_at. Pause, seek, rate, and snapshot commands remain immediate. - Host playback commands use
typeplay,pause,seek,rate, orsnapshotand require a finitepositionin seconds. - A recovering or promoted host must first apply the authoritative state delivered after
readyorpromotion; clients must not send playback commands or snapshots until that synchronization is complete. A new host withsync_required: falsemay send its initial snapshot. - A recovering or promoted host acknowledges applied state with
{ "type": "synced", "version": <state version> }. The server rejects playback commands until that participant acknowledges the current authoritative version, and resendserrorplusstatefor a stale acknowledgement. rateandsnapshotrequireplayback_ratefrom0.25through4;snapshotalso requires booleanpaused.- Guests may send
pingwith numericclient_timeorreadyto request current state. Guests cannot control playback. - Example host command:
{ "type": "seek", "position": 120.5 } - Example ping:
{ "type": "ping", "client_time": 1735689600000 }
Authority and Limits:
- The first valid join with the creation
host_keyestablishes the host. Guests may join first without taking the host role. - If the host disconnects, the server anchors and pauses playback, broadcasts a
waitingstatus and state, and keeps itshost_keyvalid for a 15-second reconnect grace period. A reconnecting host receives and must apply that paused state before controlling playback. After grace expires, the longest-connected guest is promoted, receives a rotatedhost_key, and all participants receive the authoritative paused state. A late joiner receives the current projected state, including its paused state and playback rate. - A process allows at most 1000 rooms and at most 3 rooms per download token. A room allows at most 32 participants; before a host is established, one participant slot is reserved for the host. All messages are limited to 60 per two seconds per participant, and playback commands to 20 per two seconds per host. The room also expires when its download token expires.
- Messages are limited to 8192 bytes; joins must arrive within 10 seconds. Never-joined rooms are retained for 60 seconds, and rooms expire after 30 minutes of inactivity. Participants that send nothing for 90 seconds are removed. Cleanup runs approximately every second in this process.
Close Codes:
1000- Normal client disconnect1001- Server shutdown or room expiry1008- Invalid join, missing/invalid credential, unknown room, or other policy violation during connection
Error Responses:
- Invalid playback commands, guest control attempts, invalid positions/rates, malformed JSON, oversized messages, and rate-limit violations receive an
errormessage while the connection remains open. - Invalid joins and join timeouts receive an
errormessage and close with WebSocket code1008. - Playback positions must be between
0and86400seconds inclusive. The host reconnect grace period is approximately 15 seconds. An all-message rate-limit violation closes the socket; playback command errors are reported without closing it. - Disabling or deleting a token, or deleting an upload, invalidates matching Watch Party rooms and closes their sockets. Natural token expiry rejects new joins and cleanup closes existing connections.
Notes:
- Room creation and WebSocket access require
FBC_ALLOW_PUBLIC_DOWNLOADS=1; the associated upload must be completed playable media. - The
host_keyis only for the creator/ promoted host connection, is rotated after each successful host join or promotion, and must not be put in an invite URL. - Joining before the creator is allowed; that participant remains a guest until host promotion.
Initiate a new file upload.
Authentication: Required via query parameter
Query Parameters:
token(string, required): The upload token
Request Body:
{
"meta_data": {
"title": "My Document",
"category": "reports"
},
"filename": "document.pdf",
"filetype": "application/pdf",
"size_bytes": 1024000
}Fields:
meta_data(object, required): Metadata fields validated against{config_path}/metadata.jsonschemafilename(string, optional): Original filenamefiletype(string, optional): MIME typesize_bytes(integer, optional): Total file size in bytes (> 0)
Response (201):
{
"upload_id": "rT72ZKGMPdldiEmA9eDI7kik",
"upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus",
"download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik",
"meta_data": {
"title": "My Document",
"category": "reports"
},
"allowed_mime": ["application/pdf"],
"remaining_uploads": 0,
"recommended_chunk_bytes": 1024000
}recommended_chunk_bytes: Server-selected TUS chunk size for this upload. Clients should use this value when sending checksum-verified PATCH requests and when resuming the upload.
Error Responses:
404 Not Found- Token does not exist403 Forbidden- Token expired, disabled, or upload limit reached413 Content Too Large- File size exceeds token limit415 Unsupported Media Type- File type not allowed for this token422 Unprocessable Entity- Metadata validation error
Get TUS protocol capabilities.
Authentication: None
Response (204):
Headers:
Tus-Resumable: 1.0.0
Tus-Version: 1.0.0
Tus-Extension: creation,termination,checksum
Tus-Checksum-Algorithm: sha1,sha256Check upload status (TUS protocol).
Authentication: None
Path Parameters:
upload_id(string): The upload record public ID (random string)
Response (200):
Headers:
Upload-Offset: 512000
Upload-Length: 1024000
Tus-Resumable: 1.0.0Error Responses:
404 Not Found- Upload not found409 Conflict- Upload length unknown
Upload file chunk (TUS protocol).
Authentication: None
Path Parameters:
upload_id(string): The upload record public ID (random string)
Required Headers:
Upload-Offset(integer): Current upload offset (must match server state)Tus-Resumable(string): TUS protocol version (1.0.0)Content-Type: Must beapplication/offset+octet-streamContent-Length(integer, optional): Chunk size
Optional Headers:
Upload-Checksum(string): Per-chunk checksum in the form<algorithm> <base64-digest>
Request Body: Binary data (file chunk)
Max Chunk Size: Controlled by FBC_MAX_CHUNK_BYTES (default: 90MB)
Response (204):
Headers:
Upload-Offset: 1024000
Tus-Resumable: 1.0.0
Upload-Length: 1024000Error Responses:
404 Not Found- Upload not found409 Conflict- Upload length unknown or mismatched Upload-Offset460 Checksum Mismatch- Uploaded chunk checksum did not matchUpload-Checksum413 Content Too Large- Chunk too large or upload exceeds declared length415 Unsupported Media Type- Invalid Content-Type or actual file mimetype doesn't match allowed types
Notes:
- The upload is only finalized after
POST /api/uploads/{upload_id}/complete - When
Upload-Checksumis supplied, the chunk is verified before it is appended to the upload file - Server-side MIME type validation: Upon completion, the server detects the actual file type using libmagic and validates it against the token's
allowed_mimerestrictions. If validation fails, the upload is rejected and deleted (HTTP 415) - The detected MIME type replaces the client-provided value in the database
- The
uploads_usedcounter on the token is incremented upon initiation, not completion
Delete an upload and its associated file (TUS protocol).
Authentication: None
Path Parameters:
upload_id(string): The upload record public ID (random string)
Response (204): No content
Error Responses:
404 Not Found- Upload not found
Notes:
- Deletes both the database record and the physical file
- This operation cannot be undone
Cancel an in-progress upload and restore the token slot.
Authentication: Required via query parameter
Path Parameters:
upload_id(string): The upload record public ID (random string)
Query Parameters:
token(string, required): The upload token
Response (200):
{
"message": "Upload cancelled successfully",
"remaining_uploads": 5
}Error Responses:
404 Not Found- Upload or token not found409 Conflict- Upload already completed
Notes:
- Restores the upload slot to the token (increments
remaining_uploads) - Deletes the physical file if it exists
- Can only cancel uploads that are not completed
Mark an upload as complete.
Authentication: None
Path Parameters:
upload_id(string): The upload record public ID (random string)
Response (200):
{
"public_id": "rT72ZKGMPdldiEmA9eDI7kik",
"filename": "document.pdf",
"ext": "pdf",
"mimetype": "application/pdf",
"size_bytes": 1024000,
"meta_data": {"title": "My Document"},
"upload_length": 1024000,
"upload_offset": 1024000,
"status": "completed",
"created_at": "2025-12-23T12:00:00Z",
"completed_at": "2025-12-23T12:01:00Z",
"download_url": null,
"upload_url": null
}Error Responses:
403 Forbidden- Associated upload token expired or disabled404 Not Found- Upload or associated token not found409 Conflict- Upload has not received all declared bytes415 Unsupported Media Type- Detected file type is not allowed
Get the metadata schema configuration.
Authentication: None
Response (200):
{
"fields": [
{
"key": "title",
"label": "Title",
"type": "string",
"required": true,
"minLength": 3,
"maxLength": 100
},
{
"key": "category",
"label": "Category",
"type": "select",
"required": false,
"options": ["reports", "images", "videos"],
"allowCustom": false
}
]
}Field Schema:
Each field object can have:
key(string, required): Field identifierlabel(string, optional): Human-readable labeltype(string, required): Field type (string,text,number,integer,boolean,date,datetime,select,multiselect)required(boolean, optional): Whether field is mandatoryoptions(array, optional): Available options for select/multiselectallowCustom(boolean, optional): Allow custom values not in optionsminLength,maxLength(integer, optional): String length constraintsmin,max(number, optional): Numeric value constraintsregex(string, optional): Regular expression pattern for string validationdefault(any, optional): Default value used by the frontend when initializing the upload formextract_regex(string, optional): Python regular expression used by/api/metadata/extractto prefill metadata from a filename
Notes:
- Schema is loaded from
{config_path}/metadata.json extract_regexis interpreted with Pythonresyntax
Extract metadata values from a filename using the configured schema.
Authentication: None
Request Body:
{
"filename": "240101 Example Show [youtube-dQw4w9WgXcQ].mp4"
}Response (200):
{
"metadata": {
"broadcast_date": "2024-01-01",
"title": "Example Show",
"source": "youtube",
"source_id": "dQw4w9WgXcQ"
}
}Extraction Rules:
- Fields without
extract_regexare ignored - The regex is matched against the provided filename with case-insensitive search
- Capture group 1 is used when present; otherwise the full match is used
- For
datefields, named groupsyear,month, anddayare combined intoYYYY-MM-DD - Two-digit years are normalized to
20xx
Validate metadata payload against the schema.
Authentication: None
Request Body:
{
"metadata": {
"title": "My Document",
"category": "reports"
}
}or direct metadata object:
{
"title": "My Document",
"category": "reports"
}Response (200):
{
"metadata": {
"title": "My Document",
"category": "reports"
}
}Error Responses:
422 Unprocessable Entity- Validation error{ "detail": { "field": "title", "message": "Must be at least 3 characters" } }
Validation Rules:
- Type Coercion: Values are automatically converted to the expected type
- Required Fields: Missing required fields result in validation error
- String/Text: Length validated against
minLength/maxLength, pattern validated againstregex - Number/Integer: Value validated against
min/max - Boolean: Accepts various formats (
true,1,"yes","on"→true) - Date/DateTime: Parsed from ISO format string
- Select: Value must be in
options(unlessallowCustomis true) - Multiselect: Each value must be in
options(unlessallowCustomis true)
Get the system notice (if configured).
Authentication: None
Response (200):
{
"notice": "# Important Notice\n\nThe system will undergo maintenance..."
}Fields:
notice(string or null): Markdown content from{config_path}/notice.md, ornullif file doesn't exist
Notes:
- Notice is displayed to users in the frontend
- Supports full Markdown formatting
Validate the admin API key.
Authentication: Required (Admin)
Response (200):
{
"valid": true
}Error Responses:
401 Unauthorized- Invalid or missing API key
Delete an upload record and its file (Admin only).
Authentication: Required (Admin)
Path Parameters:
upload_id(string): The upload record public ID (random string)
Response (204): No content
Error Responses:
404 Not Found- Upload not found
Notes:
- Deletes both the database record and the physical file
- Does not restore the upload slot to the token
- This operation cannot be undone
The API implements the TUS resumable upload protocol v1.0.0.
Supported Extensions:
creation: Create new uploadstermination: Delete uploadschecksum: Verify uploaded chunks with SHA-1 or SHA-256 checksums
Key Features:
- Resumable: Upload can be paused and resumed from the same offset
- Chunked: Large files can be split into smaller chunks
- Verified: Each chunk is verified by checking the offset
Required Headers:
Tus-Resumable: 1.0.0- Protocol versionUpload-Offset: <bytes>- Current upload positionContent-Type: application/offset+octet-stream- Required for PATCH
Response Headers:
Upload-Offset: Updated upload position after chunkUpload-Length: Total file size (if known)Tus-Resumable: Protocol version echo
Typical upload flow:
-
Create Token (Admin)
POST /api/tokens Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "max_uploads": 5, "max_size_bytes": 104857600, "allowed_mime": ["application/pdf"] }
-
Get Token Info (Client)
GET /api/tokens/{token}
-
Initiate Upload (Client)
POST /api/uploads/initiate?token={token} Content-Type: application/json { "meta_data": {"title": "My Document"}, "filename": "document.pdf", "filetype": "application/pdf", "size_bytes": 1024000 }
-
Upload File Chunks (TUS Protocol)
# Check current offset HEAD /api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus # Upload chunk PATCH /api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus Upload-Offset: 0 Tus-Resumable: 1.0.0 Content-Type: application/offset+octet-stream [binary data]
-
Complete Upload
POST /api/uploads/rT72ZKGMPdldiEmA9eDI7kik/complete
-
Download File
GET /api/tokens/{download_token}/uploads/rT72ZKGMPdldiEmA9eDI7kik Authorization: Bearer YOUR_API_KEY
| Code | Meaning | Common Causes |
|---|---|---|
200 |
OK | Request successful |
201 |
Created | Resource created successfully |
204 |
No Content | Request successful, no content to return |
400 |
Bad Request | Invalid request format or parameters |
401 |
Unauthorized | Missing or invalid admin credentials |
403 |
Forbidden | Token expired, disabled, or limit reached |
404 |
Not Found | Resource not found |
409 |
Conflict | Upload not ready, offset mismatch, or state conflict |
413 |
Content Too Large | File or chunk exceeds limit |
415 |
Unsupported Media Type | Invalid Content-Type or file type not allowed |
422 |
Unprocessable Entity | Validation error in request body |
500 |
Internal Server Error | Server-side error |
- All datetime values are in UTC with ISO 8601 format including timezone info
- File paths are resolved and stored as absolute paths
- Upload tokens are 18-character URL-safe strings
- Download tokens are prefixed with
fbc_followed by 16-character URL-safe strings - Upload IDs (
public_id) are 18-character URL-safe random strings (not sequential integers for security) - Metadata is stored as JSON in the database (
meta_datacolumn) - Use TUS when an upload may need to resume after an interruption or retry a failed chunk
- Maximum chunk size is controlled by
FBC_MAX_CHUNK_BYTES(default: 90MB) - Media remux eligibility is capped by
FBC_MAX_REMUX_BYTES(default: 5GB) - Multimedia post-processing runs with up to
FBC_POSTPROCESSING_WORKERSconcurrent workers (default: 4)