Developers
Your pipeline hears about every delivery.
Boita API is a REST API with an OpenAPI 3.1 description, TypeScript and Python SDKs, scoped keys, signed webhooks, an event stream, sandboxes and embeds for building large-file transfer into your own pipeline or product — part of Boita, the large-file transfer workspace.
A scoped workspace API key gives scripts, pipelines, lab instruments, render farms and integration platforms the same REST surface the Boita apps use. An OpenAPI 3.1 description generates the TypeScript and Python SDKs; webhooks post a signed event the moment a download, a portal upload or a transfer completes. Included from the Pro plan.
- Scoped keys (read / read + write)
- OpenAPI 3.1 + SDKs
- Webhook console with test and retry
- REST hooks for Zapier, Make, n8n
- Sandbox workspaces
- Embeds (Scale+)
- Device sign-in for panels
Authentication
API keys
Create a key under Developers in your workspace (admin role). Send it as a bearer token.
- Scope. A key acts as the member who created it, in that workspace only, on the resource endpoints below. It cannot sign in, change members or billing, or reach other workspaces. Read keys can only GET; read + write keys can create shares, portals, transfers and manage files.
- Lifetime. Optional expiry; revoke any time. A key stops working the moment its creator leaves the workspace.
- Limits. 600 requests per minute per key. Errors come back as
{ "error": { "code", "message", "details" } }. - Base URL.
https://api.boita.io/v1. All timestamps are ISO 8601 UTC; sizes are bytes.
# List a folder
curl https://api.boita.io/v1/workspaces/$WS/files?path=/Deliveries \
-H "Authorization: Bearer boita_sk_…"
# Create a share link for two files, valid 7 days, sent to a client
curl -X POST https://api.boita.io/v1/workspaces/$WS/shares \
-H "Authorization: Bearer boita_sk_…" -H "Content-Type: application/json" \
-d '{"paths":["Deliveries/EP03_v04.mov","Deliveries/EP03_mix.wav"],"title":"EP03 v04","expiresInDays":7,"recipients":["ravi@client.tv"]}'
# Poll the event stream from the last id you saw
curl "https://api.boita.io/v1/workspaces/$WS/events?after=$LAST_ID" -H "Authorization: Bearer boita_sk_…"SDKs
Generated from the same OpenAPI document
@boita/sdk on npm and boita on PyPI wrap every endpoint with the same names as the reference; the client is bound to one workspace, so path parameters are positional and the workspace id is never repeated.
npm install @boita/sdk
import { Boita } from '@boita/sdk';
const boita = new Boita({ apiKey: process.env.BOITA_API_KEY!, workspaceId: 'ws_…' });
const { items } = await boita.files.list({ path: 'Results' });
const share = await boita.shares.create({
paths: ['Results/run-0918/summary.h5', 'Results/run-0918/README.md'],
title: 'Run 0918', expiresInDays: 7, recipients: ['lab@partner.example'],
});
console.log(share.url);pip install boita
from boita import Boita
boita = Boita(api_key=os.environ["BOITA_API_KEY"], workspace_id="ws_…")
files = boita.files.list(path="Results")
share = boita.shares.create(
paths=["Results/run-0918/summary.h5"], title="Run 0918", expiresInDays=7,
recipients=["lab@partner.example"],
)
print(share.url)The OpenAPI 3.1 document is at https://api.boita.io/v1/openapi.json (and mirrored at boita.io/openapi.json); the rendered reference with every schema and example is at /docs/api. Sandbox workspaces are throwaway twins of your workspace on the same plan, so a script can be tested against nothing that matters.
REST
Endpoints
The same API the web, desktop and mobile apps are built on. {ws} is your workspace id, shown on the Developers page.
| Method | Path | What | Scope |
|---|---|---|---|
| GET | /workspaces/{ws}/files?path=/Deliveries | List a folder. | read |
| POST | /workspaces/{ws}/files/folder | Create a folder ({ path }). | write |
| PATCH · DELETE | /workspaces/{ws}/files | Rename ({ path, newName }) or move to trash ({ paths }). /files/search?q= and /files/trash are read routes. | write |
| GET | /workspaces/{ws}/shares | List share links; /{id} for one, /{id}/receipt for the delivery receipt (JSON or .pdf). | read |
| POST | /workspaces/{ws}/shares | Create a share link: paths, title, message, expiresInDays, maxDownloads, password, recipients. | write |
| PATCH · DELETE | /workspaces/{ws}/shares/{id} | Extend or revoke. | write |
| GET · POST | /workspaces/{ws}/portals | List or create upload portals. | write |
| GET | /workspaces/{ws}/transfers | Transfer history with status, bytes, speed. | read |
| POST | /workspaces/{ws}/transfers | Mint a transfer for the Boita desktop app or transfer engine to run (returns a transfer specification). | write |
| GET | /workspaces/{ws}/usage | Storage used, data moved, plan limits. | read |
| GET | /workspaces/{ws}/events?after={id} | The workspace event stream, for polling instead of webhooks. | read |
Transfers minted through the API are run by the Boita desktop app (including “Send from my computer” from the phone) or your own transfer client; the API returns the transfer specification and tracks progress and completion.
Webhooks
Signed events, retried until they land
Add an HTTPS endpoint under Developers, pick the events, and keep the signing secret shown once. The webhook console shows every delivery with its response, lets you send a test event and resend any failed one.
Delivery
POSTwithContent-Type: application/json, a 10-second timeout, no redirects followed.- Body:
{ id, type, createdAt, workspaceId, data, delivery: { id, attempt } }. Treatidas the idempotency key. - Headers:
Boita-Signature,Boita-Event,Boita-Delivery. - Any 2xx counts as delivered. Otherwise we retry after 1 min, 5 min, 30 min, 2 h and 12 h, then mark the delivery failed. Twenty consecutive failures disable the endpoint; re-enable it from Developers, where every delivery can also be resent.
Signature
Boita-Signature: t=<unix seconds>,v1=<hex> where v1 is HMAC-SHA256 of `${t}.${rawBody}` with your endpoint secret. Verify on the raw bytes before parsing JSON, reject if t is more than 5 minutes off, and compare in constant time.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyBoitaSignature(secret, header, rawBody) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.trim().split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5-minute tolerance
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return expected.length === v1.length && timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v1, 'hex'));
}
// Express: app.post('/boita', express.raw({ type: 'application/json' }), (req, res) => {
// if (!verifyBoitaSignature(process.env.BOITA_WEBHOOK_SECRET, req.get('Boita-Signature'), req.body.toString())) return res.sendStatus(401);
// const event = JSON.parse(req.body); // { id, type, createdAt, workspaceId, data, delivery: { id, attempt } }
// res.sendStatus(204);
// });import hmac, hashlib, time
def verify_boita_signature(secret: str, header: str, raw_body: bytes) -> bool:
parts = dict(p.strip().split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])Catalogue
Events
Subscribe to specific types or to everything (“*”), which includes types added later.
| transfer.completed | An upload or download finished cleanly (files, bytes, rate, client, paths). |
| transfer.failed | A transfer ended with an error. |
| share.created | A share link was created (items, expiry, recipients). |
| share.downloaded | A recipient completed a download of a share link. |
| share.commented | A recipient left a comment on a share page, or the sender replied. |
| delivery.completed | Auto-delivery pushed a file to your bucket or transfer server. |
| delivery.failed | Auto-delivery gave up on a file after five attempts. |
| share.extended | A share link’s expiry was extended. |
| share.revoked | A share link was revoked. |
| share.expired | A share link reached its expiry. |
| portal.created | An upload portal was created. |
| portal.upload.completed | A client’s upload through a portal landed (folder, files, bytes, sender). |
| watch_folder.created | A watch folder was set up. |
| member.joined | Someone joined the workspace. |
| member.removed | A member was removed. |
The same events are readable at GET /workspaces/{ws}/events for integrations that prefer to poll, and reach Zapier, Make and n8n through REST hooks. Delivery pages and upload portals can be embedded on your own site with a snippet from the Developers page (Scale and above); panels and desktop tools sign in with a device code instead of a copied key. See integrations and the API reference pointer.
Questions people ask
Boita API, in answers.
Written the way people ask an assistant, and published the same way in products.json and llms.txt. More in the full FAQ.
Who uses this: a bioinformatics pipeline that creates a link when a run finishes; a build system that pushes artifacts and notifies on landing; a data platform that ingests portal uploads into its own store; an IT team embedding an upload portal on an intranet; a geospatial product delivering tiles to customers from code; a media platform embedding delivery pages.
Does Boita have an API and SDKs?
Yes. A REST API with an OpenAPI 3.1 description at https://boita.io/openapi.json (live at https://api.boita.io/v1/openapi.json), SDKs for TypeScript (@boita/sdk) and Python (boita), scoped API keys and a CLI. API keys are on Pro and above.
Can Boita call my system when a file lands?
Yes. Signed webhooks fire on the events you pick, are retried until they land, and the console shows every delivery and lets you resend; an event stream carries everything.
Can I embed a delivery page or upload portal in my own site?
Yes. Embeds for delivery pages and portals are on Scale and above; sandbox workspaces let you build against test data first.
Where is the API reference?
At https://boita.io/api-reference and https://boita.io/docs/api, both generated from the OpenAPI document.
Build on Boita.
API keys, SDKs and webhooks are included from Pro. If you need something the API does not expose yet, tell us — the roadmap is short and public.