Presigned Upload
curl --request POST \
--url https://api.agent-drop.com/v1/transfers/presigned \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"mode": "<string>",
"sender": "<string>",
"recipient": "<string>",
"files": [
{}
],
"expires_in": "<string>",
"max_downloads": 123,
"message": "<string>",
"metadata": {},
"is_encrypted": true,
"encrypted_key": "<string>",
"encryption_algorithm": "<string>",
"auto_delete": true
}
'import requests
url = "https://api.agent-drop.com/v1/transfers/presigned"
payload = {
"mode": "<string>",
"sender": "<string>",
"recipient": "<string>",
"files": [{}],
"expires_in": "<string>",
"max_downloads": 123,
"message": "<string>",
"metadata": {},
"is_encrypted": True,
"encrypted_key": "<string>",
"encryption_algorithm": "<string>",
"auto_delete": True
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({
mode: '<string>',
sender: '<string>',
recipient: '<string>',
files: [{}],
expires_in: '<string>',
max_downloads: 123,
message: '<string>',
metadata: {},
is_encrypted: true,
encrypted_key: '<string>',
encryption_algorithm: '<string>',
auto_delete: true
})
};
fetch('https://api.agent-drop.com/v1/transfers/presigned', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.agent-drop.com/v1/transfers/presigned",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'mode' => '<string>',
'sender' => '<string>',
'recipient' => '<string>',
'files' => [
[
]
],
'expires_in' => '<string>',
'max_downloads' => 123,
'message' => '<string>',
'metadata' => [
],
'is_encrypted' => true,
'encrypted_key' => '<string>',
'encryption_algorithm' => '<string>',
'auto_delete' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.agent-drop.com/v1/transfers/presigned"
payload := strings.NewReader("{\n \"mode\": \"<string>\",\n \"sender\": \"<string>\",\n \"recipient\": \"<string>\",\n \"files\": [\n {}\n ],\n \"expires_in\": \"<string>\",\n \"max_downloads\": 123,\n \"message\": \"<string>\",\n \"metadata\": {},\n \"is_encrypted\": true,\n \"encrypted_key\": \"<string>\",\n \"encryption_algorithm\": \"<string>\",\n \"auto_delete\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.agent-drop.com/v1/transfers/presigned")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"mode\": \"<string>\",\n \"sender\": \"<string>\",\n \"recipient\": \"<string>\",\n \"files\": [\n {}\n ],\n \"expires_in\": \"<string>\",\n \"max_downloads\": 123,\n \"message\": \"<string>\",\n \"metadata\": {},\n \"is_encrypted\": true,\n \"encrypted_key\": \"<string>\",\n \"encryption_algorithm\": \"<string>\",\n \"auto_delete\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.agent-drop.com/v1/transfers/presigned")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"mode\": \"<string>\",\n \"sender\": \"<string>\",\n \"recipient\": \"<string>\",\n \"files\": [\n {}\n ],\n \"expires_in\": \"<string>\",\n \"max_downloads\": 123,\n \"message\": \"<string>\",\n \"metadata\": {},\n \"is_encrypted\": true,\n \"encrypted_key\": \"<string>\",\n \"encryption_algorithm\": \"<string>\",\n \"auto_delete\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"upload_urls": [
{}
],
"upload_urls[].file_id": "<string>",
"upload_urls[].file_name": "<string>",
"upload_urls[].upload_url": "<string>",
"upload_urls[].method": "<string>",
"upload_urls[].headers": {},
"confirm_url": "<string>",
"expires_at": "<string>"
}Transfers
Presigned Upload: Get S3 Upload URL for a Transfer
Get presigned R2 URLs for client-side AgentDrop file upload. Bypasses the API server for large transfers; pair with confirm-transfer to flip status to active.
POST
/
v1
/
transfers
/
presigned
Presigned Upload
curl --request POST \
--url https://api.agent-drop.com/v1/transfers/presigned \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"mode": "<string>",
"sender": "<string>",
"recipient": "<string>",
"files": [
{}
],
"expires_in": "<string>",
"max_downloads": 123,
"message": "<string>",
"metadata": {},
"is_encrypted": true,
"encrypted_key": "<string>",
"encryption_algorithm": "<string>",
"auto_delete": true
}
'import requests
url = "https://api.agent-drop.com/v1/transfers/presigned"
payload = {
"mode": "<string>",
"sender": "<string>",
"recipient": "<string>",
"files": [{}],
"expires_in": "<string>",
"max_downloads": 123,
"message": "<string>",
"metadata": {},
"is_encrypted": True,
"encrypted_key": "<string>",
"encryption_algorithm": "<string>",
"auto_delete": True
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({
mode: '<string>',
sender: '<string>',
recipient: '<string>',
files: [{}],
expires_in: '<string>',
max_downloads: 123,
message: '<string>',
metadata: {},
is_encrypted: true,
encrypted_key: '<string>',
encryption_algorithm: '<string>',
auto_delete: true
})
};
fetch('https://api.agent-drop.com/v1/transfers/presigned', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.agent-drop.com/v1/transfers/presigned",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'mode' => '<string>',
'sender' => '<string>',
'recipient' => '<string>',
'files' => [
[
]
],
'expires_in' => '<string>',
'max_downloads' => 123,
'message' => '<string>',
'metadata' => [
],
'is_encrypted' => true,
'encrypted_key' => '<string>',
'encryption_algorithm' => '<string>',
'auto_delete' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.agent-drop.com/v1/transfers/presigned"
payload := strings.NewReader("{\n \"mode\": \"<string>\",\n \"sender\": \"<string>\",\n \"recipient\": \"<string>\",\n \"files\": [\n {}\n ],\n \"expires_in\": \"<string>\",\n \"max_downloads\": 123,\n \"message\": \"<string>\",\n \"metadata\": {},\n \"is_encrypted\": true,\n \"encrypted_key\": \"<string>\",\n \"encryption_algorithm\": \"<string>\",\n \"auto_delete\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.agent-drop.com/v1/transfers/presigned")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"mode\": \"<string>\",\n \"sender\": \"<string>\",\n \"recipient\": \"<string>\",\n \"files\": [\n {}\n ],\n \"expires_in\": \"<string>\",\n \"max_downloads\": 123,\n \"message\": \"<string>\",\n \"metadata\": {},\n \"is_encrypted\": true,\n \"encrypted_key\": \"<string>\",\n \"encryption_algorithm\": \"<string>\",\n \"auto_delete\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.agent-drop.com/v1/transfers/presigned")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"mode\": \"<string>\",\n \"sender\": \"<string>\",\n \"recipient\": \"<string>\",\n \"files\": [\n {}\n ],\n \"expires_in\": \"<string>\",\n \"max_downloads\": 123,\n \"message\": \"<string>\",\n \"metadata\": {},\n \"is_encrypted\": true,\n \"encrypted_key\": \"<string>\",\n \"encryption_algorithm\": \"<string>\",\n \"auto_delete\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"upload_urls": [
{}
],
"upload_urls[].file_id": "<string>",
"upload_urls[].file_name": "<string>",
"upload_urls[].upload_url": "<string>",
"upload_urls[].method": "<string>",
"upload_urls[].headers": {},
"confirm_url": "<string>",
"expires_at": "<string>"
}Get presigned URLs for uploading files directly to storage. Use this for browser uploads, large files, or when you want to upload files from the client side without routing them through your backend.
After uploading all files to the presigned URLs, you must call confirm-transfer to activate the transfer. The transfer stays in
pending status until confirmed.Request
Headers
string
required
Bearer token. Example:
Bearer agd_live_xxxxxxxxxxxxxxxxxxxxstring
required
Must be
application/jsonBody Parameters
string
required
Transfer mode. One of:
agent-to-agent, agent-to-human, human-to-agent.string
required
Identifier for the sending agent or human.
string
required
Identifier for the intended recipient agent or human.
array
required
Array of file descriptor objects. Each object must contain
name, size, and content_type. Optionally include encryption_iv if the file is client-side encrypted.string
default:"24h"
How long the transfer stays active. Examples:
1h, 12h, 24h, 7d, 30d.integer
default:"10"
Maximum number of times the transfer can be downloaded.
string
Optional message to attach to the transfer.
object
Optional metadata object for custom key-value pairs.
boolean
default:"false"
Whether the files are client-side encrypted before upload.
string
Base64-encoded encrypted symmetric key. Required when
is_encrypted is true.string
default:"AES-256-GCM"
Encryption algorithm used. Currently only
AES-256-GCM is supported.boolean
default:"false"
Whether to automatically delete the transfer after it expires.
Response
string
The transfer ID. Use this when calling the confirm endpoint.
array
Array of presigned upload objects, one per file.
string
Unique ID for this file within the transfer.
string
The file name you specified.
string
Presigned URL to upload the file to.
string
HTTP method to use. Typically
PUT.object
Headers to include in the upload request.
string
URL to call after all files are uploaded.
string
ISO 8601 timestamp when the presigned URLs expire.
Examples
curl -X POST https://api.agent-drop.com/v1/transfers/presigned \
-H "Authorization: Bearer agd_live_xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"mode": "agent-to-agent",
"sender": "data-pipeline",
"recipient": "analysis-agent",
"files": [
{ "name": "report.pdf", "size": 1048576, "content_type": "application/pdf" },
{ "name": "data.csv", "size": 245760, "content_type": "text/csv" }
],
"expires_in": "12h",
"max_downloads": 3,
"message": "Weekly metrics"
}'
import requests
response = requests.post(
"https://api.agent-drop.com/v1/transfers/presigned",
headers={
"Authorization": "Bearer agd_live_xxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
},
json={
"mode": "agent-to-agent",
"sender": "data-pipeline",
"recipient": "analysis-agent",
"files": [
{"name": "report.pdf", "size": 1048576, "content_type": "application/pdf"},
{"name": "data.csv", "size": 245760, "content_type": "text/csv"},
],
"expires_in": "12h",
"max_downloads": 3,
"message": "Weekly metrics",
},
)
result = response.json()
# Upload each file to its presigned URL
for url_info in result["upload_urls"]:
with open(url_info["file_name"], "rb") as f:
requests.put(url_info["upload_url"], data=f, headers=url_info["headers"])
# Confirm the transfer
requests.post(result["confirm_url"], headers={"Authorization": "Bearer agd_live_xxxxxxxxxxxxxxxxxxxx"})
const response = await fetch(
"https://api.agent-drop.com/v1/transfers/presigned",
{
method: "POST",
headers: {
Authorization: "Bearer agd_live_xxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
mode: "agent-to-agent",
sender: "data-pipeline",
recipient: "analysis-agent",
files: [
{ name: "report.pdf", size: 1048576, content_type: "application/pdf" },
{ name: "data.csv", size: 245760, content_type: "text/csv" },
],
expires_in: "12h",
max_downloads: 3,
message: "Weekly metrics",
}),
}
);
const result = await response.json();
// Upload each file to its presigned URL
for (const urlInfo of result.upload_urls) {
const fileData = fs.readFileSync(urlInfo.file_name);
await fetch(urlInfo.upload_url, {
method: urlInfo.method,
headers: urlInfo.headers,
body: fileData,
});
}
// Confirm the transfer
await fetch(result.confirm_url, {
method: "POST",
headers: { Authorization: "Bearer agd_live_xxxxxxxxxxxxxxxxxxxx" },
});
Response
{
"id": "tr_4d2e8f1a-6b3c-4a7e-9d0f-5e8c1b2a3d4e",
"upload_urls": [
{
"file_id": "file_a1b2c3d4",
"file_name": "report.pdf",
"upload_url": "https://storage.agent-drop.com/uploads/tr_4d2e8f1a.../report.pdf?X-Amz-Signature=...",
"method": "PUT",
"headers": { "Content-Type": "application/pdf" }
},
{
"file_id": "file_e5f6g7h8",
"file_name": "data.csv",
"upload_url": "https://storage.agent-drop.com/uploads/tr_4d2e8f1a.../data.csv?X-Amz-Signature=...",
"method": "PUT",
"headers": { "Content-Type": "text/csv" }
}
],
"confirm_url": "https://api.agent-drop.com/v1/transfers/tr_4d2e8f1a-6b3c-4a7e-9d0f-5e8c1b2a3d4e/confirm",
"expires_at": "2026-03-28T13:00:00Z"
}
Errors
| Status | Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | Missing required fields or invalid values |
401 | UNAUTHORIZED | Invalid or missing API key |
413 | FILE_TOO_LARGE | File size exceeds your plan’s limit |
429 | RATE_LIMITED | Too many requests. Back off and retry. |
⌘I
