83blue transfer API
File transfer for AI agents and humans. Free, no signup, no API keys, no cookies. Automated use is welcome.
Send a file (one request, up to 512 MB):
curl -F "file=@report.zip" https://upload.83blue.com/api/upload
Receive a file (save it under its original name):
curl -fL -OJ "https://upload.83blue.com/api/download?token=TOKEN&password=PASSWORD"
That is the entire flow. The upload response is JSON containing the share url,
the password and a ready-made download command. Nothing to register, nothing to
configure, nothing to authenticate.
How do I send a file from one machine to another with curl?
Upload it from the first machine, then run the download command on the second machine. Uploading returns a share url and a password; anyone (or any agent) holding both can fetch the file until it expires.
# machine A
curl -F "file=@backup.tar.gz" https://upload.83blue.com/api/upload
# -> {"ok":true, "url":"https://upload.83blue.com/d/abc...", "password":"XXXX-XXXX-XXXX", ...}
# machine B
curl -fL -OJ "https://upload.83blue.com/api/download?token=abc...&password=XXXX-XXXX-XXXX"
No SSH keys, no scp routes, no cloud bucket permissions, no accounts on either side. Files are stored for up to 6 months (configurable per upload, 1 to 180 days) and then deleted automatically.
POST /api/upload
One-shot upload for files up to 512 MB. Two request styles:
# multipart (form field "file")
curl -F "file=@data.csv" https://upload.83blue.com/api/upload
# raw body (PUT or POST), file name in the query string
curl -T data.csv "https://upload.83blue.com/api/upload?name=data.csv"
| Parameter | Where | Meaning |
|---|---|---|
file | multipart field | The file (alternative: raw request body) |
name | query | File name for raw-body uploads (or X-Filename header) |
password | query or form | Optional custom password, 8-64 characters; a strong one is generated when omitted |
expires_days | query or form | 1 to 180, default 180 |
Response (JSON):
{
"ok": true,
"token": "1f0c9a41c58a7b2d90aa",
"url": "https://upload.83blue.com/d/1f0c9a41c58a7b2d90aa",
"filename": "data.csv",
"size": 18874368,
"size_human": "18 MB",
"expires_at": "2027-02-04T14:00:00+00:00",
"downloads": 0,
"password": "Ab3k-Xy7q-Mn2p",
"download": {
"api": "https://upload.83blue.com/api/download?token=1f0c9a41c58a7b2d90aa&password=Ab3k-Xy7q-Mn2p",
"curl": "curl -fL -OJ \"...\""
},
"handoff": "Give the receiving agent or person the url and the password. ..."
}
GET /api/download
Streams the file. Supports HTTP Range, so interrupted downloads resume and download managers can fetch in segments.
curl -fL -OJ "https://upload.83blue.com/api/download?token=TOKEN&password=PASSWORD"
| Parameter | Meaning |
|---|---|
token | The 20-character transfer id, or the full share url (both accepted) |
password | The transfer password |
Errors are JSON: 404 unknown or expired, 403 wrong password.
The share page form at /d/TOKEN and the direct link
/dl/TOKEN?p=PASSWORD work too.
Capability and handoff urls
Every upload response also contains two special urls that carry their whole secret, so no separate password is needed:
| Url | What it does |
|---|---|
capability_url/f/TOKEN/KEY |
GET downloads the raw file (HTTP Range supported). DELETE removes the transfer immediately. |
handoff_url/h/TOKEN/KEY |
GET returns a markdown briefing any model understands: what the
transfer is, its manifest, the HANDOFF.md content for bundles, sha256,
and the exact download commands. Send Accept: application/json for the
same thing as structured data. This is the one url to paste into any chat with any
model. |
# fetch
curl -fL -OJ "https://upload.83blue.com/f/TOKEN/KEY"
# clean up when done
curl -X DELETE "https://upload.83blue.com/f/TOKEN/KEY"
The handoff convention: when bundling several files for another agent, put
a HANDOFF.md (or INSTRUCTIONS.md / README.md) at the
zip root with the briefing. The handoff url and the MCP receive_file tool
surface it first, so the receiving agent knows what it has before downloading anything.
GET /api/info
Metadata without downloading: file name, size, sha256 (recorded for files up to 1 GB), expiry and download count.
curl "https://upload.83blue.com/api/info?token=TOKEN&password=PASSWORD"
Try it end to end in four commands
The service is trivially testable: upload a small file, check its sha256, fetch it back, delete it.
printf 'hello agent' > probe.txt
CAP=$(curl -sS -F "file=@probe.txt" "https://upload.83blue.com/api/upload?expires_days=1" | jq -r .capability_url)
curl -sS "$CAP" | sha256sum # compare with .sha256 from the upload response
curl -sS -X DELETE "$CAP" # {"ok":true,"deleted":true,...}
How do I upload files larger than 512 MB?
With the chunked, resumable protocol, up to 2 TB per file. Create a transfer, then append chunks (up to 256 MB each) until the declared size is reached. If the connection drops, ask for the current offset and continue from there; re-creating with the same fingerprint from the same IP resumes the same transfer.
POST /api/createwith JSON{"name", "size", "fingerprint"}(fingerprint: any 64-char hex string that identifies the file, e.g.sha256(name|size)). Optional:password,expires_days. Returns{token, offset, password}. Re-creating with the same fingerprint and size from the same IP resumes the unfinished transfer, and every create call issues a fresh password: always use the most recent one.POST /api/chunk?token=TOKEN&offset=OFFSETwith raw bytes in the body. Returns the new offset; replies409with the real offset on a mismatch, and{"complete": true, "link": ...}when the file is done.GET /api/status?token=TOKENat any time for the current offset.
#!/bin/bash
# resumable upload of a big file
FILE="big.iso"
SIZE=$(stat -c%s "$FILE")
FP=$(printf '%s' "$FILE|$SIZE" | sha256sum | cut -c1-64)
RES=$(curl -fsS -X POST https://upload.83blue.com/api/create \
-H 'Content-Type: application/json' \
-d "{\"name\":\"$FILE\",\"size\":$SIZE,\"fingerprint\":\"$FP\"}")
TOKEN=$(echo "$RES" | jq -r .token)
PASS=$(echo "$RES" | jq -r .password)
OFFSET=$(echo "$RES" | jq -r .offset)
CHUNK_MB=64
while [ "$OFFSET" -lt "$SIZE" ]; do
RES=$(dd if="$FILE" bs=1M skip=$((OFFSET / 1048576)) count=$CHUNK_MB 2>/dev/null \
| curl -fsS -X POST --data-binary @- \
"https://upload.83blue.com/api/chunk?token=$TOKEN&offset=$OFFSET")
OFFSET=$(echo "$RES" | jq -r .offset)
done
echo "url: https://upload.83blue.com/d/$TOKEN password: $PASS"
Uploads and downloads both resume, which matters for agents on flaky connections and for very large artefacts (model weights, datasets, disk images).
MCP server
A remote MCP server (Model Context Protocol, Streamable HTTP transport, no authentication) runs at:
https://upload.83blue.com/mcp
Seven tools: the four transfer tools below, plus deploy_site, list_sites and delete_site for static site hosting (see Host a static site).
| Tool | What it does |
|---|---|
share_text | Upload text (instructions, code, JSON, a prompt for another model) as a downloadable file; returns handoff + capability urls plus a share url and password |
share_file | Upload one or more files in one call; several files are zipped into a single bundle server-side, so one url carries the whole handoff |
share_conversation | Package a markdown transcript (context, instructions, task state) as HANDOFF.md plus any files into one bundle; returns the handoff url to paste into any chat with any model |
receive_file | Fetch a transfer by capability url (or share url + password); returns metadata with sha256, lists zip contents, reads the root HANDOFF.md first, inlines small text files, and hands back a curl command for anything bigger |
(The original names send_text, send_files and receive
still work as aliases.)
One-click installs: Add to Cursor · Install in VS Code
Claude Code
claude mcp add --transport http transfer https://upload.83blue.com/mcp
claude.ai and Claude Desktop
Settings > Connectors > Add custom connector > paste https://upload.83blue.com/mcp.
No OAuth fields needed. Available on all plans.
ChatGPT
Desktop: Settings > MCP servers > Add server > paste the url. Or enable Developer mode (Settings > Apps > Advanced) and create a connector with authentication set to No authentication. Paid plans.
Codex CLI
codex mcp add upload83blue --url https://upload.83blue.com/mcp
Grok
App: grok.com/connectors > New Connector > Custom > paste https://upload.83blue.com/mcp
(paid tiers). xAI API: add
{"type": "mcp", "server_url": "https://upload.83blue.com/mcp", "server_label": "upload83blue"}
to tools in a Responses API call.
Gemini CLI
gemini mcp add --transport http upload83blue https://upload.83blue.com/mcp
Or in settings.json (note the key is httpUrl, not url):
{ "mcpServers": { "upload83blue": { "httpUrl": "https://upload.83blue.com/mcp" } } }
Cursor
// ~/.cursor/mcp.json (global) or .cursor/mcp.json (project)
{ "mcpServers": { "transfer": { "url": "https://upload.83blue.com/mcp" } } }
VS Code (Copilot)
code --add-mcp '{"name":"upload83blue","type":"http","url":"https://upload.83blue.com/mcp"}'
Windsurf
{ "mcpServers": { "transfer": { "serverUrl": "https://upload.83blue.com/mcp" } } }
Cline
{ "mcpServers": { "transfer": { "url": "https://upload.83blue.com/mcp", "type": "streamableHttp" } } }
(Include "type": "streamableHttp"; without it Cline assumes SSE.)
Zed
{ "context_servers": { "transfer": { "url": "https://upload.83blue.com/mcp" } } }
(Zed supports remote HTTP natively now; no mcp-remote bridge needed.)
JetBrains AI Assistant (2026.1+)
Settings > Tools > AI Assistant > MCP > Add > paste
{ "url": "https://upload.83blue.com/mcp" }.
Warp
{ "transfer": { "url": "https://upload.83blue.com/mcp" } }
Raycast
Run the Install MCP Server command, choose HTTP transport, paste the url (Pro).
A2A (Agent2Agent protocol)
For agents speaking Google's A2A protocol, the agent card is published at the standard
well-known location and a JSON-RPC endpoint (protocol 0.3.0, non-streaming) answers
message/send synchronously:
Agent card: https://upload.83blue.com/.well-known/agent-card.json
Endpoint: https://upload.83blue.com/a2a
- Share: send file parts (
FileWithBytes); multiple files are zipped into one bundle. Add a data part{"transcript": "..."}to placeHANDOFF.mdat the root, and optionalexpires_days/password. The reply is a message with a text summary, aFileWithUripart (the capability url) and a data part with all urls. - Fetch: send a data part
{"url": "...", "password": "..."}(or aFileWithUripointing at this service); the reply carries the metadata and the capability url. This service is also a natural host for the uri in any A2AFileWithUripart, which the A2A specification deliberately leaves to external infrastructure.
How do two AI agents hand files to each other?
The sending agent uploads everything the next agent needs (instructions, code, data) and
passes on two strings: the url and the password. That message is small enough to travel
through any channel: a chat reply, a ticket, an e-mail, another model's context window,
or an A2A message (this service is a natural host for the uri in an A2A
FileWithUri part, which the A2A specification deliberately leaves to
external infrastructure).
A handoff message that works well as a prompt for the receiving agent:
I have uploaded a bundle for you on 83blue.
url: https://upload.83blue.com/d/TOKEN
password: XXXX-XXXX-XXXX
Fetch it with:
curl -fL -OJ "https://upload.83blue.com/api/download?token=TOKEN&password=XXXX-XXXX-XXXX"
Unzip it and read INSTRUCTIONS.md first: it explains what each file is
and what you need to do.
Agents connected over MCP skip the shell entirely: the sender calls
share_file or share_conversation, the receiver calls
receive_file, and the bundle contents plus the root briefing are surfaced
automatically so the receiving agent knows what it has.
Drop-in transfer.sh replacement
The classic transfer.sh service has been unreachable since 2024, but its upload
pattern lives on in thousands of scripts and shell functions. This service answers the same
wire contract: raw PUT to /{filename}, plain-text url response, so pointing an
old script here just works:
# the classic pattern
curl --upload-file ./hello.txt https://upload.83blue.com/hello.txt
# -> https://upload.83blue.com/f/TOKEN/KEY/hello.txt (plain text, GET it to download)
# the classic .bashrc function, one hostname swap
transfer() {
curl --progress-bar --upload-file "$1" "https://upload.83blue.com/$(basename "$1")"
echo
}
- Multipart
POST /works too, with any form field name; one url per line. - A
Max-Days: Nrequest header sets the expiry (1-180 days). - The response carries an
X-Url-Deleteheader;curl -X DELETEit to remove the file, andX-Content-SHA256for integrity checks. - Downloads are direct, unauthenticated GETs of the returned url with HTTP Range support; wget saves the proper file name.
Agent memory and scratchpad
Because every transfer is a durable blob you fetch back by url, this doubles as
short and long-term memory for AI agents. Dump text, JSON or a whole
working context with share_text (or POST /api/upload), keep it for
up to 180 days, then fetch it back by its capability url from a later
session, another machine, or a different model:
# stash working memory (kept up to 180 days)
curl -F "file=@state.json" "https://upload.83blue.com/api/upload?expires_days=180"
# -> capability_url; save it, then in any later session:
curl -fL "https://upload.83blue.com/f/TOKEN/KEY"
It is blob-by-url storage (retrieve by the url you kept, not full-text search), which is exactly what agents need to offload context between sessions, cache intermediate artefacts, or pass state to the next agent. No account, no database to set up, and it cleans itself up when the transfer expires.
How do I host a static website an agent just built?
Send the files of a static site and get back a live public url straight away:
no signup, no account, no API key. Built for when an agent has coded a landing page, demo,
prototype or report and wants to show a human, or hand it to another agent. Each site is
published at its own path, https://upload.83blue.com/s/{slug}/. The slug is
auto-generated (something like brave-otter-482) unless you pick one yourself
(3-40 characters, a-z, 0-9 and hyphens).
curl -sS -X POST https://upload.83blue.com/api/site \
-H 'Content-Type: application/json' \
-d '{"files":[{"path":"index.html","text":"<h1>Hello</h1>"},
{"path":"style.css","text":"h1{font-family:sans-serif}"}],
"expires_days":30}'
# -> {"ok":true, "url":"https://upload.83blue.com/s/brave-otter-482/", "manage_key":"..."}
| Field | Meaning |
|---|---|
files | Array of {path, text} or {path, base64} (for images, fonts, media). Must include an index.html at the root |
slug | Optional custom path, 3-40 characters (a-z, 0-9, hyphens); auto-generated when omitted |
expires_days | 1 to 30; sites auto-expire after 30 days |
GET /api/site?site=... reports status, size and expiry;
DELETE /api/site?site=...&manage_key=... takes it down. Redeploy with the same
slug and manage_key to update or renew a site you own.
Over MCP, one call does the same thing:
| Tool | What it does |
|---|---|
deploy_site | Publish a static site from a files array (each {path, text} or {path, base64}, including a root index.html); optional slug, title, expires_days (1-30) and manage_key to update your own site. Returns the live url and a manage_key |
list_sites | Look up a site by url, host or slug; returns status, size and expiry |
delete_site | Take a site down (pass the site plus its manage_key) |
A site can be up to 50 MB across up to 300 files. Sites are served as
pure static files: server-side code is never executed, and .php
and other script files are refused, so nothing you upload runs on the server. A published page
is public by design, so do not put secrets in it. Suspicious sites are flagged for review and
can be taken down. It complements the file transfer and handoff tools above: the same service
an agent already uses to move files can now put a web page live for the next task.
Limits and fair use
| Thing | Limit |
|---|---|
| Max file size | 2 TB (chunked); 512 MB in a single request; 100 MB per MCP call |
| Retention | 1 to 180 days; default 180 (6 months) for web and HTTP API uploads, 30 days for MCP tool calls |
| Simultaneous uploads | 3 site-wide. When all slots are busy the reply is HTTP 429 with a
Retry-After header and a JSON error: wait a few seconds and retry |
| Download speed | 1 Mbit/s per connection after the first 512 KB, at most 3 download connections per IP: this service optimises for handoffs, not bulk speed |
| Accounts / API keys | None, ever |
| Price | Free |
| Automated use | Welcome. Scripts, cron jobs, CI pipelines and AI agents are the intended audience |
Fair use means: no illegal content, no malware distribution, no using the service as permanent primary storage or a CDN. See the terms. Infringing or harmful files can be reported; they are removed promptly (their links then return HTTP 410) and their hashes are blocked from re-upload. Normal automated use is never restricted.
Security and privacy
- All transfers travel over HTTPS.
- Every transfer gets an unguessable 80-bit link token plus a separate auto-generated password (roughly 70 bits), stored only as a bcrypt hash.
- Files are encrypted at rest with AES-256-CTR (per-file key); video is kept unencrypted so it can stream and seek in the browser. This is encryption at rest, not end-to-end: the operator can decrypt to review content for safety and abuse, so do not treat it as a place for secrets only you can read.
- Files are deleted automatically at expiry; expired links return 404.
- No tracking, no advertising, no analytics scripts, no cookies.
- Anyone holding the url and the password can download the file until it expires; treat the pair like the secret it is.
Free file transfer for AI agents and humans. No signup, no keys, no nonsense.