Skip to content
Download as .md

File Upload Integration

Written by Rohman Beny Riyanto

How to upload and display files (images, videos, documents) through the API. The actual file bytes never pass through this API's own servers - your upload/download goes directly to storage, using a temporary signed URL the API hands you first.

The basic idea

Two separate paths:

  • Getting permission / a link - your app asks the API for a URL, the API replies with one.
  • Sending/receiving the actual file - your app talks directly to storage using that URL. The API is not in this path at all.

Base path: /v2/files/*. Currently public - no login required yet. This will require authentication once it's wired into a real feature instead of the internal test app it's used with today.

Uploading a small file

The API decides whether your file is "small" (one direct upload) or "large" (split into parts) - you don't choose this yourself, just follow what it tells you.

The last step (bind) matters: an uploaded-but-never-bound file is automatically deleted after a while. Always bind a file to something once it's confirmed, or it disappears on its own.

Uploading a large file (split into parts)

If your file is above the size threshold for its policy, create-upload-slot returns multipart: true instead of a single upload_url.

Keep the ETag from every part's upload response - complete needs {part_number, etag} for every single part, not just how many there were.

Displaying/downloading a file

Always ask for a fresh link right before you need to show/download a file - don't store the link long-term, private-file links expire.

Endpoint reference

All under /v2/files. For POST/PUT/PATCH/DELETE, wrap the body as {"command": "<name>", "data": {...}}.

MethodPathCommand/QueryPurpose
POST/uploadscreate-upload-slotStart an upload, get upload_url or multipart flag
POST/uploads/:file_id/confirmconfirm-uploadFinalize a small/single upload
DELETE/uploads/:file_idabort-uploadCancel an upload that was never confirmed
POST/uploads/:file_id/multipartinit-multipart-uploadStart a large-file (multipart) upload
POST/uploads/:file_id/multipart/partsget-part-urlGet a signed URL for one part
GET/uploads/:file_id/multipartget-multipart-progressWhich parts already finished (for resuming)
POST/uploads/:file_id/multipart/completecomplete-multipart-uploadFinish a large-file upload
DELETE/uploads/:file_id/multipartabort-multipart-uploadCancel an unfinished large-file upload
POST/:file_id/bindbind-fileAttach a file to something (required - see above)
DELETE/:file_id/bindunbind-fileDetach a file
GET/:file_idget-file-infoFile metadata + status
GET/:file_id/urlget-file-urlGet a link to view/download the file
GET?owner_service=&owner_type=&owner_ref=list-files-by-ownerAll files attached to one thing
GET/policies/:codeget-policyRules for one upload type (allowed formats, size limit)
GET/policieslist-policiesEvery available upload type and its rules

get-part-url and get-multipart-progress are exempt from the app-wide rate limit, so uploading a large file with many parts won't get throttled.

Attaching files to your own records (coming later)

Right now this API only handles files by themselves. When features like "attach a video to an order" or "set a device's thumbnail" are built, attaching a file will work the same way as bind above: you'll pass the file_id you got from uploading when you create/update that record, and the record will remember which file(s) belong to it. No change expected on the upload flow itself - the extra step is just telling that other feature which file to use.