Files
File fields do not store bytes in Mongo. The client:
- Starts an upload session (declared
filename,contentType,size). PUTs the bytes to the returned presigned URL (S3/MinIO).- Completes the session so the file is attached to the record.
The SDKs wrap this in uploadFile (upload_file in Python).
The record must already exist. Create the document first, then upload.
Completed uploads and file deletes can fire webhooks (file.uploaded, file.deleted).
All three clients reject a buffer whose length is not the declared size before they PUT, with Upload stream length (<size>) does not match bytes read (<n>). The server accepts an object smaller than the declared size. It rejects an object larger than that size.
uploadFile accepts a Blob, ArrayBuffer, or Uint8Array.
const bytes = new Uint8Array(await file.arrayBuffer());const meta = await posts.uploadFile(recordId, 'attachment', { data: bytes, size: bytes.byteLength, filename: 'photo.jpg', contentType: 'image/jpeg',});
const download = await posts.getDownloadUrl(recordId, 'attachment', meta.id);console.log(download.url);
await posts.deleteFile(recordId, 'attachment', meta.id);final bytes = await File('photo.jpg').readAsBytes();final meta = await posts.uploadFile( recordId, 'attachment', data: Stream.value(bytes), length: bytes.length, filename: 'photo.jpg', contentType: 'image/jpeg',);
final download = await posts.getDownloadUrl(recordId, 'attachment', meta.id);print(download.url);
await posts.deleteFile(recordId, 'attachment', meta.id);upload_file accepts bytes.
meta = posts.upload_file( record_id, "attachment", data=photo_bytes, size=len(photo_bytes), filename="photo.jpg", content_type="image/jpeg",)download = posts.get_download_url(record_id, "attachment", meta.id)posts.delete_file(record_id, "attachment", meta.id)| Step | Method | Path |
|---|---|---|
| Start | POST | /api/p/:projectId/:table/:recordId/files/:field/upload-sessions |
| Complete | POST | /api/p/:projectId/:table/:recordId/files/:field/:fileId/complete |
| Download URL | GET | /api/p/:projectId/:table/:recordId/files/:field/:fileId |
| Delete | DELETE | same as download |
Enable the upload, download, and fileDelete endpoints. Add file rules (fileSizeLimit, fileMimeType, fileExtensionAllow) on upload.
A disabled upload endpoint is 404 Upload endpoint not found. Download is 404 Download endpoint not found. File delete is 404 File delete endpoint not found.
The session’s maxBytes is the plan ceiling for one file. See Usage and limits.
Session, PUT, and complete
Section titled “Session, PUT, and complete”-
POST /api/p/{projectId}/{table}/{recordId}/files/{field}/upload-sessionswith{ "filename", "contentType", "size" }.sizemust be greater than 0. An empty filename or content type is400. A declaredsizeabove the plan maximum is 413File too large. A missing record is404Record not found. A disabled upload endpoint is404Upload endpoint not found. -
200
datacontainsfileId,storageKey,uploadUrl,uploadSessionId,expiresInSeconds(900), andmaxBytes(the plan ceiling). The presign TTL is 15 minutes.expiresAton the session is that same window. -
PUTthe bytes touploadUrlwith the same content type. This request goes to object storage, not toapi.tefily.com. -
POST .../files/{field}/{fileId}/completewith{ "uploadSessionId" }. 200datais the file metadata:_id,schemaId,tableName,recordId,fieldName,key,filename,contentType,size,uploadedBy,createdAt. The record field is updated in the same call.
Complete rejects the session with 400 when it does not match (Upload session does not match this upload), is already completed (Upload session already completed), or has expired (Upload session has expired). A missing object is Uploaded file not found in storage. An empty object is Uploaded file is empty. An object larger than the declared size is Uploaded file is larger than declared size. A smaller object is accepted, and the stored size is the object size. Another user’s session is 403 Upload session was created by another user unless the caller bypasses access rules.
Download GET on the same path as delete returns { "url", "expiresAt", "file" }. expiresAt is 15 minutes out. The download URL is a presigned GET.
Array file fields and deletion
Section titled “Array file fields and deletion”On a scalar file field, complete sets the field to the file id. On an array file field, complete appends the id. File delete removes the id from the array, or sets a scalar field to null.
maxBytes in the session response is the plan max_file_bytes (see Usage and limits).
Deleting the document, one row or in bulk, also deletes storage objects and _files rows for that tableName and recordId. Each object is removed on its own. An error is logged and is not returned on the API response.
Pending upload sessions expire after 15 minutes. The next upload begin or complete marks those sessions expired and deletes their objects.