Files API guide
Files (called resources in some schemas) hold the evidence attached to Threads: images, PDFs, documents, and scan artifacts. A file can be attached to a Thread directly or as the value of a resource-typed field.
There are two upload paths: a simple one-shot POST for small files, and the resumable tus protocol for large ones. Full schemas: API reference.
Endpoints at a glance
Section titled “Endpoints at a glance”| Operation | Method & path |
|---|---|
| Simple upload | POST /api/v1/files |
| Resumable upload (tus) | POST /api/v1/files/upload, then PATCH /api/v1/files/upload/{id} |
| Finalize tus uploads | POST /api/v1/files/finalize |
| Download | GET /api/v1/files/{resource_id}/download |
| Signed URLs | POST /api/v1/files/urls |
| Search files | GET /api/v1/files/search |
| List files (cursor) | GET /api/v1/files |
| List a Thread’s files | GET /api/v1/threads/{thread_id}/files |
File requests require current membership in the selected Team, which must belong to the selected organization. This applies to people and Service Accounts. Uploading to a Thread requires edit access; private uploads must target a Thread and require permission to manage its private assets. Unattached files are readable by their owning Team only when they are not private.
Simple upload
Section titled “Simple upload”For files a single request can carry comfortably, POST multipart/form-data with the file and optional attachment targets. The response includes the created resource with a signed URL:
curl -fsS "$APID_URL/api/v1/files" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "file=@inspection-report.pdf" \ -F "threadId=$THREAD_ID"const resources = await client.files.upload({ file, // a File threadId, // optional: attach to a Thread // fieldId, // optional: attach as a field value // isPrivate: true, // optional});fieldId attaches the file as the value of a resource-typed field; isPrivate restricts visibility.
Resumable upload (tus)
Section titled “Resumable upload (tus)”Large files use the tus 1.0 resumable-upload protocol at /api/v1/files/upload, then a finalize call that turns the completed upload into a resource record. The flow is upload → finalize → (already attached, or attach via fields).
-
Create the upload. POST with tus headers;
Upload-Metadatavalues are base64-encoded, andfilenameis required (optionalfieldId,threadId,isPrivate):Terminal window curl -i -X POST "$APID_URL/api/v1/files/upload" \-H "Authorization: Bearer $DUST_TOKEN" \-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \-H "Tus-Resumable: 1.0.0" \-H "Upload-Length: 52428800" \-H "Upload-Metadata: filename $(printf 'video.mp4' | base64),threadId $(printf %s "$THREAD_ID" | base64)"# → 201 Created# → Location: …/api/v1/files/upload/<upload-id>Keep the
<upload-id>fromLocationfor finalization. The final response supplies the permanent resource ID, which can differ. Declare the intended Thread, field, and privacy at creation; these cannot be changed during finalization.Upload-Lengthmust be a positive, known byte count. -
Send the bytes (resumable — repeat PATCHes continue from
Upload-Offset):Terminal window curl -i -X PATCH "$APID_URL/api/v1/files/upload/$UPLOAD_ID" \-H "Authorization: Bearer $DUST_TOKEN" \-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \-H "Tus-Resumable: 1.0.0" \-H "Upload-Offset: 0" \-H "Content-Type: application/offset+octet-stream" \--data-binary @video.mp4Upload requests tolerate longer idle pauses. To resume after an interruption,
HEADthe same URL to read the currentUpload-Offset, then PATCH from there. Any tus 1.0 client library (e.g.tus-js-client) speaks this protocol for you. -
Finalize. Uploads become resource records only after finalize:
Terminal window curl -fsS "$APID_URL/api/v1/files/finalize" \-H "Authorization: Bearer $DUST_TOKEN" \-H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \-H "Content-Type: application/json" \-d '{"threadId": "'"$THREAD_ID"'","requests": [{ "resId": "'"$UPLOAD_ID"'", "filename": "video.mp4", "size": 52428800 }]}'POST /api/v1/files/finalizeaccepts many uploads at once; each request item takes the tus upload ID asresIdplusfilenameandsize(optionalfieldId,isPrivate). The filename, size, Thread, field, and privacy must match the upload creation request. Use the same authenticated identity and organization/Team context for creation, chunks, HEAD, DELETE, and finalization. Upload sessions expire seven days after creation. Only completed uploads can be finalized.An identical finalization retry within that window returns the original resources with fresh signed URLs. A batch must contain either only new completed uploads or only previously finalized uploads. Finalized uploads cannot be patched or terminated through tus.
Downloads and signed URLs
Section titled “Downloads and signed URLs”GET /api/v1/files/{resource_id}/download— redirect to a signed URL for the file.POST /api/v1/files/urls?ids=<id>&ids=<id>— mint short-lived signed URLs for direct object-storage access; use these when a browser or downstream system needs the bytes without proxying.
Search and listing
Section titled “Search and listing”Two query styles exist:
GET /api/v1/files/search— page-indexed search withq,threadId,mimeFilters, andincludeArchived.GET /api/v1/files— cursor-paginated listing (cursor,pageSize;includeArchivedis required) filtered bythreadIdorcreatedBy.
For files in the context of one Thread, prefer GET /api/v1/threads/{thread_id}/files (see the Threads guide).
Related pages
Section titled “Related pages”- Threads API guide — attaching files to Thread fields and thumbnails
- Core model — where files sit in the domain
- API reference — full parameter and schema detail