Public API · Open Source

Build on Clex.

A small, honest public API for browser-to-browser file transfer. Create a transfer from your terminal, get a 6-character room code and a receive link, and let the receiver pull the file straight from your machine. No accounts, no servers in the middle, no API keys to manage.

$ curl -sS -X POST https://clex.in/api/transfers \
    -H 'content-type: application/json' \
    -d '{"file_name":"design.zip","file_size_bytes":4823491}'

{
  "transfer_id": "f4a3e9b1c8…",
  "code":        "X7K92Q",
  "receive_url": "https://clex.in/receive?code=X7K92Q",
  "status":      "created",
  "expires_at":  1762542901
}

How a transfer works

Clex is privacy-first. The API only stores transfer metadata — a short room code, a receive URL, optional file metadata, and a status timeline. The actual file bytes never touch our servers; they go peer-to-peer over WebRTC, with a same-LAN shortcut when both sides are on the same network.

  1. Create. You POST /api/transfers. We mint a transfer_id, a short code, and a receive_url. Default expiry is 24h (you can override 60s–7d).
  2. Hand off. Share the code or receive_url with the receiver. Anyone with the link can join.
  3. Connect. The browser opens a WebRTC connection through the Clex signaling worker. The room code matches the two peers.
  4. Transfer. Bytes flow sender→receiver directly. No server-side relay unless P2P is impossible, in which case Clex offers a Drive-backed fallback.
  5. Mark complete. The sender (or your CLI) POSTs an events entry — connected, completed, cancelled, etc. — so the public chain ledger has an honest record.

Privacy model

Everything that's stored on our side is what you'd be comfortable putting on a sticky note: a code, a status, optional non-sensitive file metadata you chose to share. File contents never enter our servers. Transfers auto-expire and are deleted from KV when the TTL fires.

Endpoints

All endpoints live on https://clex.in, return JSON, and accept JSON request bodies. CORS is open. No keys required for the public surface.

POST /api/transfers

Create a new transfer session. Returns a transfer_id, a 6-character code, and the canonical receive_url.

Request body (all fields optional)
{
  "ttl_seconds":     86400,            // 60 .. 604800; default 86400 (24h)
  "file_name":       "design.zip",     // optional metadata for the receiver UI
  "file_size_bytes": 4823491,          // optional, for progress UI
  "file_mime":       "application/zip",
  "client_label":    "clex CLI v1.2"
}
Response 200
{
  "transfer_id": "f4a3e9b1c8…",
  "code":        "X7K92Q",
  "receive_url": "https://clex.in/receive?code=X7K92Q",
  "status":      "created",
  "expires_at":  1762542901,
  "created_at":  1762456501
}
GET /api/transfers/<id_or_code>

Read the current state of a transfer. Pass either the transfer_id or the 6-character code.

Response 200
{
  "transfer_id": "f4a3e9b1c8…",
  "code":        "X7K92Q",
  "receive_url": "https://clex.in/receive?code=X7K92Q",
  "status":      "connected",
  "route":       "p2p",
  "file_name":   "design.zip",
  "file_size_bytes": 4823491,
  "file_mime":   "application/zip",
  "events": [
    { "type": "created",   "ts": 1762456501 },
    { "type": "connected", "ts": 1762456538, "details": { "peer": "browser" } }
  ],
  "expires_at": 1762542901,
  "created_at": 1762456501
}
POST /api/transfers/<id_or_code>/events

Append a lifecycle event. Useful from a CLI or browser client for keeping the public chain ledger honest.

Request body
{
  "type":    "completed",          // created | connected | completed | cancelled | expired | error
  "details": { "bytes_sent": 4823491, "duration_ms": 9421 }
}
Response 200
{ "ok": true, "status": "completed" }
DELETE /api/transfers/<id_or_code>

Cancel a transfer that hasn't completed yet. Already-completed transfers stay marked completed and are not retroactively erased from the chain ledger.

Response 200
{ "ok": true, "status": "cancelled" }
GET /api/health

Public liveness probe. Used by status pages and the lnch.in registry. Cacheable.

Response 200
{
  "ok":      true,
  "service": "clex-api",
  "ts":      1762456501,
  "version": "phase-2-admin-api"
}

Snippets

Drop-in examples for the most common flow: create a transfer, then poll until it's marked completed or cancelled.

# Create a transfer
curl -sS -X POST https://clex.in/api/transfers \
  -H 'content-type: application/json' \
  -d '{"file_name":"design.zip","file_size_bytes":4823491}' \
  | tee /tmp/clex.json

CODE=$(jq -r .code /tmp/clex.json)
echo "Share https://clex.in/receive?code=$CODE"

# Poll status
while :; do
  STATUS=$(curl -sS https://clex.in/api/transfers/$CODE | jq -r .status)
  echo "$STATUS"
  case "$STATUS" in completed|cancelled|expired) break;; esac
  sleep 2
done
async function shareFile({ name, size }) {
  const r = await fetch('https://clex.in/api/transfers', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ file_name: name, file_size_bytes: size }),
  });
  const t = await r.json();
  console.log(`Receive: ${t.receive_url}`);
  console.log(`Code:    ${t.code}`);
  return t;
}

async function pollUntilDone(code) {
  while (true) {
    const t = await fetch(`https://clex.in/api/transfers/${code}`).then(r => r.json());
    console.log(t.status);
    if (['completed', 'cancelled', 'expired'].includes(t.status)) return t;
    await new Promise(r => setTimeout(r, 2000));
  }
}
import time, requests

def share_file(name: str, size: int) -> dict:
    r = requests.post(
        "https://clex.in/api/transfers",
        json={"file_name": name, "file_size_bytes": size},
        timeout=10,
    )
    r.raise_for_status()
    t = r.json()
    print(f"Receive: {t['receive_url']}")
    print(f"Code:    {t['code']}")
    return t

def poll_until_done(code: str) -> dict:
    while True:
        t = requests.get(f"https://clex.in/api/transfers/{code}", timeout=10).json()
        print(t["status"])
        if t["status"] in {"completed", "cancelled", "expired"}:
            return t
        time.sleep(2)

Reference

Transfer status

ValueMeaning
createdTransfer minted, no peer connected yet.
connectedReceiver opened the room, P2P connecting.
completedBytes delivered. Terminal state.
cancelledSender or receiver aborted. Terminal state.
expiredTTL elapsed before completion. Terminal state.

Routes

ValueMeaning
p2pWebRTC sender→receiver direct.
localSame-LAN shortcut (no internet hop).
fallbackDrive-backed temporary relay (sender-signed, 24h).
Rate limits: The public API today has no hard rate limit. We reserve the right to throttle abusive callers (>30 req/s sustained per IP) by returning 429. If you need higher throughput, ping [email protected].
Open source: Everything you see here is open. Source: github.com/Abhinavv-007/clex. File issues, send PRs, or fork it.