Embed the MASV uploader
In this tutorial, you’ll use your application for the upload UI — the file picker, the progress bar, the branding — while MASV handles all the complexity of large file transfer underneath. The Web Uploader SDK (@masvio/uploader) is the data plane for this pattern. Your back end is responsible for orchestrating the session: creating the portal, creating the package, issuing credentials to the client, and reacting to completed transfers.
What you’ll learn
Section titled “What you’ll learn”In this tutorial, you will:
- Resolve a portal
- Create a package
- Initialize the uploader
- Collect files and start an upload
- Show upload progress
- React to the completed transfer
Architecture overview
Section titled “Architecture overview”[User's browser] │ │ 1. User initiates an upload action in your app. │[Your back end] │ │ 2. Your server authenticates to MASV and creates a package. │ 3. Your server returns the package ID + token to the front end. │[User's browser + MASV Web Uploader SDK] │ │ 4. The SDK uses the package credentials to upload directly to MASV. │ 5. Your app shows progress using SDK events. │[MASV Cloud] │ │ 6. MASV triggers a webhook when the package is finalized. │[Your back end] │ │ 7. Your back end reacts: triggers processing, sends notifications, etc.The key security principle here is that your API key never leaves your server. The client receives only the short-lived package token, scoped to a single upload session.
Before you begin
Section titled “Before you begin”-
Create a MASV account and generate an API key from the MASV Web App.
-
Create a MASV Portal (if you’re not planning on sending packages directly to individual recipients or teams).
-
Install the MASV Web Uploader SDK in your front-end project:
Terminal window npm install @masvio/uploader# oryarn add @masvio/uploader -
Configure a webhook endpoint on your server and register it with your MASV Portal. Your server will receive
package.createdandpackage.finalizedevents.
Step 1: Resolve the portal (back end)
Section titled “Step 1: Resolve the portal (back end)”If you are uploading to a portal, you need its ID. Resolve it once at startup or on demand using the portal’s subdomain:
GET https://api.massive.app/v1/subdomains/portals/{subdomain}Store the returned id — this is the portal ID you will use when creating packages.
Step 2: Create a package (back end)
Section titled “Step 2: Create a package (back end)”When a user begins an upload, your server creates a package on their behalf. This call requires your API key and should never be made from the browser.
# Store your API key in an environment variable — never hardcode it.# See /api/api-keys/ for key management best practices.curl -X POST "https://api.massive.app/v1/portals/{portal_id}/packages" \ -H "X-API-KEY: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Project Assets - June 2026", "description": "Rushes from the Montréal shoot", "sender": "user@yourdomain.com" }'The response contains two values you must hold onto:
id— the package ID.access_token— the package token that authorizes write access to this specific package.
Security note: Return only the id and access_token to your front end. Your API key must remain server-side. The package token is short-lived and scoped to this upload — it cannot be used to access other packages or team resources.
Step 3: Initialize the uploader (front end)
Section titled “Step 3: Initialize the uploader (front end)”In your browser code, import the SDK and initialize an uploader instance using the credentials your back end returned:
import { Uploader } from '@masvio/uploader';
const uploader = new Uploader(packageId, packageToken, 'https://api.massive.app');Step 4: Collect files and start the upload (front end)
Section titled “Step 4: Collect files and start the upload (front end)”The Web Uploader does not provide a file picker UI. You build that yourself — typically a standard HTML input, a drag-and-drop zone, or a custom selection component.
After the user selects files, prepare them in the format the uploader expects:
const filesToUpload = Array.from(fileInput.files).map(file => ({ id: crypto.randomUUID(), // a unique identifier you assign file: file, // the browser File object path: file.name // relative path used to preserve folder structure}));
uploader.addFiles(filesToUpload);As soon as files are added, uploading begins immediately. There is no separate start call required.
Unlike the MASV API or MASV Agent, you do not need to finalize the upload manually. The Web Uploader handles package finalization automatically after all files are uploaded.
Step 5: Show progress (front end)
Section titled “Step 5: Show progress (front end)”The Web Uploader emits events as the transfer progresses. Attach listeners to keep your UI in sync with the transfer state:
uploader.on('progress', (completed, total, speed) => { const percent = Math.round((completed / total) * 100); progressBar.style.width = `${percent}%`; speedDisplay.textContent = `${(speed.moving / 1e6).toFixed(1)} MB/s`;});
uploader.on('complete', () => { statusMessage.textContent = 'Upload complete.';});
uploader.on('error', (err) => { console.error('Upload failed:', err); statusMessage.textContent = 'Upload failed. Please try again.';});Step 6: React to the completed transfer (back end)
Section titled “Step 6: React to the completed transfer (back end)”When MASV finalizes the package, it triggers a package.finalized webhook to your registered endpoint. The payload tells you the package ID, sender, total size, and number of files:
{ "event_type": "package.finalized", "object": { "id": "JF4D076SA6FA1", "name": "Project Assets - June 2026", "portal_id": "01CYCWJC40RXPK3HNQVYKAX1K1", "sender": "user@yourdomain.com", "size": 33454545, "state": "finalized", "total_files": 12 }}Use this event to trigger whatever happens next in your system, such as moving files to storage, updating a database record, notifying a team member, or kicking off a processing job.
Production considerations
Section titled “Production considerations”- Retry handling: The Web Uploader handles retries on upload failures internally. You do not need to implement retry logic in your application for the transfer itself.
- Package token expiry: Package tokens are short-lived. If your upload session spans a long user interaction before files are added, create the package closer to the moment the upload starts.
- Webhook reliability: MASV retries webhook delivery up to five times, with incremental backoff intervals. Make your webhook handler idempotent so duplicate deliveries do not cause side effects.