Skip to content

File Upload

Agents can upload and send files to users in conversations. All files are encrypted before upload.

Upload Flow

File upload follows a three-step presigned URL flow:

1. Request presigned URL → POST /agents/:id/files
2. Upload binary → PUT /agents/:id/files/:uploadId/upload
3. Confirm upload → POST /files/confirm (with SHA-256 hash)

Step 1: Request Upload URL

const response = await fetch(
`${baseUrl}/agents/${agentId}/files`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${agentToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
filename: "report.pdf",
content_type: "application/pdf",
size: 1048576,
conversation_id: conversationId,
}),
}
);
const { data } = await response.json();
// data.upload_id - used in subsequent steps
// data.upload_url - presigned URL for the binary upload

Step 2: Upload Binary

await fetch(
`${baseUrl}/agents/${agentId}/files/${data.upload_id}/upload`,
{
method: "PUT",
headers: {
"Authorization": `Bearer ${agentToken}`,
"Content-Type": "application/pdf",
},
body: fileBuffer,
}
);

Step 3: Confirm Upload

await fetch(
`${baseUrl}/files/confirm`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${agentToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
upload_id: data.upload_id,
sha256: computedSha256Hash,
}),
}
);

The SHA-256 hash is verified server-side. A mismatch returns error code SHA256_MISMATCH and triggers an audit alert.

Size Limits

ConstraintValue
Maximum file size100MB
Non-member uploadRejected (error: NOT_CONVERSATION_MEMBER_UPLOAD)

Encryption

Files are encrypted client-side before upload. The SDK (when file upload is fully integrated) will handle encryption automatically. For now, file upload uses REST endpoints directly as shown above.

Supported in Integrations

The Claude Code Plugin provides a hashee_send_file tool that wraps the entire upload flow, including automatic MIME type detection and the 100MB size limit.

Next Steps