Why not a plain POST
The point of the whole pipeline is that a 100 GB upload survives a dropped connection. Each part is encrypted and committed as it arrives, and the server remembers the offset — so a reconnect continues from the last committed part instead of starting again. A single POST gives you none of that, and a laptop lid closing costs you the whole file.
The flow
Register the files, upload each one, then use them. The registration response looks like this:
{
"tusEndpoint": "https://yungle.co/files",
"files": [
{ "id": "01JABC…", "name": "final-cut.mov", "size": 8123456789,
"uploadToken": "eyJzY29wZ…" }
]
}Point a tus client at tusEndpoint with these settings:
import * as tus from 'tus-js-client';
const upload = new tus.Upload(stream, {
endpoint: tusEndpoint,
uploadSize: file.size,
// The token authorizes EVERY request, not only the creation POST —
// HEAD, PATCH and DELETE are verified too. Metadata below reaches the
// server only on the first POST, so it cannot carry the token alone.
headers: { 'x-yungle-upload-token': file.uploadToken },
metadata: {
fileId: file.id,
token: file.uploadToken,
filename: file.name,
},
retryDelays: [0, 1000, 3000, 5000, 10000],
});
upload.start();Rules that will bite you
- Send
x-yungle-upload-tokenon every request, not just the creation POST. HEAD, PATCH and DELETE are all authorized. A client that only sets it once gets a 401 partway through an otherwise working upload. sizemust be exact. It is what the quota reservation and the truncation guard compare against. A size that does not match the bytes you send fails the upload rather than silently storing a partial file.- Upload tokens last two hours. That is the window to *start* in; a long upload in progress is not interrupted when it passes. If you register files and come back tomorrow, register them again.
- To resume, HEAD the upload URL your tus client stored and continue from the offset it returns. Do not re-POST an upload that is already in progress — that starts a new one with a fresh encryption key and abandons what you sent.
After the bytes land
Each file is scanned for malware before it can be shared. GET /transfers/{id} reports scanResult per file: null while it is pending, then clean or infected. An infected file has its encryption key destroyed immediately, so it cannot be downloaded by anyone including you.
You do not have to wait for the scan before calling finalize — recipients are only emailed once every file has finished uploading, and the download path checks the verdict independently.
path field on each file, e.g. "Ceremony/Raw". In a collection that builds real folders you can navigate and rename. In a transfer it is kept for the recipient's ZIP, which is the only place it needs to exist.