Skip to main content

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.
note

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 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 ordered folders array.
Sample response
{
"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.

Sample request
{
"name": "Spring Campaign",
"parentFolderId": null
}

POST response

  • 201 Created: Returns the created folder object.
  • 400 Bad Request: Validation failed.

PATCH /api/b2b/media/folders/[folderId]

Renames a folder or moves it under a different parent folder.

Sample request
{
"name": "Spring Campaign 2026",
"parentFolderId": null
}

PATCH response

  • 200 OK: Returns the updated folder object.
  • 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.
warning

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. Use root to list assets without a folder.
  • query: Optional text search.
  • mediaType: Optional. image or video.
  • sort: Optional. Supported values include uploadedAt-desc, uploadedAt-asc, name-asc, name-desc, size-desc, and size-asc.
  • limit: Optional result cap.
Sample request
GET /api/b2b/media/assets?folderId=root&mediaType=image&sort=uploadedAt-desc&limit=50

GET response for asset listing

  • 200 OK: Returns an assets array.
Sample response
{
"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 the asset object.
  • 404 Not Found: Asset not found.

PATCH /api/b2b/media/assets/[assetId]

Updates editable asset fields such as the display name or assigned folder.

Sample request
{
"displayName": "Warm Up Hero Banner",
"folderId": "folder_abc123"
}

PATCH response for a single asset

  • 200 OK: Returns the updated asset object.
  • 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.

Sample request
{
"assetIds": ["asset_xyz789", "asset_xyz790"],
"folderId": "folder_abc123"
}

POST response for bulk move

  • 200 OK: Returns the updated assets array.
  • 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. Use root or omit to upload into the root folder.

POST response for upload

  • 201 Created: Returns the created asset object.
  • 400 Bad Request: File type, size, or payload validation failed.
Example multipart upload
POST /api/b2b/media/upload
Authorization: Bearer <firebase_id_token>
Content-Type: multipart/form-data
note

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. Use true to show only videos that are ready to stream.
  • limit: Optional result cap.

GET response for Cloudflare video listing

  • 200 OK: Returns a videos array.
Sample response
{
"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

Sample request
{
"fileName": "seated-knee-lift.mp4",
"fileSize": 10485760,
"maxDurationSeconds": 1800
}

POST response for direct upload session

  • 201 Created: Returns a one-time uploadUrl plus a placeholder video record.
  • 400 Bad Request: Validation failed.
Sample response
{
"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 the video object.
  • 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

  1. Upload shared images through /api/b2b/media/upload.
  2. Upload reusable streamed videos by creating a direct upload session through /api/b2b/media/cloudflare/videos/direct-upload and posting the file to the returned Cloudflare URL.
  3. Browse or search reusable images with /api/b2b/media/assets and Cloudflare videos with /api/b2b/media/cloudflare/videos.
  4. Organize image assets with /api/b2b/media/folders and the bulk move endpoint.
  5. Store the selected image URL or Cloudflare HLS URL in content records that need a media reference.
tip

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.