Skip to main content
File uploads are handled via presigned URLs to MinIO. The API exposes two main steps:
  • POST /api/v1/initiate-upload – create an upload session and receive presigned URLs.
  • POST /api/v1/complete-upload – finalize the upload and register the file for operations.
Single-part vs multipart: For objects 5 GiB (5 × 2³⁰ bytes) or smaller, you can upload the whole file in one presigned PUT (parts: 1). For files larger than 5 GiB, you must use multipart upload: set parts to the number of chunks you will upload (each chunk is uploaded with its own presigned URL), PUT each part separately, collect each response’s ETag, then call complete-upload with every (etag, part_number) pair. This matches S3‑compatible limits: a single PUT cannot exceed 5 GiB per object part.

Initiate upload

curl -X POST https://api.dragdropdo.com/api/v1/initiate-upload \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "file_name": "document.pdf",
    "size": 1234567,
    "mime_type": "application/pdf",
    "parts": 1
  }'
import { Dragdropdo } from "dragdropdo-sdk";

const client = new Dragdropdo({
  apiKey: "your-api-key",
  baseURL: "https://api.dragdropdo.com",
});

// Initiate upload (handled automatically by uploadFile)
const uploadResult = await client.uploadFile({
  file: "/path/to/document.pdf",
  fileName: "document.pdf",
  mimeType: "application/pdf",
});

console.log("File key:", uploadResult.fileKey);
console.log("Presigned URLs:", uploadResult.presignedUrls);
from dragdropdo_sdk import Dragdropdo, D3ClientConfig

client = Dragdropdo(
    api_key="your-api-key",
    base_url="https://api.dragdropdo.com"
)

# Initiate upload (handled automatically by upload_file)
upload_result = client.upload_file(
    file="/path/to/document.pdf",
    file_name="document.pdf",
    mime_type="application/pdf"
)

print("File key:", upload_result.file_key)
print("Presigned URLs:", upload_result.presigned_urls)
use DragdropdoSdk\Dragdropdo;

$client = new Dragdropdo([
    'api_key' => 'your-api-key',
    'base_url' => 'https://api.dragdropdo.com',
]);

// Initiate upload (handled automatically by uploadFile)
$uploadResult = $client->uploadFile([
    'file' => '/path/to/document.pdf',
    'file_name' => 'document.pdf',
    'mime_type' => 'application/pdf',
]);

echo "File key: " . $uploadResult['file_key'] . "\n";
echo "Presigned URLs: " . implode(', ', $uploadResult['presigned_urls']) . "\n";
require 'dragdropdo_sdk'

client = DragdropdoSdk::Dragdropdo.new(
  api_key: 'your-api-key',
  base_url: 'https://api.dragdropdo.com'
)

# Initiate upload (handled automatically by upload_file)
upload_result = client.upload_file(
  file: '/path/to/document.pdf',
  file_name: 'document.pdf',
  mime_type: 'application/pdf'
)

puts "File key: #{upload_result[:file_key]}"
puts "Presigned URLs: #{upload_result[:presigned_urls]}"
import "github.com/d3/dragdropdo-sdk-go"

client, _ := d3.NewDragdropdo(d3.Config{
    APIKey:  "your-api-key",
    BaseURL: "https://api.dragdropdo.com",
});

// Initiate upload (handled automatically by UploadFile)
uploadResult, _ := client.UploadFile(d3.UploadFileOptions{
    File:     "/path/to/document.pdf",
    FileName: "document.pdf",
    MimeType: "application/pdf",
});

fmt.Printf("File key: %s\n", uploadResult.FileKey)
fmt.Printf("Presigned URLs: %v\n", uploadResult.PresignedURLs)
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;

Dragdropdo client = new Dragdropdo(
    new DragdropdoConfig("your-api-key")
        .setBaseUrl("https://api.dragdropdo.com")
);

// Initiate upload (handled automatically by uploadFile)
UploadResponse uploadResult = client.uploadFile(
    new UploadFileOptions()
        .setFile("/path/to/document.pdf")
        .setFileName("document.pdf")
        .setMimeType("application/pdf")
);

System.out.println("File key: " + uploadResult.getFileKey());
System.out.println("Presigned URLs: " + uploadResult.getPresignedUrls());
  • file_name (required) – original file name including extension.
  • size (required) – file size in bytes.
  • mime_type (required) – MIME type (e.g. application/pdf).
  • parts (optional) – number of multipart parts; defaults to 1 when omitted or zero. Use 1 for files ≤ 5 GiB. For > 5 GiB, set parts to how many pieces you will upload (see Multipart upload example).
Response (ExternalUploadResponse):
{
  "file_key": "file_key_123",
  "object_name": "external/...",
  "upload_id": "upload-id",
  "presigned_urls": [
    "https://minio.example.com/bucket/object?X-Amz-Signature=..."
  ]
}
  • file_key – stable key to reference this file in later operations.
  • object_name – internal object name in MinIO.
  • upload_id – underlying multipart upload id (if applicable).
  • presigned_urls – list of PUT URLs to upload file parts.

Upload file contents

Use the provided presigned_urls to upload file data directly to MinIO. For single‑part uploads there will be exactly one URL. For multipart uploads there is one URL per part, in order (index 0part_number 1, and so on).

Getting ETag values for complete-upload

After each successful presigned PUT:
  1. Read the ETag HTTP response header (not the request body).
  2. Remove surrounding double quotes if present (S3 and MinIO often return "abc123...").
  3. Send that string as parts[i].etag with part_number equal to the 1‑based part index (first URL → 1, second → 2, …).
If you omit a part or scramble part_number order, complete-upload can fail when the storage backend completes the multipart upload.
# 1) Upload file to presigned URL (-i prints response headers; copy ETag for step 2)
curl -i -X PUT "https://minio.example.com/bucket/object?X-Amz-Signature=..." \
  -H "Content-Type: application/pdf" \
  --data-binary "@document.pdf"

# 2) Complete upload — use file_key, object_name, upload_id from initiate-upload,
#    and etag / part_number from the PUT response (strip " quotes from ETag if present)
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "file_key": "file_key_123",
    "object_name": "external/...",
    "upload_id": "upload-id",
    "parts": [
      { "etag": "etag-from-put-response", "part_number": 1 }
    ]
  }'
import axios from "axios";
import * as fs from "fs";

// Upload to presigned URL
await axios.put(presignedUrl, fs.createReadStream("./document.pdf"), {
  headers: {
    "Content-Type": "application/pdf",
  },
});

// Note: The SDK handles this automatically when using uploadFile()
import requests

# Upload to presigned URL
with open("./document.pdf", "rb") as f:
    requests.put(presigned_url, data=f, headers={"Content-Type": "application/pdf"})

# Note: The SDK handles this automatically when using upload_file()
// Upload to presigned URL
$fileContent = file_get_contents("./document.pdf");
$ch = curl_init($presignedUrl);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/pdf"]);
curl_setopt($ch, CURLOPT_INFILE, fopen("./document.pdf", "r"));
curl_exec($ch);
curl_close($ch);

// Note: The SDK handles this automatically when using uploadFile()
require 'net/http'
require 'uri'

# Upload to presigned URL
uri = URI(presigned_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Put.new(uri.request_uri)
request['Content-Type'] = 'application/pdf'
request.body = File.read('./document.pdf')
http.request(request)

# Note: The SDK handles this automatically when using upload_file()
import (
    "net/http"
    "os"
)

// Upload to presigned URL
file, _ := os.Open("./document.pdf")
defer file.Close()

req, _ := http.NewRequest("PUT", presignedURL, file)
req.Header.Set("Content-Type", "application/pdf")
http.DefaultClient.Do(req)

// Note: The SDK handles this automatically when using UploadFile()
import okhttp3.*;
import java.io.File;

// Upload to presigned URL
File file = new File("./document.pdf");
RequestBody requestBody = RequestBody.create(file, MediaType.parse("application/pdf"));
Request request = new Request.Builder()
    .url(presignedUrl)
    .put(requestBody)
    .build();
OkHttpClient client = new OkHttpClient();
client.newCall(request).execute();

// Note: The SDK handles this automatically when using uploadFile()

Multipart upload example (files larger than 5 GiB)

Why multipart: S3‑compatible storage allows at most 5 GiB per single PUT. Objects larger than 5 GiB must be split into multiple parts: each part is uploaded with its own presigned URL, and you pass every part’s ETag into complete-upload. Flow:
  1. Decide how many parts N you need (each uploaded part’s body must be ≤ 5 GiB; size the ranges so the whole file is covered).
  2. POST /api/v1/initiate-upload with parts: N, size: total file size in bytes, plus file_name and mime_type. The response includes N entries in presigned_urls (in order: part 1, part 2, …).
  3. For each index i from 0 to N - 1, PUT the corresponding byte range of your file to presigned_urls[i]. After each successful PUT, read ETag from the response headers and strip double quotes (see Getting ETag values for complete-upload).
  4. POST /api/v1/complete-upload with parts listing all N parts, each with the correct part_number (1N) matching the presigned URL order.
Example: three parts — initiate with "parts": 3, upload three ranges, then complete with three { "etag", "part_number" } objects.
# 0) Assume archive.tar is > 5 GiB — split into 3 PUTs (ranges are illustrative; use split/dd or your app)
# 1) Initiate — parts must match how many presigned URLs you will use
curl -X POST https://api.dragdropdo.com/api/v1/initiate-upload \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "file_name": "archive.tar",
    "size": 6442450944,
    "mime_type": "application/x-tar",
    "parts": 3
  }'

# Save file_key, object_name, upload_id, presigned_urls[0..2] from JSON.

# 2) PUT each part — capture ETag from each response (-D writes headers to stdout; trim quotes for JSON)
curl -i -X PUT "$PRESIGNED_URL_PART_1" -H "Content-Type: application/x-tar" --data-binary "@part1.bin"
curl -i -X PUT "$PRESIGNED_URL_PART_2" -H "Content-Type: application/x-tar" --data-binary "@part2.bin"
curl -i -X PUT "$PRESIGNED_URL_PART_3" -H "Content-Type: application/x-tar" --data-binary "@part3.bin"

# 3) Complete — one entry per part; part_number matches URL order (1 = first URL)
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "file_key": "file_key_123",
    "object_name": "external/...",
    "upload_id": "upload-id",
    "parts": [
      { "etag": "etag-from-put-part-1", "part_number": 1 },
      { "etag": "etag-from-put-part-2", "part_number": 2 },
      { "etag": "etag-from-put-part-3", "part_number": 3 }
    ]
  }'
In application code, the pattern is the same as the official SDKs: loop over presigned_urls, PUT each chunk, push { etag: strippedETag, part_number: i + 1 }, then POST the array to complete-upload.

Complete upload

POST /api/v1/complete-upload finalizes the multipart upload in storage and registers the file so it can be referenced by file_key in /api/v1/do and related APIs. Call it only after every part has been uploaded successfully via the presigned PUT URLs. Authentication: same as other Business API routes — Authorization: Bearer <your-api-key> and Content-Type: application/json.

Request body

JSON body (CompleteUploadRequest):
{
  "file_key": "file_key_123",
  "object_name": "external/...",
  "upload_id": "upload-id-from-initiate-response",
  "parts": [
    { "etag": "hex-or-quoted-etag-from-put-response", "part_number": 1 }
  ]
}
  • file_key (required) – from initiate-upload response.
  • object_name (required) – from initiate-upload response.
  • upload_id (required) – from initiate-upload response.
  • parts (required) – one entry per uploaded part, in order:
    • etag (required) – value of the ETag header from the corresponding presigned PUT response. If the header includes double quotes (common for S3‑compatible storage), strip them before sending.
    • part_number (required) – 1‑based index matching the part and presigned URL order from initiate upload.
For multipart uploads, include every part; for a single‑part upload, parts is a one‑element array.

Response

On success, HTTP 200 with a JSON body (CompleteUploadResponse):
{
  "message": "Upload completed successfully. ETag: \"d41d8cd98f00b204e9800998ecf8427e\"",
  "file_key": "file_key_123"
}
  • message – status text from the server (typically includes the stored object ETag after a successful complete). If the upload was already finalized, the message may instead be a short confirmation such as file upload completed.
  • file_key – echoes the key you sent; use this key in operations.
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "file_key": "file_key_123",
    "upload_id": "upload-id",
    "object_name": "external/...",
    "parts": [
      { "etag": "etag-part-1", "part_number": 1 }
    ]
  }'
import { Dragdropdo } from "dragdropdo-sdk";

const client = new Dragdropdo({
  apiKey: "your-api-key",
  baseURL: "https://api.dragdropdo.com",
});

// uploadFile() runs initiate-upload → presigned PUT(s) → complete-upload for you.
// For a custom pipeline, POST /api/v1/complete-upload with the JSON body above (e.g. axios.post).
from dragdropdo_sdk import Dragdropdo, D3ClientConfig

client = Dragdropdo(
    api_key="your-api-key",
    base_url="https://api.dragdropdo.com"
)

# Note: upload_file() automatically completes the upload
# All steps are handled automatically by the SDK
use DragdropdoSdk\Dragdropdo;

$client = new Dragdropdo([
    'api_key' => 'your-api-key',
    'base_url' => 'https://api.dragdropdo.com',
]);

// Note: uploadFile() automatically completes the upload
// All steps are handled automatically by the SDK
require 'dragdropdo_sdk'

client = DragdropdoSdk::Dragdropdo.new(
  api_key: 'your-api-key',
  base_url: 'https://api.dragdropdo.com'
)

# Note: upload_file() automatically completes the upload
# All steps are handled automatically by the SDK
import "github.com/d3/dragdropdo-sdk-go"

client, _ := d3.NewDragdropdo(d3.Config{
    APIKey:  "your-api-key",
    BaseURL: "https://api.dragdropdo.com",
});

// Note: UploadFile() automatically completes the upload
// All steps are handled automatically by the SDK
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;

Dragdropdo client = new Dragdropdo(
    new DragdropdoConfig("your-api-key")
        .setBaseUrl("https://api.dragdropdo.com")
);

// Note: uploadFile() automatically completes the upload
// All steps are handled automatically by the SDK
On success, the server persists metadata and the file becomes available for /api/v1/do operations.