Complete Multipart Upload
curl --request POST \
--url https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete \
--header 'Content-Type: application/json' \
--data '
{
"parts": [
{}
]
}
'import requests
url = "https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete"
payload = { "parts": [{}] }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({parts: [{}]})
};
fetch('https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'parts' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete"
payload := strings.NewReader("{\n \"parts\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete")
.header("Content-Type", "application/json")
.body("{\n \"parts\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"parts\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"code": 200,
"message": "Upload finalized",
"data": {
"file": {
"id": "11111111-1111-4111-8111-111111111111",
"url": "https://cdn.vireel.io/verified-media/.../11111111-1111-4111-8111-111111111111.mp4",
"contentType": "video/mp4",
"filename": "phone-footage.mp4",
"size": 367001600,
"createdAt": "2026-10-02T12:00:00.000Z"
},
"attestation": {
"sha256": "…",
"sizeBytes": 367001600,
"durationMs": 61250,
"width": 2160,
"height": 3840,
"hasVideoStream": true,
"hasAudioStream": true
}
}
}
Files
Complete Multipart Upload
Assemble uploaded parts, verify the media, and create the CDN-backed Media Library record.
POST
/
api
/
partner
/
v1
/
files
/
{fileId}
/
multipart
/
complete
Complete Multipart Upload
curl --request POST \
--url https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete \
--header 'Content-Type: application/json' \
--data '
{
"parts": [
{}
]
}
'import requests
url = "https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete"
payload = { "parts": [{}] }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({parts: [{}]})
};
fetch('https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'parts' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete"
payload := strings.NewReader("{\n \"parts\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete")
.header("Content-Type", "application/json")
.body("{\n \"parts\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/partner/v1/files/{fileId}/multipart/complete")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"parts\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"code": 200,
"message": "Upload finalized",
"data": {
"file": {
"id": "11111111-1111-4111-8111-111111111111",
"url": "https://cdn.vireel.io/verified-media/.../11111111-1111-4111-8111-111111111111.mp4",
"contentType": "video/mp4",
"filename": "phone-footage.mp4",
"size": 367001600,
"createdAt": "2026-10-02T12:00:00.000Z"
},
"attestation": {
"sha256": "…",
"sizeBytes": 367001600,
"durationMs": 61250,
"width": 2160,
"height": 3840,
"hasVideoStream": true,
"hasAudioStream": true
}
}
}
Completing a multipart upload assembles the parts and runs the same verification as
Send a stable
POST /files/{fileId}/finalize: the stored bytes must decode as the declared media type, and
videos must contain a playable video stream. Only then is the CDN file.url created.
Body Parameters
array
required
Every part from Start Multipart Upload, each once:
{ "partNumber": 1, "etag": "\"…\"" }. etag is the ETag response header from that part’s
PUT, quoted or not. Order does not matter.Idempotency-Key. Retrying with the same key returns the same finalized file.
Response
200 with file (id, url, contentType, filename, size, createdAt). Videos also
include attestation with the verified durationMs, width, height, and sha256.
{
"ok": true,
"code": 200,
"message": "Upload finalized",
"data": {
"file": {
"id": "11111111-1111-4111-8111-111111111111",
"url": "https://cdn.vireel.io/verified-media/.../11111111-1111-4111-8111-111111111111.mp4",
"contentType": "video/mp4",
"filename": "phone-footage.mp4",
"size": 367001600,
"createdAt": "2026-10-02T12:00:00.000Z"
},
"attestation": {
"sha256": "…",
"sizeBytes": 367001600,
"durationMs": 61250,
"width": 2160,
"height": 3840,
"hasVideoStream": true,
"hasAudioStream": true
}
}
}
Error Responses
400 upload_failed- a part is missing, listed twice, or itsetagdoes not match the uploaded part; the upload expired or was aborted; or the bytes failed verification400 invalid_request-Idempotency-Keyis missing or invalid422 invalid_payload-partsis empty or malformed
