Skip to content
POST /file

One request, for a backend that holds a session or an API token, and for the console’s Files screen (which uses it for **Upload**). Two forms, told apart by `X-Filename` (or `?filename=`): - **With it, the body is the file** (`Content-Type` is the declared type, `Content-Length` is required, at most 95 MiB). Streamed straight into storage with the size counted on the bytes actually read, so a header that lies is caught mid-stream and nothing is kept. `411` `upload_length_required` without a length, `413` `upload_too_large` over the limit. A raw upload of a JSON file sends `X-Filename`, so it is never taken for the second form. - **Without it, the body is JSON** `{ filename, content, encoding: utf8 | base64, mime? }` for content the client wrote itself, at most 1 MiB of decoded content (`413` `inline_content_too_large`; the body is never buffered past that, whatever it declares). Either way the answer is `201 { file }`, the file as `GET /file/{id}` reads it, owned by the caller and kept for 7 days. The stored type is read from the bytes, never taken from the declaration. `origin` is `upload` for a person’s session, `api` for a token. Audited as `file.uploaded`.

Query Parameters

filenamestring

Same as X-Filename, for a client that cannot set headers.

Request Body

contentstring
encodingstring
Possible values:
utf8base64
filenamestring
mimestring

Response Body

fileobject
accessstring,null
Possible values:
ownerviewnull
can_deleteboolean
can_openboolean
can_revoke_shareboolean
can_see_sharesboolean
can_shareboolean
expires_atstring · date-time
file_idstring
filenamestring
max_bytesinteger,null

The link’s size limit while pending.

mimestring,null
originstring

How the bytes got here: tool (a connector tool returned them), upload (a person chose it on the Files screen), request (a person handed it to an app that asked, through an upload request; origin_host is the app), inline (content an assistant wrote with file.create), api (a backend sent it to POST /file with a token), url_import, web_fetch (fetched from the web), export (produced by an export). Provenance for wording (“Uploaded by Dana”), never an access gate.

Possible values:
tooluploadrequestinlineapiurl_importweb_fetchexport
origin_hoststring,null

The web host an import or web fetch read from, or the asking app’s name for origin request; null otherwise.

size_bytesinteger,null
statusstring

pending: an upload link you made is waiting for its bytes (not a file yet: no type, size or URL). ready: a file you can open. expired: your link ran out unused, or the file it filled is gone.

Possible values:
pendingreadyexpired
urlstring,null

The console download page, for a ready file.

curl -X POST 'https://api.elaichi.ai/file' \
  -H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"filename":"your_filename","content":"your_content","encoding":"utf8","mime":"your_mime"}'
const body = {
  "filename": "your_filename",
  "content": "your_content",
  "encoding": "utf8",
  "mime": "your_mime"
};

const response = await fetch('https://api.elaichi.ai/file', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(body),
});

const data = await response.json();
console.log(data);
import os
import requests

url = "https://api.elaichi.ai/file"
headers = {
    "Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
    "Content-Type": "application/json",
}
payload = {
    "filename": "your_filename",
    "content": "your_content",
    "encoding": "utf8",
    "mime": "your_mime"
}

response = requests.post(url, headers=headers, json=payload)
print(response.json())