xferio

Build on Xferio

Let people send files from your product without your servers ever touching the bytes. You create the transfer; the sender’s browser does the sending; webhooks tell you how it went.

How an integration works

  1. Your backend creates a transfer with an API key.
  2. You send the person who is sending to the transfer’s hostUrl, a sending page hosted by Xferio. They choose files there.
  3. Recipients open the share link. Files stream from the sender’s browser to theirs while the sender’s tab stays open.
  4. Your webhook endpoint receives events such as a recipient connecting or a download finishing.

The TypeScript SDK (@xferio/sdk) wraps every step, including webhook verification. Any HTTP client works too.

API keys

Create keys under API & webhooks in your workspace. API access is part of paid plans. A key is shown once; store it as a secret. Send it as a bearer token:

Authorization header
Authorization: Bearer xf_live_…

Each key works only in its workspace and only for the scopes you tick. A key sees and stops the transfers it created; give it transfers:read_all to see the whole workspace. Rotating a key keeps the old one working for 24 hours.

Create a transfer

Create a transfer with curl
curl -X POST https://api.example.com/v1/tenants/$WORKSPACE_ID/transfers \
  -H "Authorization: Bearer $XFERIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Signed contract", "expiresInHours": 24, "password": "optional"}'

The response includes:

  • transfer.shareUrl: the link recipients open.
  • hostUrl: the sending page. Redirect the sender there and never log it; whoever has it can send as this transfer.
  • host and signaling: for advanced integrations that run the sender inside their own page with the SDK.
The same with the SDK
import { XferioApi } from '@xferio/sdk';

const xferio = new XferioApi({ baseUrl: 'https://api.example.com', apiKey: process.env.XFERIO_API_KEY });
const created = await xferio.createTransfer(workspaceId, { title: 'Signed contract' });
return Response.redirect(created.hostUrl, 303);

Webhooks

Add an HTTPS endpoint under API & webhooks and pick the events you want. Each delivery is a JSON event signed with the endpoint’s secret:

Signature header
X-Xferio-Signature: t=1791547200,v1=5f2b…
# v1 = hex HMAC-SHA256(secret, "<t>.<raw request body>")
Verify a delivery with the SDK
import { verifyWebhook } from '@xferio/sdk';

const event = await verifyWebhook({
  payload: rawBody,                       // raw bytes, before JSON parsing
  signature: req.headers['x-xferio-signature'],
  secret: process.env.XFERIO_WEBHOOK_SECRET,
});                                       // throws if forged, altered or older than 5 minutes
if (await alreadyHandled(event.id)) return; // retries reuse the same id

Reply with any 2xx within 10 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. Events marked source: "client", such as transfer.completed, were reported by a browser.

Errors and limits

Errors share one shape, and their codes never change meaning:

Error body
{ "error": { "code": "QUOTA_EXCEEDED", "message": "…", "requestId": "…" } }
StatusCodeMeaning
400VALIDATION_FAILEDThe request body or query is invalid; details list each field.
401UNAUTHENTICATEDMissing, revoked or expired key or token.
402ENTITLEMENT_MISSINGYour plan does not include this feature.
402QUOTA_EXCEEDEDA monthly limit or the seat limit is reached.
403FORBIDDENThe key lacks the scope, or the transfer belongs to someone else.
409TRANSFER_NOT_JOINABLEThe transfer has ended or expired.
409TRANSFER_FULLEvery recipient place on the link is taken.
429RATE_LIMITEDToo many requests; wait for the time in the message.

Each API key may make 300 requests a minute; creating transfers is limited to 60 a minute.

Reference

  • OpenAPI 3.1 document: https://api.example.com/v1/openapi.json
  • Example service with webhook verification: examples/backend in the Xferio repository.
  • Files are encrypted between the browsers by WebRTC (DTLS), also when they pass through the relay. There is no additional application-level encryption, and Xferio cannot scan files for malware because it never receives them.