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.

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

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

    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)"
    # → 201 Created
    # → Location: …/api/v1/files/upload/<upload-id>

    The <upload-id> in the returned Location is the upload’s resource ID — keep it.

  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

    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 response returns the created resources with signed URLs.

  • GET /api/v1/files/{resource_id}/download — stream the file through the API.
  • 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).