Xferio home

Developers

File transfer inside your workflow.

Create Transfer sessions from your backend and receive signed events as people share files. The sender chooses files in their browser; your servers never handle the file bytes.

API access requires an eligible plan.

How the integration works

Your backend creates a Transfer. The sender opens hostUrl; recipients use shareUrl. Your endpoint receives signed events.
  1. 1. Backend creates a Transfer

    Your backend creates a transfer with an API key.

  2. 2. Sender opens hostUrl

    You send the person who is sending to the transfer’s hostUrl, a sending page hosted by Xferio. They choose files there.

  3. 3. Recipients use shareUrl

    Recipients open the share link. Files stream from the sender’s browser to theirs while the sender’s tab stays open.

  4. 4. Backend receives events

    Your webhook endpoint receives events such as a recipient connecting or a download finishing.

The TypeScript SDK (@xferio/sdk, in this repository under packages/sdk-ts) wraps every step, including webhook verification. It is not yet published to a package registry; any HTTP client works too.

Keys and scopes

Workspace bearer keys stay on your backend. An API key is not a sign-in credential for people. Request only the scopes you need.
  • Keep keys on the backend

    Never expose a key in browser code or client apps.
  • Shown once

    Store the key as a secret when it is created; it is not shown again.
  • Existing scopes only

    Tick only the scopes the integration needs.

API keys are workspace-scoped bearer keys. Create them under API & webhooks in your workspace. A key is shown once; store it as a secret and use it only on your backend. Never expose it in browser code or client apps.

http: Authorization header
Authorization: Bearer <your-api-key>

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 from your backend. hostUrl is a secret for the sender; shareUrl is for recipients.

Use your API base URL and workspace id.

bash: Create a transfer with curl
curl -X POST https://xferio.io/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.
Treat hostUrl as a secret. Share shareUrl with recipients.
typescript: The same with the SDK
import { XferioApi } from '@xferio/sdk';

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

Signed webhooks

Verify signatures against the raw request body and handle duplicate event IDs. Browser-reported completion is a signal, not independent proof.
  • Verify the raw body

    Check the signature before parsing JSON.
  • Check the signature

    HMAC-SHA256 with the endpoint’s secret.
  • Deduplicate event IDs

    Retries reuse the same id.
  • Browser reports are signals

    source: "client" events come from a browser.

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. Verify X-Xferio-Signature against the raw request body:

http: Signature header
X-Xferio-Signature: t=1791547200,v1=5f2b…
# v1 = hex HMAC-SHA256(secret, "<t>.<raw request body>")
typescript: 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.

Common events:transfer.createdpeer.joinedtransfer.completed

Events marked source: "client", such as transfer.completed, were reported by a browser; treat them as a signal, not as an independent verification.

Errors, limits and reference

Plan for the error codes, rate limits and retries below. The OpenAPI document has every route. The SDK is not yet published.
  • Rate limits and retries

    Each API key may make 300 requests a minute; creating transfers is limited to 60 a minute. After a 429, wait for the time in the message.
  • Reference and SDK

    OpenAPI reference. The SDK lives in this repository and is not yet published.

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

json: 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://xferio.io/v1/openapi.json
  • Example service with webhook verification: examples/backend in the Xferio repository.
  • In a Direct Transfer, 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 Direct Transfer files for malware because it never receives them. Xferio Drive stores files and checks them separately; see Security.

Product scope

These public integration examples cover Direct Transfer. Drive upload APIs and Drive webhook events are not part of the documented integration.
Direct TransferCreate Transfer sessions, share files with recipients and receive signed events.
Drive (not included)Drive upload APIs and Drive events are not part of the public integration.

API keys are workspace credentials for your backend, not a way for people to sign in to an app.

Get started

Ready for your next file?

Open workspace