Upload Files
API reference for uploading files via direct upload, signed URLs, or external URL registration.
PlatformXe supports three upload methods depending on your architecture: direct server-side upload, signed URLs for client-side upload, and external URL registration.
Direct upload
Upload a file directly from your server to PlatformXe.
POST /api/v1/storage/media/upload
Scope: storage:upload
Content-Type: multipart/form-data
Request body
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | The file to upload |
folder | string | No | Target folder path (e.g. invoices/2026) |
access | string | No | Access level: public, authenticated, or private. Default: public |
tags | string | No | Comma-separated tags for organization |
HEIC/HEIF is accepted and converted. iPhone photos arrive as HEIC, which no browser except Safari renders. PlatformXe transcodes them to JPEG before storage, so the stored file, its url, format and size describe the JPEG (the name keeps its stem with a .jpg extension, and the JPEG is typically ~1.5× the HEIC). A HEIC the decoder cannot read is refused with 415 UNSUPPORTED_MEDIA_TYPE rather than stored unreadable — convert it before uploading (on an iPhone: Settings → Camera → Formats → Most Compatible).
Response
{
"success": true,
"data": {
"fileId": "file_abc123def456",
"url": "https://cdn.platformxe.com/org_123/invoices/2026/receipt.pdf",
"format": "pdf",
"size": 245760,
"width": null,
"height": null,
"createdAt": "2026-04-05T14:30:00.000Z"
}
}
curl
curl -X POST https://api.platformxe.com/api/v1/storage/media/upload \
-H "x-api-key: pxk_live_your_api_key_here" \
-F "file=@/path/to/image.png" \
-F "folder=products/thumbnails" \
-F "access=public" \
-F "tags=product,thumbnail"
SDK
import { PlatformXeClient } from '@caldera/platformxe-sdk';
const px = new PlatformXeClient({ apiKey: 'pxk_live_your_api_key_here' });
const result = await px.storage.upload({
file: fileBuffer,
folder: 'products/thumbnails',
access: 'public',
tags: ['product', 'thumbnail'],
});
console.log(result.data.url);
// "https://cdn.platformxe.com/org_123/products/thumbnails/image.png"
Signed upload
Generate a pre-signed URL so clients can upload files directly from the browser without routing through your server.
POST /api/v1/storage/media/sign-upload
Scope: storage:upload
Request body
| Field | Type | Required | Description |
|---|---|---|---|
filename | string | Yes | Original filename |
contentType | string | Yes | MIME type of the file (e.g. image/png) |
folder | string | No | Target folder path |
access | string | No | Access level. Default: public |
maxSize | number | No | Maximum allowed file size in bytes |
Response
{
"success": true,
"data": {
"uploadUrl": "https://upload.platformxe.com/signed/abc123...",
"fileId": "file_pending_abc123",
"expiresAt": "2026-04-05T15:30:00.000Z"
}
}
Signed upload URLs expire after 60 minutes. The client must complete the upload before expiry or request a new signed URL.
curl
# Step 1: Get the signed URL
curl -X POST https://api.platformxe.com/api/v1/storage/media/sign-upload \
-H "Content-Type: application/json" \
-H "x-api-key: pxk_live_your_api_key_here" \
-d '{
"filename": "avatar.jpg",
"contentType": "image/jpeg",
"folder": "avatars",
"maxSize": 5242880
}'
# Step 2: Upload to the signed URL (from client)
curl -X PUT "https://upload.platformxe.com/signed/abc123..." \
-H "Content-Type: image/jpeg" \
--data-binary @avatar.jpg
Register external URL
Register a file that is already hosted elsewhere. PlatformXe indexes the file and optionally copies it to CDN storage.
POST /api/v1/storage/media/register
Scope: storage:upload
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Publicly accessible URL of the file |
filename | string | No | Display name for the file |
folder | string | No | Target folder path |
copyToCdn | boolean | No | If true, download and re-host on CDN. Default: false |
Response
{
"success": true,
"data": {
"fileId": "file_ext_abc123",
"url": "https://cdn.platformxe.com/org_123/imported/avatar.jpg",
"sourceUrl": "https://example.com/avatar.jpg",
"copied": true,
"createdAt": "2026-04-05T14:30:00.000Z"
}
}
SDK
const result = await px.storage.register({
url: 'https://example.com/product-photo.jpg',
folder: 'products',
copyToCdn: true,
});
console.log(result.data.fileId);
// "file_ext_abc123"
A registered HEIC/HEIF (from the signed-upload flow) is normalised the same way as a direct upload: PlatformXe fetches it back, converts it to JPEG, stores the JPEG beside it and deletes the original. The response and the file record describe the JPEG. A blob that cannot be converted returns 415 UNSUPPORTED_MEDIA_TYPE and nothing is recorded.
Use copyToCdn: true if the external URL may become unavailable. The file is downloaded and stored on PlatformXe CDN for reliable access.
Error responses
| Code | Description |
|---|---|
BAD_REQUEST | Missing file, unsupported file type, or exceeds size limit |
FORBIDDEN | API key does not have the storage:upload scope |
RATE_LIMITED | Rate limit exceeded for this API key |
MODERATION_REJECTED | File was rejected by content moderation |
UNSUPPORTED_MEDIA_TYPE | HTTP 415. The file is an accepted type that could not be made storable (a HEIC/HEIF the decoder cannot read). Convert it before uploading; retrying will not help |