Uploading files

Large files & chunked uploads

Split a file into parts, send each part to CDNForge, then complete the upload. The server binds size, MIME type, storage target, and public id to an upload session so clients cannot override those values at the end.

When to use chunked uploads

  • Files that are too large to send as one multipart body through a serverless request limit.
  • Video and audio from integrations that always prefer chunked (including the official FiveM resource).
  • Same-origin uploads when you do not want to depend on storage CORS for a client PUT.

For browser or NUI flows that can PUT directly to storage, presigned URLs remain a valid alternative.

Size limits

LimitValueNotes
Maximum upload size100 MBEnforced by the API for all upload paths (MAX_UPLOAD_BYTES).
Chunk / part size4 MBReturned as partSize from init. Each part must be 1 byte–4 MB.
Session TTL1 hourDefault. Overridable with server env UPLOAD_SESSION_TTL_SEC.

3.5 MB is not an API hard limit

Direct multipart endpoints (POST /api/v1/upload and POST /api/v1/file) accept files up to 100 MB when the request body can reach the API. The common 3.5 MB threshold is a client/platform guidance used by the dashboard helpers and the FiveM resource so uploads stay under typical serverless body limits (~4.5 MB). Prefer chunked (or presigned) once you are near that size.

Two API families

  • Media — /api/v1/upload/chunked/* with cnf_img_, cnf_vid_, or cnf_aud_.
  • Generic files — /api/v1/file/chunked/* with cnf_fil_ only. Bytes are stored as-is (no image compression).

Flow

  1. Init — declare filename, MIME type, and total size. CDNForge creates a session and returns uploadId.
  2. Part — upload each chunk as multipart form data with the session id and part number.
  3. Complete — send only the uploadId. The session supplies public id, MIME, size, and storage target.

1) Init

POST /api/v1/upload/chunked/init
curl -X POST https://app.back2rp.org/api/v1/upload/chunked/init \
  -H "Authorization: Bearer cnf_vid_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "clip.mp4",
    "mimeType": "video/mp4",
    "size": 18432000
  }'
Example response
{
  "uploadId": "V1StGXR8_Z5jdHi6B-myT",
  "publicId": "abc123xyz.mp4",
  "mimeType": "video/mp4",
  "originalName": "clip.mp4",
  "storageBucket": "cdnforge-media",
  "partSize": 4194304,
  "expiresAt": "2026-08-31T23:00:00.000Z"
}

Optional fields on init: folder, folderId, and metadata. They are stored on the session and applied when the upload completes.

uploadId is server-generated

Always use the uploadId returned by init. Do not invent session ids. Treat publicId, mimeType, and storageBucket from the response as informative; the session remains authoritative on complete.

2) Upload parts

Multipart form fields: uploadId, partNumber (starts at 1), and chunk (the binary part).

POST /api/v1/upload/chunked/part
curl -X POST https://app.back2rp.org/api/v1/upload/chunked/part \
  -H "Authorization: Bearer cnf_vid_your_key" \
  -F uploadId="$UPLOAD_ID" \
  -F partNumber=1 \
  -F chunk=@part1.bin
Example response
{
  "ok": true,
  "partNumber": 1,
  "size": 4194304
}
  • Use the partSize from init (4 MB) for all but possibly the last part.
  • Parts that would exceed the declared total size return HTTP 413.
  • Invalid or missing chunks return HTTP 400.

3) Complete

Send only the session id. Do not send publicId, mimeType, size, or storageBucket on complete — the API ignores client overrides and uses the upload session.

POST /api/v1/upload/chunked/complete
curl -X POST https://app.back2rp.org/api/v1/upload/chunked/complete \
  -H "Authorization: Bearer cnf_vid_your_key" \
  -H "Content-Type: application/json" \
  -d '{"uploadId":"V1StGXR8_Z5jdHi6B-myT"}'
Example response
{
  "id": "fil_...",
  "url": "https://cdn.back2rp.org/u/abc123xyz.mp4",
  "publicId": "abc123xyz.mp4",
  "size": 18432000,
  "mimeType": "video/mp4",
  "originalName": "clip.mp4"
}

Generic file chunked routes

Same protocol with a file key and the /api/v1/file/chunked/* paths:

  • POST /api/v1/file/chunked/init
  • POST /api/v1/file/chunked/part
  • POST /api/v1/file/chunked/complete
Init with a file key
curl -X POST https://app.back2rp.org/api/v1/file/chunked/init \
  -H "Authorization: Bearer cnf_fil_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "pack.zip",
    "mimeType": "application/zip",
    "size": 52428800
  }'

Errors & retries

StatusMeaning
400Invalid part, missing chunk, no parts found, or assembled size mismatch.
404Unknown session, or session belonging to another organization.
409Session already completed, or file already saved.
410Upload session expired.
413Part would exceed the size declared at init, or file exceeds 100 MB.
415Declared filename/MIME is a blocked type (for example HTML, SVG, or executable).
429 / 5xxRate limited or temporary failure — safe to retry the same part. Do not invent a new uploadId.

The official FiveM resource retries 429/5xx up to three times and keeps at most two uploads in flight. Custom clients should retry transient failures on the same session and part number.

Sessions expire

Pending sessions expire after the TTL (default one hour). Expired sessions return HTTP 410. Start a new init if that happens; previous parts for that session are no longer usable.