Upload a file through an upload link
/file-upload/{id}
The credential-less half of `elaichi__file__upload_link`, for an AI app that can run `curl` on the machine that holds the file. The app names the file first (`filename`, `size_bytes`, `sha256`) and is handed this address, the headers to send and a ready `curl` command; the link is bound to exactly that file. There is no session or API token: the secret is the `X-Upload-Token` header (64 hex characters; in a header, never the path, so it is not in request logs). The link works ONCE and for 10 minutes. `Content-Length` is required and must equal the declared `size_bytes` (`411` `upload_length_required`, `413` `upload_too_large`, `400` `upload_length_mismatch`; none of these spends the link). The body is streamed into storage and hashed on the same pass. If the sha256 is not the declared one the object is deleted, the link is spent and the answer is `409` `upload_hash_mismatch`. Every other refusal (no such link, wrong secret, already used, expired, organization closed, member gone) is the SAME `404` `upload_link_invalid`, so a probe learns nothing. On success the file is the link owner’s own (origin `mcp_link`, kept 7 days) and `201` names it. The addresses the link was requested from and the PUT came from are recorded on the audit rows (`file.upload_link_issued`, `file.uploaded`) and a difference is flagged, never refused: a sandbox’s egress address is not the AI app’s.
Path Parameters
The file id (file_…) the link reserved.
Response Body
ready
curl -X PUT 'https://api.elaichi.ai/file-upload/<id>' \
-H 'Content-Type: application/json'const response = await fetch('https://api.elaichi.ai/file-upload/<id>', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
console.log(data);import os
import requests
url = "https://api.elaichi.ai/file-upload/<id>"
headers = {
"Content-Type": "application/json",
}
response = requests.put(url, headers=headers)
print(response.json())