Direct MASV API integration
In this tutorial, you’ll use the MASV REST API to communicate directly with your application, without using the MASV Agent or Web Uploader SDK. Your code implements the full upload lifecycle: creating packages, registering files, obtaining pre-signed upload URLs, uploading chunks to cloud storage, finalizing files, and finalizing the package.
What you’ll learn
Section titled “What you’ll learn”In this tutorial, using the MASV API, you will:
- Create a package
- Register a file with the package
- Initiate a multipart upload to cloud storage
- Obtain pre-signed URLs for each file chunk
- Upload file chunks
- Finalize a file
- Finalize a package
Architecture overview
Section titled “Architecture overview”[Your application] │ │ All API calls authenticated with X-API-KEY header │ ├── 1. Create package POST /teams/{team_id}/packages │ POST /portals/{portal_id}/packages │ │ Package returns: id, access_token (package token) │ All subsequent file operations use X-Package-Token header. │ ├── For each file: │ ├── 2. Register file POST /packages/{pkg_id}/files │ ├── 3. Create in storage Follow create_blueprint (S3 multipart initiation) │ ├── 4. Get chunk URLs POST /packages/{pkg_id}/files/{file_id}?start=0&count=N │ ├── 5. Upload chunks PUT directly to pre-signed S3 URLs │ ├── 6. Finalize file POST /packages/{pkg_id}/files/{file_id}/finalize │ └── 7. Finalize package POST /packages/{pkg_id}/finalizeBefore you begin
Section titled “Before you begin”Obtain your Team ID. You can retrieve it using:
curl -H "X-API-KEY: $API_KEY" \ -X GET https://api.massive.app/v1/teamsDecide whether you are uploading to an individual/team (sending to email recipients or creating a link) or to a portal (depositing into a designated collection point).
Step 1: Create a package
Section titled “Step 1: Create a package”All uploaded files must belong to a package. Create one for your team or a portal:
Team package:
# 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/teams/{team_id}/packages" \ -H "X-API-KEY: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Render Output 2026-06-10", "description": "Final composited frames for episode 3", "recipients": ["editor@studio.com"], "chunk_size": 104857600 }'Portal package:
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": "Render Output 2026-06-10", "description": "Final composited frames for episode 3", "sender": "render@yourpipeline.com", "chunk_size": 104857600 }'The response contains id (the package ID) and access_token (the package token). Store both — id is used in all subsequent URLs, and access_token is passed as the X-Package-Token header for all file operations.
Set chunk_size explicitly. Although not required, providing it ensures compatibility and consistent performance. The default is 100 MiB (104857600 bytes). For files approaching 1 TB, you will need to increase this value to stay within the 10,000-chunk limit imposed by the underlying storage service.
Step 2: Register a file with the package
Section titled “Step 2: Register a file with the package”For each file you want to upload, register it with the package. This tells MASV about the file and returns the blueprint you will use to initiate the multipart upload in cloud storage.
curl -X POST "https://api.massive.app/v1/packages/{package_id}/files" \ -H "X-Package-Token: $PACKAGE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "kind": "file", "name": "frame_0001.exr", "path": "renders/ep3/", "last_modified": "2026-06-10T09:00:00.000Z", "size": 220200960 }'The response includes a create_blueprint object and a file.id you will need in later steps. The create_blueprint contains the method, URL, and headers for creating the file in cloud storage.
Step 3: Initiate the multipart upload in cloud storage
Section titled “Step 3: Initiate the multipart upload in cloud storage”Execute the HTTP request described by create_blueprint exactly as specified:
{method}: {url}Headers: {headers from blueprint}This initiates a multipart upload in MASV’s cloud storage (typically Amazon S3). The response is XML and contains an UploadId — store it, as it is required for all subsequent chunk operations for this file.
Step 4: Obtain pre-signed URLs for each file chunk
Section titled “Step 4: Obtain pre-signed URLs for each file chunk”Request upload URLs for the chunks you are about to send. You can request them in bulk:
curl -X POST \ "https://api.massive.app/v1/packages/{package_id}/files/{file_id}?start=0&count=3" \ -H "X-Package-Token: $PACKAGE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "upload_id": "THE_UPLOAD_ID_FROM_STEP_3" }'The response is an ordered array of blueprints — one per chunk — each containing a method and url (and sometimes additional headers).
Request only as many URLs as you need. Over-requesting causes unnecessary overhead. For large files or streaming scenarios where the total size is not known up front, request URLs in smaller batches as you consume them.
URLs expire. Pre-signed upload URLs are time-limited. If a URL expires before you use it, request a fresh set for the remaining chunks.
Step 5: Upload file chunks
Section titled “Step 5: Upload file chunks”Upload each chunk using its corresponding blueprint. Chunks are uploaded directly to MASV’s cloud storage — not to the MASV API server.
PUT {url from blueprint}Headers: {any headers specified in the blueprint}Body: the raw bytes for this chunkFrom the response headers, capture:
ETag— the hash of the chunk as confirmed by cloud storage.
Along with the chunk’s partNumber (its 1-based index), you will need both values to finalize the file.
Include all blueprint headers. If the blueprint specifies headers, they must be included in your PUT request exactly as provided, or the request will fail.
Step 6: Finalize the file
Section titled “Step 6: Finalize the file”After all chunks for a file are uploaded, tell MASV that the file is complete:
curl -X POST \ "https://api.massive.app/v1/packages/{package_id}/files/{file_id}/finalize" \ -H "X-Package-Token: $PACKAGE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "size": 220200960, "chunk_size": 104857600, "file_extras": { "upload_id": "THE_UPLOAD_ID_FROM_STEP_3" }, "chunk_extras": [ { "part_number": "1", "etag": "\"abc123...\"" }, { "part_number": "2", "etag": "\"def456...\"" }, { "part_number": "3", "etag": "\"ghi789...\"" } ] }'A 204 No Content response means the file was successfully sealed in storage. Repeat steps 2–6 for each file in the package.
If the submitted size does not match the actual object size in storage, MASV responds with 409 Conflict. Always provide the exact byte count.
Step 7: Finalize the package
Section titled “Step 7: Finalize the package”After all files are finalized, close the package. This triggers delivery to recipients and starts the expiry clock.
curl -X POST \ "https://api.massive.app/v1/packages/{package_id}/finalize" \ -H "X-Package-Token: $PACKAGE_TOKEN" \ -H "Content-Type: application/json"A 204 No Content response means the package is dispatched.
Finalize files before finalizing the package. Any files that are not yet finalized at the time of package finalization are dropped and will not be delivered. If you are uploading files concurrently, wait for all file finalizations to complete before finalizing the package.
Production considerations
Section titled “Production considerations”-
Implement retries. Network interruptions, expired pre-signed URLs, and transient 5xx errors are normal. Design your upload loop to detect failures, re-request chunk URLs if needed, and retry failed chunks with exponential back-off.
-
Track chunk metadata. Your application needs to store
partNumberandETagfor each successfully uploaded chunk to finalize the file. If your process crashes mid-upload, you may need to restart the upload for that file from the beginning unless you persist this metadata. -
Concurrency. You can upload multiple chunks concurrently for a single file, and you can upload multiple files concurrently within the same package. Tune concurrency carefully according to your network capacity.
-
Chunk size for large files. For files approaching 1 TB, verify that
file_size / chunk_sizedoes not exceed the 10,000-chunk limit (the S3 limit). Fetch the system spec endpoint to retrieve current limits:Terminal window curl -H "X-API-KEY: $API_KEY" \https://api.massive.app/v1/system/packages/spec