B2B Media Library API
The B2B Media Library API lets authenticated client admins upload, organize, browse, and reuse shared media inside the GOFA B2B workspace.
These endpoints are designed for the GOFA B2B admin experience. They support reusable asset management so content editors can select existing media instead of repeatedly entering external URLs.
Image assets and streamed video assets now follow separate flows:
- Images continue to use the shared B2B media library stored by GOFA.
- Videos are uploaded directly to Cloudflare Stream and returned as HLS playback URLs for smoother playback in AI Move and AI Training.
This API is intended for authenticated B2B admin users. Requests must come from a valid admin session or include a valid Firebase user token with admin access for the current client.
Authentication
Use the authenticated admin user's Firebase token in the Authorization header:
Authorization: Bearer <firebase_id_token>
Requests without a valid admin identity return 401 or 403.
Folder Endpoints
GET /api/b2b/media/folders
Returns the available folders for the current client.
GET response
200 OK: Returns an orderedfoldersarray.
{
"folders": [
{
"folderId": "folder_abc123",
"name": "Campaign Assets",
"parentFolderId": null,
"path": ["Campaign Assets"],
"pathDisplay": "Campaign Assets",
"createdAt": "2026-04-22T10:30:00.000Z",
"updatedAt": "2026-04-22T10:30:00.000Z"
}
]
}
POST /api/b2b/media/folders
Creates a new folder.
{
"name": "Spring Campaign",
"parentFolderId": null
}
POST response
201 Created: Returns the createdfolderobject.400 Bad Request: Validation failed.
PATCH /api/b2b/media/folders/[folderId]
Renames a folder or moves it under a different parent folder.
{
"name": "Spring Campaign 2026",
"parentFolderId": null
}
PATCH response
200 OK: Returns the updatedfolderobject.400 Bad Request: Validation failed or the move is not allowed.404 Not Found: Folder not found.
DELETE /api/b2b/media/folders/[folderId]
Deletes an empty folder.
DELETE response
200 OK: Folder deleted.400 Bad Request: Folder is not empty or cannot be deleted.404 Not Found: Folder not found.
Folders are organizational metadata only. Moving or deleting a folder does not rewrite existing media URLs.
Asset Endpoints
GET /api/b2b/media/assets
Lists shared media-library assets for the current client.
Asset query parameters
folderId: Optional. Userootto list assets without a folder.query: Optional text search.mediaType: Optional.imageorvideo.sort: Optional. Supported values includeuploadedAt-desc,uploadedAt-asc,name-asc,name-desc,size-desc, andsize-asc.limit: Optional result cap.
GET /api/b2b/media/assets?folderId=root&mediaType=image&sort=uploadedAt-desc&limit=50
GET response for asset listing
200 OK: Returns anassetsarray.
{
"assets": [
{
"assetId": "asset_xyz789",
"folderId": null,
"displayName": "Group Exercise Banner",
"mediaType": "image",
"url": "https://...",
"thumbnailUrl": null,
"originalFileName": "group-exercise-banner.png",
"mimeType": "image/webp",
"fileSize": 184322,
"width": 1600,
"height": 900,
"durationMs": null,
"uploadedAt": "2026-04-22T10:35:00.000Z",
"updatedAt": "2026-04-22T10:35:00.000Z"
}
]
}
GET /api/b2b/media/assets/[assetId]
Returns a single asset.
GET response for a single asset
200 OK: Returns theassetobject.404 Not Found: Asset not found.
PATCH /api/b2b/media/assets/[assetId]
Updates editable asset fields such as the display name or assigned folder.
{
"displayName": "Warm Up Hero Banner",
"folderId": "folder_abc123"
}
PATCH response for a single asset
200 OK: Returns the updatedassetobject.400 Bad Request: Validation failed.404 Not Found: Asset not found.
DELETE /api/b2b/media/assets/[assetId]
Deletes an asset from the shared library.
DELETE response for a single asset
200 OK: Asset deleted.404 Not Found: Asset not found.
Bulk Move Endpoint
POST /api/b2b/media/assets/bulk-move
Moves multiple assets into the same folder.
{
"assetIds": ["asset_xyz789", "asset_xyz790"],
"folderId": "folder_abc123"
}
POST response for bulk move
200 OK: Returns the updatedassetsarray.400 Bad Request: Validation failed.
Upload Endpoint
POST /api/b2b/media/upload
Uploads a new image asset using multipart/form-data.
Form fields
file: Required image file.folderId: Optional folder destination. Userootor omit to upload into the root folder.
POST response for upload
201 Created: Returns the createdassetobject.400 Bad Request: File type, size, or payload validation failed.
POST /api/b2b/media/upload
Authorization: Bearer <firebase_id_token>
Content-Type: multipart/form-data
Direct video uploads are no longer accepted on this endpoint. Use the Cloudflare Video endpoints below to create streamed video assets instead.
Cloudflare Video Endpoints
GET /api/b2b/media/cloudflare/videos
Lists Cloudflare Stream videos scoped to the authenticated client.
Results are returned in descending order by the most recent known modification timestamp so B2B clients can group the latest videos first.
Cloudflare video query parameters
query: Optional text search against the stored display name, original file name, or video ID.ready: Optional boolean filter. Usetrueto show only videos that are ready to stream.limit: Optional result cap.
GET response for Cloudflare video listing
200 OK: Returns avideosarray.
{
"videos": [
{
"videoId": "f65014bc6ff5419ea86e7972a047ba22",
"clientId": "bupa",
"creator": "bupa",
"displayName": "Seated Knee Lift",
"originalFileName": "seated-knee-lift.mp4",
"readyToStream": true,
"status": {
"state": "ready",
"pctComplete": "100.000000"
},
"hlsUrl": "https://videodelivery.net/f65014bc6ff5419ea86e7972a047ba22/manifest/video.m3u8",
"previewUrl": "https://videodelivery.net/f65014bc6ff5419ea86e7972a047ba22/watch",
"thumbnailUrl": "https://videodelivery.net/f65014bc6ff5419ea86e7972a047ba22/thumbnails/thumbnail.jpg",
"fileSize": 10485760,
"durationMs": 18500,
"modifiedAt": "2026-04-25T08:15:00.000Z",
"uploadedAt": "2026-04-24T09:30:00.000Z"
}
]
}
modifiedAt is populated from the latest Cloudflare Stream metadata when available. Clients can use it to group videos by week or month while still falling back to uploadedAt if needed.
POST /api/b2b/media/cloudflare/videos/direct-upload
Creates a one-time Cloudflare Stream direct upload URL for the authenticated client.
POST request body
{
"fileName": "seated-knee-lift.mp4",
"fileSize": 10485760,
"maxDurationSeconds": 1800
}
POST response for direct upload session
201 Created: Returns a one-timeuploadUrlplus a placeholdervideorecord.400 Bad Request: Validation failed.
{
"uploadUrl": "https://upload.videodelivery.net/f65014bc6ff5419ea86e7972a047ba22",
"video": {
"videoId": "f65014bc6ff5419ea86e7972a047ba22",
"clientId": "bupa",
"creator": "bupa",
"displayName": "seated-knee-lift",
"originalFileName": "seated-knee-lift.mp4",
"readyToStream": false,
"status": {
"state": "pendingupload"
},
"hlsUrl": "https://videodelivery.net/f65014bc6ff5419ea86e7972a047ba22/manifest/video.m3u8",
"previewUrl": "https://videodelivery.net/f65014bc6ff5419ea86e7972a047ba22/watch",
"thumbnailUrl": "https://videodelivery.net/f65014bc6ff5419ea86e7972a047ba22/thumbnails/thumbnail.jpg",
"fileSize": 10485760
}
}
After receiving the one-time uploadUrl, the B2B frontend uploads the file directly to Cloudflare Stream using multipart/form-data. Once Cloudflare finishes processing the video, the hlsUrl can be used for smooth playback.
GET /api/b2b/media/cloudflare/videos/[videoId]
Returns the current state of a single Cloudflare video for the authenticated client.
The returned video object uses the same fields as the listing response, including modifiedAt when available.
GET response for a single Cloudflare video
200 OK: Returns thevideoobject.404 Not Found: Video not found for the current client.
DELETE /api/b2b/media/cloudflare/videos/[videoId]
Deletes a Cloudflare video owned by the authenticated client.
DELETE response for a single Cloudflare video
200 OK: Video deleted.404 Not Found: Video not found for the current client.
Typical B2B Usage Flow
- Upload shared images through
/api/b2b/media/upload. - Upload reusable streamed videos by creating a direct upload session through
/api/b2b/media/cloudflare/videos/direct-uploadand posting the file to the returned Cloudflare URL. - Browse or search reusable images with
/api/b2b/media/assetsand Cloudflare videos with/api/b2b/media/cloudflare/videos. - Organize image assets with
/api/b2b/media/foldersand the bulk move endpoint. - Store the selected image URL or Cloudflare HLS URL in content records that need a media reference.
If you only need to reference an existing shared asset in the B2B editor, prefer listing and selecting from the media library instead of uploading the same file again.