Storage
Base URL: https://storage-api.pcampus.co
CDN: https://cdn.pcampus.co
Canonical API: /api-reference/storage — paths under /v1/* (bucket + key)
Overview
Storage is the object storage service for storing and serving all file types used in the Pcampus ecosystem — from brand assets in Content OS and creative files in Cockpit, to files that customer developers upload through their own apps. It supports CDN delivery so end users receive files quickly.
Who uses this
| Persona | What they do |
|---|---|
| Internal service | Content OS, Cockpit call the API to store assets and retrieve URLs |
| Developer (customer) | Upload and serve files for their own app (user avatars, documents, etc.) |
| End user | Upload a profile picture or document via an app |
| Admin | View storage usage, manage buckets |
Key features
- File upload — Direct upload (
POST /v1/upload) and presigned upload (POST /v1/presign) for client-side - Bucket management — Buckets separated by organization and use case (public, private)
- CDN delivery — Public objects served at
https://cdn.pcampus.co/{bucket}/{key} - Image transformation — Resize, crop, and compress on-the-fly via URL parameters (see API reference)
- Access control — Public buckets (CDN) vs. private buckets (auth or download presign)
- Usage tracking — Track storage used per org / bucket
- Object identity — Addressed by
bucket+key(plusobjectIdin responses)
User stories
Content OS (internal):
"When a brand manager uploads a logo, I need it stored and a CDN URL returned for display."
→ Content OS calls POST /v1/upload → receives CDN url + objectId → stores it in the Content OS DB → renders via CDN
Developer (customer):
"I want users to upload profile pictures without the file passing through my backend."
→ POST /v1/presign → receive uploadUrl + objectUrl → client PUTs directly to the object store → use objectUrl as the public URL
Admin:
"I want to see an org's storage usage to check whether it's approaching the limit."
→ Console → Storage → Usage dashboard → see used / limit per bucket
How it works
Direct upload flow:
Presigned URL flow (client-side upload):
Data model
Objects are addressed by bucket + key. Responses may also include objectId for correlation — never expose raw internal storage_path to clients.
Integration
- Content OS — Stores brand logos, images, and content attachments
- Cockpit — Stores campaign creative files
- PIP — Every mutating call goes through an access check (
storage.upload,storage.read) withAuthorization: Bearer+x-tenant-id
Image transformation
Public images support on-the-fly transformation via URL query params:
# Resize to 400x300
https://cdn.pcampus.co/{bucket}/{key}?w=400&h=300
# Crop (center) and compress
https://cdn.pcampus.co/{bucket}/{key}?w=400&h=300&fit=cover&q=80
# Convert format
https://cdn.pcampus.co/{bucket}/{key}?f=webpParameter details: API reference → Image Transformation
API overview
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/upload | Upload file directly (multipart; bucket, key, public) |
POST | /v1/presign | Presigned URL for client PUT upload |
GET | /v1/objects/:bucket/:key | Get / redirect object (private requires auth) |
POST | /v1/presign/download | Presigned download for private objects |
DELETE | /v1/objects/:bucket/:key | Delete object |
GET | /v1/buckets | List buckets for tenant |
POST | /v1/buckets | Create bucket |
DELETE | /v1/buckets/:bucketName | Delete bucket (destructive — empties objects) |
Full request/response shapes: API reference
Upload response
{
"objectId": "obj_xxx",
"bucket": "products",
"key": "PROD-001/main.jpg",
"url": "https://cdn.pcampus.co/products/PROD-001/main.jpg",
"size": 204800,
"mimeType": "image/jpeg",
"public": true,
"uploadedAt": "2026-07-15T10:00:00.000Z"
}Private bucket objects require a time-limited signed URL or an authenticated API call — never expose storage_path directly.