CBFiles Developer Guide¶
Complete integration guide for the CBFiles file management service.
Overview¶
CBFiles provides a REST API for file storage and management, wrapping MinIO object storage. It offers:
- File Operations: Upload, download, list, delete files
- Bucket Management: Create and configure storage buckets
- Access Control: Private and public bucket access modes
- Signed URLs: Time-limited URLs for secure file sharing
- CDN: Public file delivery via
cdn.nominate.ai
Endpoints¶
| Service | URL | Purpose |
|---|---|---|
| API | https://files.nominate.ai |
Full API access |
| CDN | https://cdn.nominate.ai |
Public file delivery |
| Docs | https://files.nominate.ai/docs |
OpenAPI/Swagger UI |
Authentication¶
All API requests (except public endpoints) require an API key.
Header Formats¶
# Option 1: X-API-Key header
curl -H "X-API-Key: cbfiles_abc123..." https://files.nominate.ai/api/v1/users/me
# Option 2: Bearer token
curl -H "Authorization: Bearer cbfiles_abc123..." https://files.nominate.ai/api/v1/users/me
Obtaining an API Key¶
Contact your administrator or use the CLI if you have server access:
Python Client Library¶
The recommended way to integrate with CBFiles from Python applications.
Installation¶
# From the cbfiles package
pip install cbfiles
# Or install httpx separately if using client only
pip install httpx
Basic Usage¶
from cbfiles.client import CBFilesClient
# Initialize client
client = CBFilesClient(
base_url="https://files.nominate.ai",
api_key="cbfiles_your_api_key_here"
)
# Upload a file
with open("document.pdf", "rb") as f:
result = client.upload_file(
bucket_name="my-bucket",
object_name="documents/report.pdf",
content=f,
content_type="application/pdf"
)
print(f"Uploaded: {result['name']}, ETag: {result['etag']}")
# Download a file
content = client.download_file("my-bucket", "documents/report.pdf")
with open("downloaded.pdf", "wb") as f:
f.write(content)
# List files
files = client.list_files("my-bucket", prefix="documents/")
for file in files:
print(f"{file['name']}: {file['size']} bytes")
# Get signed URL for sharing
signed = client.get_signed_url("my-bucket", "documents/report.pdf", expires_in=3600)
print(f"Share this URL: {signed['url']}")
# Clean up
client.close()
Context Manager¶
from cbfiles.client import CBFilesClient
with CBFilesClient("https://files.nominate.ai", "cbfiles_xxx") as client:
files = client.list_files("my-bucket")
# Client automatically closes when exiting the block
Error Handling¶
from cbfiles.client import CBFilesClient, CBFilesError
try:
client = CBFilesClient("https://files.nominate.ai", "cbfiles_xxx")
content = client.download_file("my-bucket", "nonexistent.txt")
except CBFilesError as e:
print(f"Error: {e}")
print(f"Status code: {e.status_code}")
REST API Reference¶
Base URL¶
Buckets¶
List Buckets¶
GET /buckets?include_public=true
# Response
[
{
"name": "my-bucket",
"owner_id": "uuid",
"access": "private",
"description": "My files",
"created_at": "2024-01-01T00:00:00Z"
}
]
Create Bucket¶
POST /buckets
Content-Type: application/json
{
"name": "my-bucket",
"access": "private",
"description": "Optional description"
}
# access options: "private" | "public_read"
Update Bucket¶
PATCH /buckets/{bucket_name}
Content-Type: application/json
{
"access": "public_read",
"description": "Updated description"
}
Delete Bucket¶
Files¶
List Files¶
GET /buckets/{bucket_name}/files?prefix=documents/&recursive=true
# Response
[
{
"bucket": "my-bucket",
"name": "documents/file.pdf",
"size": 12345,
"content_type": "application/pdf",
"etag": "abc123",
"last_modified": "2024-01-01T00:00:00Z"
}
]
Upload File¶
POST /buckets/{bucket_name}/files/{object_name}
Content-Type: multipart/form-data
# Form field: file (binary)
# Response
{
"bucket": "my-bucket",
"name": "documents/file.pdf",
"size": 12345,
"etag": "abc123",
"url": "https://..."
}
Download File¶
GET /buckets/{bucket_name}/files/{object_name}
# Returns binary content with appropriate Content-Type header
Delete File¶
Get File Info (HEAD)¶
HEAD /buckets/{bucket_name}/files/{object_name}
# Returns headers: Content-Type, Content-Length, ETag, Last-Modified
Signed URLs¶
Generate Download URL¶
POST /buckets/{bucket_name}/files/{object_name}/signed-url?expires_in=3600
# Response
{
"url": "https://...",
"expires_at": "2024-01-01T01:00:00Z",
"bucket": "my-bucket",
"object_name": "file.pdf"
}
Generate Upload URL¶
POST /buckets/{bucket_name}/files/{object_name}/upload-url?expires_in=3600
# Response - use this URL with PUT to upload directly
{
"url": "https://...",
"expires_at": "2024-01-01T01:00:00Z"
}
Users & API Keys¶
Get Current User¶
GET /users/me
# Response
{
"id": "uuid",
"name": "Service Name",
"email": "service@example.com",
"is_active": true,
"created_at": "2024-01-01T00:00:00Z"
}
List API Keys¶
GET /users/me/keys
# Response (note: actual key values are never returned)
[
{
"id": "uuid",
"name": "production-key",
"prefix": "cbfiles_abc",
"created_at": "2024-01-01T00:00:00Z",
"last_used_at": "2024-01-15T12:00:00Z",
"is_active": true
}
]
Create API Key¶
POST /users/me/keys?key_name=new-key
# Response (key shown only once!)
{
"key_info": {
"id": "uuid",
"name": "new-key",
"prefix": "cbfiles_xyz"
},
"api_key": "cbfiles_xyzABC123..."
}
Revoke API Key¶
CDN Access¶
Public buckets can be accessed directly via the CDN without authentication.
Direct CDN URL¶
Example:
Making a Bucket Public¶
# Via API
PATCH /buckets/my-bucket
{"access": "public_read"}
# Via Python client
client.update_bucket("my-bucket", access="public_read")
CDN Headers¶
CDN responses include caching headers:
- Cache-Control: public, max-age=31536000 (1 year)
- ETag for cache validation
Integration Examples¶
FastAPI Service Integration¶
from fastapi import FastAPI, UploadFile, HTTPException
from cbfiles.client import CBFilesClient, CBFilesError
app = FastAPI()
cbfiles = CBFilesClient("https://files.nominate.ai", "cbfiles_xxx")
@app.post("/upload")
async def upload_document(file: UploadFile):
try:
content = await file.read()
result = cbfiles.upload_file(
"documents",
f"uploads/{file.filename}",
content,
file.content_type or "application/octet-stream"
)
return {"url": result["url"], "name": result["name"]}
except CBFilesError as e:
raise HTTPException(status_code=e.status_code or 500, detail=str(e))
@app.get("/download/{filename}")
async def get_download_url(filename: str):
try:
signed = cbfiles.get_signed_url("documents", f"uploads/{filename}", expires_in=300)
return {"download_url": signed["url"]}
except CBFilesError as e:
raise HTTPException(status_code=404, detail="File not found")
Django Integration¶
# settings.py
CBFILES_URL = "https://files.nominate.ai"
CBFILES_API_KEY = os.environ.get("CBFILES_API_KEY")
# utils.py
from django.conf import settings
from cbfiles.client import CBFilesClient
def get_cbfiles_client():
return CBFilesClient(settings.CBFILES_URL, settings.CBFILES_API_KEY)
# views.py
from django.http import JsonResponse
from .utils import get_cbfiles_client
def upload_file(request):
if request.method == "POST":
file = request.FILES["file"]
client = get_cbfiles_client()
result = client.upload_file(
"uploads",
file.name,
file.read(),
file.content_type
)
return JsonResponse(result)
JavaScript/Fetch Integration¶
const CBFILES_URL = 'https://files.nominate.ai/api/v1';
const API_KEY = 'cbfiles_xxx';
// Upload a file
async function uploadFile(bucket, objectName, file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(
`${CBFILES_URL}/buckets/${bucket}/files/${objectName}`,
{
method: 'POST',
headers: {
'X-API-Key': API_KEY
},
body: formData
}
);
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`);
}
return response.json();
}
// Get signed URL for download
async function getSignedUrl(bucket, objectName, expiresIn = 3600) {
const response = await fetch(
`${CBFILES_URL}/buckets/${bucket}/files/${objectName}/signed-url?expires_in=${expiresIn}`,
{
method: 'POST',
headers: {
'X-API-Key': API_KEY
}
}
);
return response.json();
}
// List files
async function listFiles(bucket, prefix = '') {
const params = new URLSearchParams({ prefix, recursive: true });
const response = await fetch(
`${CBFILES_URL}/buckets/${bucket}/files?${params}`,
{
headers: {
'X-API-Key': API_KEY
}
}
);
return response.json();
}
cURL Examples¶
# Set your API key
export CBFILES_KEY="cbfiles_your_key_here"
# Create a bucket
curl -X POST "https://files.nominate.ai/api/v1/buckets" \
-H "X-API-Key: $CBFILES_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "my-files", "access": "private"}'
# Upload a file
curl -X POST "https://files.nominate.ai/api/v1/buckets/my-files/files/docs/readme.txt" \
-H "X-API-Key: $CBFILES_KEY" \
-F "file=@./README.md"
# List files
curl "https://files.nominate.ai/api/v1/buckets/my-files/files" \
-H "X-API-Key: $CBFILES_KEY"
# Download a file
curl "https://files.nominate.ai/api/v1/buckets/my-files/files/docs/readme.txt" \
-H "X-API-Key: $CBFILES_KEY" \
-o downloaded.txt
# Get signed URL
curl -X POST "https://files.nominate.ai/api/v1/buckets/my-files/files/docs/readme.txt/signed-url?expires_in=3600" \
-H "X-API-Key: $CBFILES_KEY"
# Delete a file
curl -X DELETE "https://files.nominate.ai/api/v1/buckets/my-files/files/docs/readme.txt" \
-H "X-API-Key: $CBFILES_KEY"
Best Practices¶
Bucket Naming¶
- Use lowercase letters, numbers, and hyphens
- 3-63 characters
- Must start and end with alphanumeric
Object Naming¶
- Use forward slashes for logical folder structure:
reports/2024/q1/summary.pdf - Avoid special characters except:
- _ . / - Maximum path length: 1024 characters
Security¶
- Store API keys in environment variables, never in code
- Use short expiration times for signed URLs (5-60 minutes for sensitive files)
- Use private buckets by default, only make public what needs to be
Performance¶
- Use presigned upload URLs for large files to upload directly to storage
- Leverage CDN caching for public assets
- Use prefix-based listing for large buckets
Error Codes¶
| HTTP Code | Meaning |
|---|---|
| 400 | Bad request (invalid parameters) |
| 401 | Unauthorized (missing or invalid API key) |
| 403 | Forbidden (no access to resource) |
| 404 | Not found (bucket or file doesn't exist) |
| 409 | Conflict (duplicate bucket name) |
| 500 | Server error |
Error responses include a detail field:
Support¶
- API Documentation: https://files.nominate.ai/docs
- Issues: Contact your administrator