Upload
MASV API clients upload files directly to MASV's private cloud infrastructure. MASV achieves this by providing pre-signed URLs while abstracting the interaction for each step of the file process.
Tip
The recommended way to upload files is to use MASV Transfer Agent. It abstracts the complexities of directly using the MASV API for uploading. It also offers features for managing automations, multiple connections, rate limits, and local storage. Use the MASV API for uploading when your application's host OS is not supported by the Transfer Agent or you need finer-grained control over uploading.
Warning
MASV stores an uploaded package in MASV storage until its expiry time. When packages expire, MASV deletes the package and its content. For details, see Pricing and How Extended Storage Works and What it Costs.
Packages and Authorization Mechanism
Files uploaded to MASV belong to a package object. This package can be thought of as a virtual directory inside of MASV. Each package contains a token that can be used to upload files to that specific virtual directory. A package can be created by interacting with a different API endpoint (for example, portals or teams).
All requests going to upload endpoints require a token passed as an HTTP header, X-Package-Token
. This token authorizes the user to add and remove files from the package until the package is finalized.
Lifecycle Of An Upload
When uploading single or multiple files as part of a package, the following steps generalizes the upload interaction:
- For each file in the package.
- Add file to the package
API Action
- Create the file in cloud storage
- Collect metadata from cloud storage service for the file
- Identify the number of file chunks to split the file
- Obtain authorized URLs to upload each chunk
- Upload all chunks to cloud storage
- Collect metadata from the cloud storage service for each uploaded chunk.
- Once all chunks are uploaded, send request to finalize file.
- Finalize package
Info
After the uploaded package has been finalized, the transfer is locked and cannot be further modified.
Finalization of the package will then dispatch the file to the intended recipient immediately.
Warning
Please avoid uploading the following files/directories because they tend to change while upload is running which could cause the upload to fail:
desktop.ini
.DS_Store
.fcpcache
MASV Blueprints
MASV's API abstracts interactions with cloud storage services by creating a Blueprint object. This blueprint instructs the clients on which request they need to execute next during the file upload process.
Each blueprint has four properties:
Name | Type | Required | Description |
---|---|---|---|
url |
String | Yes | URL that the request should be forwarded towards. |
method |
String | Yes | The HTTP Method to use. Example: Get , POST , PUT , DELETE |
headers |
JSON | Yes | Key-value map of header names and their values. |
body |
String | Yes | HTTP Request body. |
Upload API Interaction
Each package belongs to either a team or portal on MASV. Depending on your use case you can create the initial package under either. Other API requests are portal/team agnostic.
To create a package for a Team, you must provide an API key.
To create a package for a Portal, you do not need an API key. However, you must provide an access code if a Portal is configured to require it.
1) Create a package
A. Team package
Method | Route |
---|---|
POST |
/teams/{team_id}/packages |
HEADERS
Name | Type | Required | Description |
---|---|---|---|
X-API-KEY |
String | Yes | API key |
Content-Type |
String | Yes | Must be application/json |
URL Parameters
Name | Type | Required | Description |
---|---|---|---|
team_id |
String | Yes | The team ID to bind the package to. |
BODY
Name | Type | Required | Description |
---|---|---|---|
access_limit |
Integer | No | Override default number of downloads for the package |
description |
String | Yes | Description of the package |
name |
String | Yes | Name of the package |
password |
String | No | Password to protect download access to the package |
recipients |
String[] | Yes | Email address of recipient(s) |
unlimited_storage |
Boolean | No | Enables and disables unlimited exended storage for package |
B. Portal package
Method | Route |
---|---|
POST |
/portals/{portal_id}/packages |
HEADERS
Name | Type | Required | Description |
---|---|---|---|
Content-Type |
String | Yes | Must be application/json |
X-Access-Code |
String | No | URI encoded upload password (if required by the Portal settings) |
URL Parameters
Name | Type | Required | Description |
---|---|---|---|
portal_id |
String | Yes | The portal id to bind the package to. (Retrieved in (Portals)) |
BODY
Name | Type | Required | Description |
---|---|---|---|
description |
String | Yes | Description of the package |
name |
String | Yes | Name of the package |
sender |
String | Yes | Email address of the portal package sender |
Warning
If you set unlimited_storage
to true
, Unlimited storage will be activated and charges will be incurred beyond the free period in your pricing plan. For details, see How does Extended Storage Work.
REQUEST
curl -d "{\"access_limit\":$ACCESS_LIMIT, \"description\":\"$DESCRIPTION\", \"name\":\"$NAME\", \"password\": \"$PASSWORD\", \"recipients\":[\"$RECIPIENT_EMAIL\"]}" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-s -X POST https://api.massive.app/v1/teams/$TEAM_ID/packages
After a successful request where a package has been created, this endpoint will return an HTTP response with a status code of 201 Created
and a body similar to the one below.
{
"access_token": "xxxxxxxxxxxxxxxxx",
"created_at": "2021-07-26T18:41:24.508Z",
"custom_expiry": true,
"unlimited_storage": false,
"description": "Testing 123",
"expiry": "2021-08-05T18:41:24.508Z",
"extra_storage_updated_at": "2021-07-26T18:41:24.508Z",
"extras": {
"storage_id": "aws-wdc"
},
"id": "01FBJ1Y9GW3DQ1VHBAJZ7J49FQ",
"name": "File",
"progress_channels": [
{
"auth": {
"token": "xxxxxxxxxxxxxxxxx",
"channel_name": "proc.upload.01D72PEE5AH74NR9EG4CZS29JZ",
"publish_key": "pub-c-xxxxx-xxxxxxxxxxxxx",
"subscribe_key": "sub-c-xxxxx-xxxxxxxxxxxxx"
},
"topic":"upload",
"provider": "pubnub"
}
],
"recipients": [
"[email protected]"
],
"state": "new",
"updated_at": "2021-07-26T18:41:24.508Z",
"usage_updated_at": "2021-07-26T18:41:24.508Z"
}
Response Properties:
Property | Description |
---|---|
access_token |
The auth token returned during auth request. This token allows write access to the package. |
created_at |
Time that the object was created at |
custom_expiry |
Was a custom expiration set |
description |
The description for the package. |
expiry |
The expiry for the package |
extra_storage_updated_at |
The time the extra storage was updated at. |
extras |
Various extra fields that can be populated by the user |
extras.storage_id |
The storage id for extras. |
id |
The unique package identifier. |
name |
The name of the package |
progress_channels |
PubNub progress events |
recipients |
Array of emails to send the package to. |
state |
Current state of the package |
updated_at |
Last time the package was updated. |
usage_updated_at |
When usage was last calculated. |
Note
Take note of the id
and the access_token
as they are required to interact with the upload API.
2) Add a file to the package
Method | Route |
---|---|
POST |
/packages/{package_id}/files |
HEADERS
Name | Type | Required | Description |
---|---|---|---|
X-Package-Token |
String | Yes | Package JSON Web Token |
Content-Type |
String | Yes | Must be application/json |
BODY
Name | Type | Required | Description |
---|---|---|---|
kind |
String | Yes | Type of file Accepted Values: file /directory |
name |
String | Yes | Filename (in the file system) |
path |
String | Yes | File's relative path (in the file system) |
last_modified |
String | Yes | File's last modified date in the file system (in UTC format) |
REQUEST PARAMETERS
Name | Type | Required | Description |
---|---|---|---|
package_id |
String | Yes | Package ID obtained in step 1 |
Info
For the following request we will use a specific file example:
- Kind: file
- Name: my_video.mpeg
- Path: Same as where the curl command is being executed
- Size: 210 MiB in size
REQUEST
curl -d '{"kind":"file", "name":"my_video.mpeg", "path": "", "last_modified":"2018-12-17T16:14:34.450Z"}' \
-H "X-Package-Token: $PACKAGE_TOKEN" \
-H "Content-Type: application/json" \
-s -X POST https://api.massive.app/v1/packages/$PACKAGE_ID/files
After a successful request where a file has been added to the package, this endpoint will return an HTTP response with a status code of 201 Created
and a body similar to the one below.
{
"storage_type": "s3",
"max_chunk_size": 5368709120,
"max_chunks_count": 10000,
"create_blueprint": {
"headers": {
"Content-Disposition": "attachment; filename=\"my_video.mpeg\"; filename*=UTF-8my_video.mpeg"
},
"method": "POST",
"url": "https://masv3-storage-prod-wdc.s3-accelerate.amazonaws.com/01D3MNHA6P4XFXDMHJDTXT61CB/01D3MP8G3RBA7A1P0K98VEV2V3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJT7DTCC6DRVEACYQ%2F20190214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190214T002448Z&X-Amz-Expires=172799&X-Amz-SignedHeaders=host&uploads=&X-Amz-Signature=4bc45729a1e768a04b655f4a5972b548db1f8b359be16e719434bc5cc7ffd019"
},
"file": {
"id": "01D3MP8G3RBA7A1P0K98VEV2V3",
"last_modified": "2018-12-17T16:14:34.450Z",
"name": "my_video.mpeg"
}
}
Response Properties:
Name | Type | Required | Description |
---|---|---|---|
storage_type |
String | Yes | Type of cloud storage service the file should be uploaded to. Required value: s3 |
max_chunk_size |
String | Yes | Maximum allowed chunk size by the cloud service. |
max_chunks_count |
String | yes | Maximum number of chunks allowed for the file. |
create_blueprint |
Blueprint{} | yes | The request blueprint for creating the file. |
file |
file{} | yes | File metadata, take note of property: file.id as it is needed later. |
3) Create the file in MASV's cloud storage
From Step 2, use the create_blueprint
and construct a corresponding HTTP request based on the supplied information. For example, using the create_blueprint
above, one can construct a curl request as follows:
curl -H "BLUE_PRINT_HDR" \
-s -X BLUE_PRINT_METHOD 'BLUE_PRINT_URL'
Where:
HEADERS
Name | Type | Required | Description |
---|---|---|---|
BLUE_PRINT_HDR |
String | Yes | property: headers from CreateBlueprint step. |
REQUEST PARAMETERS
Name | Type | Required | Description |
---|---|---|---|
BLUE_PRINT_METHOD |
String | Yes | property: method from CreateBlueprint step. |
BLUE_PRINT_URL |
String | Yes | property: url from CreateBlueprint step. |
The cloud storage service (in this case Amazon AWS S3) response will be similar to the following:
<?xml version="1.0" encoding="UTF-8"?>
<InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Bucket>masv3-storage-prod-wdc</Bucket>
<Key>01D3MNHA6P4XFXDMHJDTXT61CB/01D3MP8G3RBA7A1P0K98VEV2V3</Key>
<UploadId>Gng8Oh.yKzM41CmGxMRzHkO20GbP2RarZfTJVHHyPGqTVwrHHRgIZKqxMykJBjzDLZ9o3.pyYPwbVbtSI2LDOEa96Mllx6roowA47KHu2GkXcH6PC7qJTFDtYS38SwqI</UploadId>
</InitiateMultipartUploadResult>
Note
Data format in the response will be XML.
Note
Ensure to store property: uploadId
from Step 3 as it is metadata required for interacting with the upload API methods related to this file.
4) Obtain upload URLs
This endpoint is used to get pre-signed upload URLs for each of a file's parts. These upload URLs are essentially limited access to storage.
Note
Upload URLs are valid for a limited amount of time and must be re-requested if they expire.
The client can request upload URLs in bulk which will save a lot of back-and-forth communication with the MASV API.
POST /packages/{package_id}/files/{file_id}
HEADERS
Name | Type | Required | Description |
---|---|---|---|
X-Package-Token |
String | Yes | Package JSON Web Token |
Content-Type |
String | Yes | Must be application/json |
BODY
Name | Type | Required | Description |
---|---|---|---|
upload_id |
String | Yes | ID for the initiated multi-part upload |
PARAMETERS
Name | Type | Required | Description |
---|---|---|---|
package_id |
String | Yes | Package ID obtained in step 1 |
file_id |
string | yes | File ID obtained from step 1. |
start |
Integer | Yes | Starting index of the chunk. (zero-indexed) |
count |
Integer | Yes | Number of chunk requests to generate |
REQUEST
curl -d "{\"upload_id\":\"$UPLOAD_ID\"}" \
-H "X-Package-Token: $PACKAGE_TOKEN" \
-H "Content-Type: application/json" \
-s -X POST https://api.massive.app/v1/packages/$PACKAGE_ID/files/$FILE_ID?start=$START&count=$COUNT
Our example requires 3 blueprints (for the 3 chunks it's trying to upload START=0
and COUNT=3
).
The start
and count
parameters provide control over how many bulk chunk requests are obtained. This is useful for times when total file size upfront is unavailable (ie: data streams.)
The server will return an array of blueprints equal to parameter: count
. The blueprints are in ascending order based on the chunk index (starting at 0.)
To continue with our example of uploading my_video.mpeg
. We would receive the following output:
[
{
"method": "PUT",
"url": "https://masv3-storage-prod-wdc.s3-accelerate.amazonaws.com/01D3MNHA6P4XFXDMHJDTXT61CB/01D3MP8G3RBA7A1P0K98VEV2V3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJT7DTCC6DRVEACYQ%2F20190214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190214T011216Z&X-Amz-Expires=86399&X-Amz-SignedHeaders=host&partNumber=1&uploadId=Gng8Oh.yKzM41CmGxMRzHkO20GbP2RarZfTJVHHyPGqTVwrHHRgIZKqxMykJBjzDLZ9o3.pyYPwbVbtSI2LDOEa96Mllx6roowA47KHu2GkXcH6PC7qJTFDtYS38SwqI&X-Amz-Signature=a1ac9186728c3b45d21afc48a70d1535f7597b4f885b0c6ce8cafbbdcde72c72"
},
{
"method": "PUT",
"url": "https://masv3-storage-prod-wdc.s3-accelerate.amazonaws.com/01D3MNHA6P4XFXDMHJDTXT61CB/01D3MP8G3RBA7A1P0K98VEV2V3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJT7DTCC6DRVEACYQ%2F20190214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190214T011216Z&X-Amz-Expires=86399&X-Amz-SignedHeaders=host&partNumber=2&uploadId=Gng8Oh.yKzM41CmGxMRzHkO20GbP2RarZfTJVHHyPGqTVwrHHRgIZKqxMykJBjzDLZ9o3.pyYPwbVbtSI2LDOEa96Mllx6roowA47KHu2GkXcH6PC7qJTFDtYS38SwqI&X-Amz-Signature=fba71b0a4eaf033ac47c12aaaf310cba5f78280b75e44cb2176d9f3b0e64b794"
},
{
"method": "PUT",
"url": "https://masv3-storage-prod-wdc.s3-accelerate.amazonaws.com/01D3MNHA6P4XFXDMHJDTXT61CB/01D3MP8G3RBA7A1P0K98VEV2V3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJT7DTCC6DRVEACYQ%2F20190214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190214T011216Z&X-Amz-Expires=86399&X-Amz-SignedHeaders=host&partNumber=3&uploadId=Gng8Oh.yKzM41CmGxMRzHkO20GbP2RarZfTJVHHyPGqTVwrHHRgIZKqxMykJBjzDLZ9o3.pyYPwbVbtSI2LDOEa96Mllx6roowA47KHu2GkXcH6PC7qJTFDtYS38SwqI&X-Amz-Signature=0e6c68340af207d43efe23812e831ccc93a0f83a2bbf9ca0181051b9798cd6d8"
}
]
Note
The blueprints returned will always include:
method
url
Sometimes a headers
value is provided. It maps header keys and values that must be included in the request to upload chunks. For example:
{
"headers": {
"x-amz-tagging": "Fragment=true",
},
"method": "PUT",
"url": "https://masv3-storage-prod-wdc.s3-accelerate.amazonaws.com/01D3MNHA6P4XFXDMHJDTXT61CB/01D3MP8G3RBA7A1P0K98VEV2V3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJT7DTCC6DRVEACYQ%2F20190214%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190214T011216Z&X-Amz-Expires=86399&X-Amz-SignedHeaders=host&partNumber=3&uploadId=Gng8Oh.yKzM41CmGxMRzHkO20GbP2RarZfTJVHHyPGqTVwrHHRgIZKqxMykJBjzDLZ9o3.pyYPwbVbtSI2LDOEa96Mllx6roowA47KHu2GkXcH6PC7qJTFDtYS38SwqI&X-Amz-Signature=0e6c68340af207d43efe23812e831ccc93a0f83a2bbf9ca0181051b9798cd6d8"
}
Your upload request must incorporate all properties of the blueprint as provided, or the request will fail.
5) Upload the file chunks
We now begin with the actual upload of your file. Using the blueprint received you can upload each chunk as binary data in the request body.
Continuing with our my_video.mpeg
upload. We want to upload chunks of 100mb, dd
reads in chunk data as bytes. Using a simple converter we can convert 100mb
to 104857600
bytes. We input this into our curl command to tell it how many bytes need to be read.
Info
Note: The <()
is a bit of bash magic to read a byte range of a file without having to physically chunk it
curl -i -s -X BLUE_PRINT_METHOD "BLUE_PRINT_URL" –-data-binary @FILE_CHUNK,
curl -i -s -X PUT BLUEPRINT_URL \
--data-binary @<(dd if=my_video.mpeg skip=0 count=104857600 iflag=skip_bytes,count_bytes)
Where:
Name | Type | Required | Description |
---|---|---|---|
BLUE_PRINT_METHOD |
String | Yes | parameter: method obtained in step 5 |
BLUE_PRINT_URL |
String | Yes | parameter: url obtained in step 5 |
FILE_CHUNK |
String | yes | The file chunk to be uploaded |
We would receive the following response:
Content-Length: 0
Connection: keep-alive
x-amz-id-2: NgcsGsD8A5YSO+uNfp5plgERV1Y1uPuAAz2mHSQB++1g/MdNBYh6kXrK7zkVHzOxhzSmv4jAu1w=
x-amz-request-id: 65CAB8D270C820CB
Date: Thu, 14 Feb 2019 01:47:39 GMT
ETag: "7be1b3cc95a04a6dd07d157ad6fae64a"
Server: AmazonS3
X-Cache: Miss from cloudfront
Via: 1.1 56f9e0effaf2e1930aee6248ba70abb7.cloudfront.net (CloudFront)
X-Amz-Cf-Id: giSjIOdkcJ4qbIAcPBq--uItpHaRXb-8ER4YUjvQcx-yKTUOjNRQ4w==
Note
For each uploaded chunk, the following metadata must be collected:
partNumber
which is part of the chunk upload request URL parametersETag
which is returned as a response header by the storage service
6) Publish upload progress
Note
This feature is only available to customers with this feature enabled. To request access to upload progress API and channels for your team, please contact team@masv.io.
MASV integrates with PubNub to indicate overall file upload progress on each package. These progress updates are posted to the progress channel returned in the Create Package response.
Using the credentials returned for the upload
topic in the progress_channels
property of the response, messages should be publishing following this format:
{
"completed": 123544,
"total": 1073741824,
"speed_bps": 123,
"eta": 301.054
}
Properties:
Name | Type | Required | Description |
---|---|---|---|
completed |
String | Yes | Current number of bytes successfully uploaded. |
total |
String | Yes | Total size of all files in package that will be uploaded format: bytes |
speed_bps |
String | Yes | Rolling average of bits per second |
eta |
String | Yes | Estimated time of arrival based on speed_bps in seconds. |
Note
For more information on integrating PubNub into your client/service refer to their extensive developer documentation.
Warning
PubNub has recently migrated their authentication mechanism to rely on tokens instead of authentication codes. The progress_channels
auth.token
property should be used for all authentication requests with PubNub and the now deprecated auth.auth_key
property should no longer be used at all. More details on how to switch from auth_key
to token
can be found on their migration guide.
7) Finalize the file
Once all chunks have been uploaded, the client must inform the API that the file is successfully uploaded to the cloud storage service. This call informs the MASV backend that all uploading for the file is completed and must supply all collected metadata needed to seal the file in storage.
Method | Route |
---|---|
POST |
/packages/{package_id}/files/{file_id}/finalize |
HEADERS
Name | Type | Required | Description |
---|---|---|---|
X-Package-Token |
String | Yes | Package JSON Web Token |
Content-Type |
String | Yes | Must be application/json |
BODY
Name | Type | Required | Description |
---|---|---|---|
chunk_extras |
Array Of Objects | Yes | Information about all the chunks that make up the file |
chunk_extras[$index].partNumber |
String | Yes | Chunk part number |
chunk_extras[$index].etag |
String | Yes | Chunk part hash |
chunk_extras[$index].file_extras |
Array of Objects | Yes | Chunk part extras |
file_extras.uploadId |
String | Yes | The ID of the upload provided in from step 3 |
size |
Integer | Yes | The file size |
chunk_size |
Integer | * | The chunk size used when uploading the file. There is a performance impact if this field is not provided. Providing this field ensures the generation of a manifest file used to verify file integrity. In custom upload implementations, this field should always be provided. |
REQUEST
curl -d '{"chunk_extras":[{"partNumber": CHUNK_PART_NUMBER, "etag": CHUNK_ETAG}], "file_extras":{"upload_id": UPLOAD_ID}, "size": FILE_SIZE, "chunk_size": CHUNK_SIZE}' \
-H "X-Package-Token: PACKAGE_TOKEN" \
-H "Content-Type: application/json" \
-s -X POST https://api.massive.app/v1/packages/PACKAGE_ID/files/FILE_ID/finalize
For our my_video.mpeg
example, our JSON body would be similar to below:
{
"size": 147778580,
"file_extras": {
"upload_id": "Gng8Oh.yKzM41CmGxMRzHkO20GbP2RarZfTJVHHyPGqTVwrHHRgIZKqxMykJBjzDLZ9o3.pyYPwbVbtSI2LDOEa96Mllx6roowA47KHu2GkXcH6PC7qJTFDtYS38SwqI"
},
"chunk_extras": [
{
"partNumber": "1",
"etag": "\"7be1b3cc95a04a6dd07d157ad6fae64a\""
},
{
"partNumber": "2",
"etag": "\"ca8e85fc588f90d07449d0303e9cf329\""
},
{
"partNumber": "3",
"etag": "\"a6a6aa6836a7a70c28a06dec02dbf199\""
}
]
}
On success, the server will respond with an empty body and a 204 No Content
status code.
Note
The client has to repeat steps 2-7 for each additional file in the package.
Attention
If the file size submitted in this request does not match the actual size of the object in storage the service will respond with a 409 Conflict
status code.
8) Finalize the package
Once all files have been uploaded and finalized, the client must indicate that the package has been completed and ready to be dispatched to the intended recipient.
This call informs the MASV backend that the package is ready to be dispatched to the intended recipient. The package will be dispatched to the intended recipient immediately upon the finalize call.
Once a package is finalized, MASV sets the expiry times of its package and links.
Attention
Make sure all uploaded files have been completed and finalized before finalizing the package. Any in-flight, non-finalized files will be dropped when finalize package is called. If no files were uploaded/finalized, this API call will fail.
POST /packages/{package_id}/finalize
HEADERS
Name | Type | Required | Description |
---|---|---|---|
X-Package-Token |
String | Yes | Package JSON Web Token |
Content-Type |
String | Yes | Must be application/json |
URL Parameters
Name | Type | Required | Description |
---|---|---|---|
package_id |
String | yes | Package ID obtained in step 1 |
REQUEST
curl -H "X-Package-Token: $PACKAGE_TOKEN" \
-H "Content-Type: application/json" \
-s -X POST https://api.massive.app/v1/packages/PACKAGE_ID/finalize
On success, the server will respond with an empty body and a 204 No Content
status code.
Creating Additional Links
MASV's API allows you to create additional direct-download links or send a link to a specific email recipient after the upload has been finalized. See the Links page for more details and examples.