WispFile

WispFile for developers

The WispFile SDK adds browser-to-browser file transfer to any web app: your page shares files with a link, and they travel straight to whoever opens it, end-to-end encrypted, never stored. It is the same engine wispfile.com runs on, in one JavaScript module with no dependencies to install.

Key facts

  • One module: https://wispfile.com/sdk/v1/wispfile.js
  • Files are encrypted in the browser (AES-GCM, 256-bit key in the link) and never reach our servers.
  • No account needed to start; an API key attributes transfers to your plan.
  • The sending page must stay open while receivers download.

Quickstart

Paste this into any page. No build step, no key.

<input type="file" id="files" multiple>
<script type="module">
  import { WispFile } from 'https://wispfile.com/sdk/v1/wispfile.js';

  const wispfile = new WispFile();

  document.querySelector('#files').addEventListener('change', async (event) => {
    const share = await wispfile.send(event.target.files, {
      onEvent: (e) => console.log(e.type, e),
    });
    console.log('Share this link:', share.link);
    // Keep the page open: the files are served from here.
  });
</script>

The link opens on wispfile.com, where anyone can download without installing anything. Set appUrl to your own page if you receive links yourself.

Receiving in your page

const offer = await wispfile.open(link);
console.log(offer.files);            // [{ name, size }]

const { files } = await offer.download({
  onProgress: (received, total) => console.log(received / total),
});
// files: File[] (collected in memory by default)

By default each file is collected in memory and returned as a File, which suits files up to a few hundred megabytes. For larger ones, give download an openSink that writes to disk as bytes arrive:

// Large downloads: write each file to disk as it arrives instead.
const dir = await window.showDirectoryPicker({ mode: 'readwrite' });
await offer.download({
  openSink: async (file) => {
    const handle = await dir.getFileHandle(file.name, { create: true });
    const writable = await handle.createWritable();
    return {
      write: (bytes) => writable.write(bytes),
      close: () => writable.close(),
      abort: () => writable.abort(),
    };
  },
});

Every chunk is checked against a SHA-256 fingerprint before it reaches your sink, and a dropped connection resumes where it stopped.

Reference

CallWhat it does
new WispFile(options)apiUrl, signalUrl, appUrl, apiKey, createTransfer, fetch: all optional
send(files, options)Shares File objects (or { file, path } for folders). Options: title, message, expires, password, onEvent. Resolves to { link, linkId, stop() }
share.stop()Ends the link for good: receivers are disconnected and it cannot be opened again
open(link)Reads what a link offers: files, totalBytes, title, message, requiresPassword
offer.download(options)Receives everything. Options: password, openSink, onProgress, signal. Resolves to { files }
onEventreceiver-joined, receiver-connected, progress, receiver-failed, receiver-left, error
WispFileErrorThrown for API refusals; .code is the reason (for example rate-limited, forbidden)

API keys

Without a key, transfers are anonymous and on the free terms (direct connections only). With a key, create one on your account page, transfers count as yours: they appear in your history, use your plan (including its relay for networks that block direct connections), and carry your branding.

Keys are secret by default. A key is refused when it is sent from a browser page, because anyone who opens a page can read its code. There are two ways to use one with a browser app:

  1. Mint transfers on your server (recommended). The key stays on your server; the SDK asks your server to create the transfer and serves the files itself.
  2. List allowed origins on the key, for example an internal tool at https://tools.example.com. The key then works from pages on those origins only, and you accept that visitors to them can read it.
// Your server (Node): the only place the secret key lives.
app.post('/wispfile/transfers', async (req, res) => {
  const answer = await fetch('https://api.wispfile.com/api/transfers', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      authorization: `Bearer ${process.env.WISPFILE_API_KEY}`,
    },
    body: JSON.stringify(req.body), // the file list: names and sizes only
  });
  res.status(answer.status).json(await answer.json());
});

// Your page: the SDK asks your server instead of calling the API itself.
const wispfile = new WispFile({
  createTransfer: (request) =>
    fetch('/wispfile/transfers', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(request),
    }).then((response) => response.json()),
});

A key can create and list transfers, nothing else: it cannot change your account, your billing, your team or other keys. Revoke a key on your account page and it stops working at once.

REST API

The SDK is a thin layer over these endpoints at https://api.wispfile.com. Request bodies are JSON and capped at 64 KB; file contents are never sent to the API.

EndpointPurposeAuth
POST /api/transfersCreate a transfer from a file list (up to 1,000 per request); returns linkId and hostTokenOptional key
POST /api/transfers/:linkId/filesAppend more files (up to 10,000 in all)hostToken
GET /api/transfers/:linkIdWhat a link offersNone
GET /api/turn-credentials?linkId=…ICE servers for the link: STUN, and TURN on plans with a relayNone
POST /api/transfers/:linkId/completeReport a finished download (bytes and route only)None
POST /api/transfers/:linkId/closeEnd a link for goodhostToken
GET /api/me/transfersYour transfer historyKey or session

Signaling is a WebSocket at wss://api.wispfile.com/ws; the SDK handles it. Errors come back as { "error": "…", "message": "…" }.

Limits

  • 60 requests a minute per address without a key; 600 a minute per key.
  • No limit on file size. Transfers are verified end to end up to 5 GB so far.
  • Up to 10,000 files per transfer.
  • The relay (TURN) follows the plan of the key’s owner; without a key, links are direct only.

Security model

  • The SDK creates the encryption key in the browser and puts it only in the link’s fragment, after the #. Browsers never send the fragment to a server; the API and signaling service never see it.
  • The API sees file names and sizes, and the title and message if you set them. Do not put secrets in them.
  • The SDK never sends cookies to the API, so a visitor’s WispFile session can never be used by your page.
  • Browser support: verified in Chromium-based browsers (Chrome, Edge); Firefox and Safari are in testing.

Versioning

/sdk/v1/ keeps a stable API; breaking changes will ship under /sdk/v2/. Questions or an integration to discuss: [email protected].

Send it directly now

Try the transfer yourself before you build on it: no account, nothing stored.

Send files with WispFile