Skip to content

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.

OperationMethod & path
Simple uploadPOST /api/v1/files
Resumable upload (tus)POST /api/v1/files/upload, then PATCH /api/v1/files/upload/{id}
Finalize tus uploadsPOST /api/v1/files/finalize
DownloadGET /api/v1/files/{resource_id}/download
Signed URLsPOST /api/v1/files/urls
Search filesGET /api/v1/files/search
List files (cursor)GET /api/v1/files
List a Thread’s filesGET /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.

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:

Terminal window
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"

fieldId attaches the file as the value of a resource-typed field; isPrivate restricts visibility.

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).

  1. Create the upload. POST with tus headers; Upload-Metadata values are base64-encoded, and filename is required (optional fieldId, 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> from Location for 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-Length must be a positive, known byte count.

  2. 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.mp4

    Upload requests tolerate longer idle pauses. To resume after an interruption, HEAD the same URL to read the current Upload-Offset, then PATCH from there. Any tus 1.0 client library (e.g. tus-js-client) speaks this protocol for you.

  3. 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/finalize accepts many uploads at once; each request item takes the tus upload ID as resId plus filename and size (optional fieldId, 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.

  • 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.

Two query styles exist:

  • GET /api/v1/files/search — page-indexed search with q, threadId, mimeFilters, and includeArchived.
  • GET /api/v1/files — cursor-paginated listing (cursor, pageSize; includeArchived is required) filtered by threadId or createdBy.

For files in the context of one Thread, prefer GET /api/v1/threads/{thread_id}/files (see the Threads guide).