Uploads API
Resumable uploads in chunks, for large files and several files behind one link. Every request sends a token in the Authorization: Bearer header; see Get started with the API.
Start an upload
Section titled “Start an upload”POST /api/upload-session
Starts a resumable upload of one file, or of several files behind one link. The response’s artifact says how to send the bytes: when chunk_size is 0, send everything with Upload in one request; otherwise send total_chunks chunks with Upload a chunk, or straight to the bucket when direct_upload is true (Get upload URLs for chunks). Then Finish an upload creates the shared link.
To send several files behind one link, use session_type multi (or folder to keep folders): you write the files into one uncompressed ZIP archive, laid out as described under session_type, under the entry_name each file gets in the response, and upload that archive. A ZIP you already have can also be sent as a single upload.
When you act as a user with Dew-Act-As, the upload belongs to them: send the same header on every later call for this upload session.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Request body
Section titled “Request body”Content type application/json.
| Field | Type | Description |
|---|---|---|
session_type (required) |
string: single, multi, folder |
single: one file, uploaded as it is. multi: several files behind one link. folder: like multi, keeping the folder structure and empty folders. For multi and folder you upload one ZIP archive that you write yourself. It must be exactly artifact.size_bytes long, which it is when you follow this layout: - Entries in order: the empty_dirs names first, as returned, then the files by entry_index, each named by its entry_name. - Every entry stored (no compression), with the UTF-8 name flag set and no extra fields except zip64 ones. No archive comment. - A file entry is a local header (30 bytes plus the name) with the data descriptor flag set and CRC and sizes zero, then the file’s bytes, then a data descriptor with its signature: 16 bytes, or 24 bytes when the file is 4 GiB - 1 bytes or larger. A folder entry is a local header only, with a name ending in /. - An entry uses zip64 when its size or the offset of its local header reaches 4 GiB - 1. Its central directory record then carries the 28-byte zip64 extra field with both sizes and the offset, and both 32-bit size fields are 0xFFFFFFFF. - Add the zip64 end of central directory record and locator when there are 65,535 entries or more, or the central directory’s size or offset reaches 4 GiB - 1. Finish an upload checks the archive’s central directory: exactly the files you listed (minus any you skip), stored, at their declared sizes. |
files (required) |
array of UploadSessionFileInput | The files to upload, in archive order. A single upload takes exactly one. A folder upload may send none when empty_dirs lists at least one folder. Requests are capped at 4 MiB; send more files with Add files. |
link_type |
string: public, protected, private, onetime |
Who can open the shared link: public (anyone with the link), protected (password, SMS code or invited email addresses, set in protection), private (IP allowlist, set in protection) or onetime (one download). Default "public". |
protection |
UploadLinkProtection | |
domain |
string | The domain for the shared link. It must be active and one you may use. Defaults to the domain you call the API on. |
folder_name |
string | multi and folder: the archive’s name, without .zip, at most 1020 bytes. Defaults to archive- plus the date and time. |
empty_dirs |
array of string | folder: empty folders to keep, such as 2026/raw. |
Response 200
Section titled “Response 200”The upload session was created.
data is UploadSession.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | The request is invalid: for example an unknown session_type, no files, a single upload with more than one file, an inactive domain, a blocked file type, an unsafe relative_path or invalid link settings. |
“The ‘.exe’ file type has been blocked by your administrator.” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, the domain is not one you may use (Not allowed to use this domain), or the user’s account is unlicensed (Unlicensed). |
“Not allowed to use this domain” |
| 413 | The upload is larger than the domain allows (Total size exceeds limit (N GB)), or larger than 5 TB on S3-compatible storage. |
“Total size exceeds limit (100 GB)” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
|
| 503 | The domain’s storage is unavailable. Contact your admin. | “Storage provider for this domain is not available. Contact your administrator.” |
| 507 | The storage doesn’t have enough free space for this upload. | “Not enough storage space for this upload” |
Get an upload
Section titled “Get an upload”GET /api/upload-session/{session_id}
Returns an upload session’s state and plan, for example to resume after a restart. The files are not listed again. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
Response 200
Section titled “Response 200”The upload session.
data is UploadSessionStatus.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | The request is invalid; message says what to change. |
|
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
Cancel an upload
Section titled “Cancel an upload”DELETE /api/upload-session/{session_id}
Cancels an upload that hasn’t finished and deletes everything uploaded for it, including chunks sent straight to the bucket. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
Response 200
Section titled “Response 200”The upload was canceled.
data is Success.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | The request is invalid; message says what to change. |
|
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 409 | The upload is being finished or has finished. Delete the file instead. | “Session is no longer cancelable” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
Add files to an upload
Section titled “Add files to an upload”POST /api/upload-session/{session_id}/add-files
Adds more files to a multi or folder upload, for example when listing a large folder in batches. Only possible before any bytes of the archive are uploaded: write the archive from the plan this returns, which replaces the earlier one. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
Request body
Section titled “Request body”Content type application/json.
| Field | Type | Description |
|---|---|---|
files (required) |
array of UploadSessionFileInput | The files to add. Requests are capped at 4 MiB. |
Response 200
Section titled “Response 200”The files were added.
data is UploadSessionFilesAdded.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | No files, the upload is single or no longer active, a blocked file type, or an unsafe relative_path. |
“Files can only be added to a multi-file or folder session” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 409 | Bytes of the archive were already uploaded. | “Files cannot be added after the upload has started” |
| 410 | The upload session expired. Start a new upload. | “This upload has expired. Start it again.” |
| 413 | The upload is larger than the domain allows (Total size exceeds limit (N GB)), or larger than 5 TB on S3-compatible storage. |
“Total size exceeds limit (100 GB)” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
|
| 507 | The storage doesn’t have enough free space for this upload. | “Not enough storage space for this upload” |
Upload a chunk
Section titled “Upload a chunk”PATCH /api/upload-session/{session_id}/files/{file_id}/chunk/{chunk_number}
Uploads one chunk of the planned bytes. Chunk n starts at byte (n - 1) × chunk_size and is chunk_size bytes long; the last chunk carries the rest. Chunks can be sent in parallel. Sending a chunk again replaces it, so retry any chunk that fails. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
file_id (required) |
path | string | The upload’s artifact.file_id. It is always the same as session_id. |
chunk_number (required) |
path | integer | The chunk’s number, from 1 to total_chunks. Minimum 1. |
X-Chunk-Offset (required) |
header | integer (int64) | Where the chunk starts: (chunk_number - 1) × chunk_size. Minimum 0. |
Content-Length (required) |
header | integer (int64) | The chunk’s exact length. Chunked transfer encoding is not accepted. |
Request body
Section titled “Request body”The chunk’s bytes.
Content type application/octet-stream.
Response 200
Section titled “Response 200”The chunk was stored.
data fields:
| Field | Type | Description |
|---|---|---|
chunk_number |
integer |
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | The chunk doesn’t match the plan (number, offset or length), X-Chunk-Offset is missing, the upload goes in one request, file_id is not the upload’s, or the upload is no longer active. |
“Chunk 3 must carry 16777216 bytes at offset 33554432” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 409 | This chunk is still being uploaded by another request. Retry in a few seconds. | “Chunk is already being uploaded” |
| 410 | The upload session expired. Start a new upload. | “This upload has expired. Start it again.” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
List uploaded chunks
Section titled “List uploaded chunks”GET /api/upload-session/{session_id}/files/{file_id}/chunks
Returns which chunks the server already has, including chunks sent straight to the bucket. Use it to resume an interrupted upload: send only the missing chunks, then finish. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
file_id (required) |
path | string | The upload’s artifact.file_id. It is always the same as session_id. |
Response 200
Section titled “Response 200”The chunks received so far.
data is UploadChunkProgress.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | file_id is not the upload’s, or the upload is no longer active. |
“Session is not active” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 410 | The upload session expired. Start a new upload. | “This upload has expired. Start it again.” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
Upload in one request
Section titled “Upload in one request”PUT /api/upload-session/{session_id}/files/{file_id}/fast
Uploads all the planned bytes in one request, for uploads whose plan has chunk_size 0. Send exactly artifact.size_bytes bytes: more is refused, and a different size makes finishing fail. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
file_id (required) |
path | string | The upload’s artifact.file_id. It is always the same as session_id. |
Request body
Section titled “Request body”The file, or your ZIP archive for multi and folder uploads.
Content type application/octet-stream.
Response 200
Section titled “Response 200”The bytes were stored.
data fields:
| Field | Type | Description |
|---|---|---|
uploaded_bytes |
integer (int64) |
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | The upload must be sent in chunks, file_id is not the upload’s, or the upload is no longer active. |
“File must be uploaded in chunks” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 410 | The upload session expired. Start a new upload. | “This upload has expired. Start it again.” |
| 413 | More bytes were sent than planned. | “File exceeds declared size” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
Get upload URLs for chunks
Section titled “Get upload URLs for chunks”POST /api/upload-session/{session_id}/files/{file_id}/presign
When the upload session has direct_upload: true, returns URLs to PUT chunks straight to the storage bucket, up to 500 per request. Each URL accepts exactly its chunk’s planned length and stops working after 6 hours; ask for URLs as you need them. Chunks sent this way and chunks sent with Upload a chunk can be mixed. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
file_id (required) |
path | string | The upload’s artifact.file_id. It is always the same as session_id. |
Request body
Section titled “Request body”Content type application/json.
| Field | Type | Description |
|---|---|---|
part_numbers (required) |
array of integer | Chunk numbers to get URLs for, from 1 to total_chunks. At most 500 items. |
Response 200
Section titled “Response 200”The URLs, by chunk number.
data is UploadPartUrls.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | Direct upload is not available for this domain, the upload is not sent in chunks, part_numbers is empty, has more than 500 numbers or a number out of range, file_id is not the upload’s, or the upload is no longer active. |
“Direct upload is not available for this domain” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 410 | The upload session expired. Start a new upload. | “This upload has expired. Start it again.” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
Finish an upload
Section titled “Finish an upload”POST /api/upload-session/{session_id}/finalize
Checks the uploaded bytes against the plan and creates the file and its shared link; for multi and folder uploads it also checks the archive’s file list. Email-protected links send their invitations now. Calling it again after a lost response returns the same links; a 409 Finalizing means it is still running, so ask again in a few seconds. If the upload session was created with Dew-Act-As, send the same header here.
- Permission: Files: Upload and share (
files:write) - Act for a user: accepts the
Dew-Act-Asheader. Personal tokens work too.
Parameters
Section titled “Parameters”| Name | In | Type | Description |
|---|---|---|---|
session_id (required) |
path | string | The upload session’s ID (session_id from Start an upload). |
Request body
Section titled “Request body”Send {} when there is nothing to skip.
Content type application/json.
| Field | Type | Description |
|---|---|---|
skip_file_ids |
array of string | multi and folder: the session_file_id of files you left out of the archive’s central directory because they couldn’t be read. Their planned bytes must still be in the archive (as zeros), so its size doesn’t change. |
Response 200
Section titled “Response 200”The upload is finished.
data is UploadFinishResult.
Errors
Section titled “Errors”| Status | When | Example message |
|---|---|---|
| 400 | The body is not valid JSON, the upload is no longer active, or what was uploaded doesn’t match the plan: a different size, an archive whose files differ from the ones listed, or nothing left to deliver. In those last cases the uploaded bytes are deleted; start a new upload. | “The uploaded archive does not match the files that were registered” |
| 401 | The token is missing, wrong, revoked or expired. | |
| 403 | The token lacks the permission, or the upload session belongs to someone else. | “Not your session” |
| 404 | No upload session with this ID. | “Session not found” |
| 409 | Finalizing: an earlier call is still finishing this upload; ask again in a few seconds. Conflict: nothing was uploaded; start a new upload. |
“This upload is still being finished. Try again in a moment.” |
| 410 | The upload session expired. Start a new upload. | “This upload has expired. Start it again.” |
| 500 | Something failed on the server. error is an error code and ref a reference to quote to your admin, who can find it in the system logs. |
