Complete a Direct Blob Upload
curl --request POST \
--url https://blob.vertracloud.app/v1/uploads/{upload_id}/completeimport requests
url = "https://blob.vertracloud.app/v1/uploads/{upload_id}/complete"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://blob.vertracloud.app/v1/uploads/{upload_id}/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://blob.vertracloud.app/v1/uploads/{upload_id}/complete",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://blob.vertracloud.app/v1/uploads/{upload_id}/complete"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://blob.vertracloud.app/v1/uploads/{upload_id}/complete")
.asString();require 'uri'
require 'net/http'
url = URI("https://blob.vertracloud.app/v1/uploads/{upload_id}/complete")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_body{
"response": {
"id": "0b6f7c1e-3d2a-4f5b-9a8c-1d2e3f4a5b6c",
"path": "images/logo.png",
"size": 48213,
"content_type": "image/png",
"etag": "9b2cf535f27731c974343645a3085d39",
"sha256": null,
"visibility": "public",
"url": "https://blob.vertracloud.dev/k7p2m9x4qa/images/logo.png",
"cache_control": null,
"content_disposition": "inline",
"filename": null,
"metadata": {},
"expires_at": null,
"created_at": "2026-10-14T12:00:00.000Z",
"updated_at": "2026-10-14T12:00:00.000Z"
}
}
Uploads
Complete a Direct Blob Upload
Checks the stored bytes and creates the blob object. Call it after every part is stored. Accepts an upload token.
POST
/
v1
/
uploads
/
{upload_id}
/
complete
Complete a Direct Blob Upload
curl --request POST \
--url https://blob.vertracloud.app/v1/uploads/{upload_id}/completeimport requests
url = "https://blob.vertracloud.app/v1/uploads/{upload_id}/complete"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://blob.vertracloud.app/v1/uploads/{upload_id}/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://blob.vertracloud.app/v1/uploads/{upload_id}/complete",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://blob.vertracloud.app/v1/uploads/{upload_id}/complete"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://blob.vertracloud.app/v1/uploads/{upload_id}/complete")
.asString();require 'uri'
require 'net/http'
url = URI("https://blob.vertracloud.app/v1/uploads/{upload_id}/complete")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_body{
"response": {
"id": "0b6f7c1e-3d2a-4f5b-9a8c-1d2e3f4a5b6c",
"path": "images/logo.png",
"size": 48213,
"content_type": "image/png",
"etag": "9b2cf535f27731c974343645a3085d39",
"sha256": null,
"visibility": "public",
"url": "https://blob.vertracloud.dev/k7p2m9x4qa/images/logo.png",
"cache_control": null,
"content_disposition": "inline",
"filename": null,
"metadata": {},
"expires_at": null,
"created_at": "2026-10-14T12:00:00.000Z",
"updated_at": "2026-10-14T12:00:00.000Z"
}
}
Finishes a direct upload. The API checks that every part is stored, that the total size matches, that the checksum matches if you sent one, and that the content isn’t an executable. Then it creates the object and returns it. Until this call succeeds, the object doesn’t exist.
Scope:
blob:write. Accepts an upload token.
Upload token: this route also accepts an upload token (Authorization: Bearer vcup_...) instead of an API key.
string
required
The upload ID.
string
The workspace ID, when the blob belongs to a workspace. The call acts on the blob of the workspace owner and needs the
blob:read workspace permission (blob:manage on routes that change data). Leave it out to act on your own blob.Example request
curl -X POST "https://blob.vertracloud.app/v1/uploads/7c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f/complete" \
-H "Authorization: Bearer $VERTRA_API_KEY"
Response
object
The created blob object.
Show properties
Show properties
string
Stable ID of the object. It never changes, even when the path, visibility or expiration does.
string
Full path inside the account, for example
images/avatars/joao.png.number
Size in bytes.
string
MIME type, derived on the server from the file extension.
string
Entity tag of the stored content.
string | null
SHA-256 of the content in hex, when the upload sent a checksum.
null otherwise.string
public (readable through its public URL) or private (needs a temporary link or a share).string | null
Public URL.
null while the object is private.string | null
immutable, max-age=<seconds> or no-cache (no-cache needs Enterprise-26 or higher). null means the default.string
inline (shown by the browser) or attachment (downloaded). HTML, SVG and XML are always attachment.string | null
Name suggested on download.
null uses the last segment of path.object
Up to 5 keys, each
[a-z0-9-]{1,64}, 512 bytes in total. Enterprise-12 or higher; {} on other plans.string | null
When the object is deleted, as an ISO 8601 timestamp.
null means no expiration.string
ISO 8601 creation time.
string
ISO 8601 time of the last change.
{
"response": {
"id": "0b6f7c1e-3d2a-4f5b-9a8c-1d2e3f4a5b6c",
"path": "images/logo.png",
"size": 48213,
"content_type": "image/png",
"etag": "9b2cf535f27731c974343645a3085d39",
"sha256": null,
"visibility": "public",
"url": "https://blob.vertracloud.dev/k7p2m9x4qa/images/logo.png",
"cache_control": null,
"content_disposition": "inline",
"filename": null,
"metadata": {},
"expires_at": null,
"created_at": "2026-10-14T12:00:00.000Z",
"updated_at": "2026-10-14T12:00:00.000Z"
}
}
Error Responses
string
The error code indicating the reason for failure. Possible values:
AUTH_TOKEN_MISSING(401): NoAuthorizationheader was sent.API_KEY_INVALID(401): The API key doesn’t exist, was revoked, or its secret doesn’t match.API_KEY_SCOPE_DENIED(403): The key lacks the scope this route needs (blob:readorblob:write);details.requirednames it.WORKSPACE_PERMISSION_DENIED(403): Only withworkspace_id: you’re not a member, or your role lacks the blob permission.WORKSPACE_NOT_FOUND(404): Only withworkspace_id: no workspace with that ID, or you’re not a member.BLOB_UPLOAD_TOKEN_INVALID(401): The upload token doesn’t exist, has expired or was revoked (only with an upload token).BLOB_UPLOAD_NOT_FOUND(404): No upload with that ID in this account, or it was already completed or aborted.BLOB_UPLOAD_EXPIRED(410): The upload’s time limit has passed. Start a new upload.BLOB_UPLOAD_INCOMPLETE(409): Parts are missing, or the stored size differs from the declaredsize.BLOB_CHECKSUM_MISMATCH(400): The stored content doesn’t match thechecksum_sha256sent at start.BLOCKED_FILE_TYPE(422): The content is an executable, detected from its bytes.BLOB_OBJECT_ALREADY_EXISTS(409):pathwas taken by another object while the upload was running, andoverwriteis nottrue.
{
"code": "AUTH_TOKEN_MISSING"
}
{
"code": "API_KEY_SCOPE_DENIED"
}
{
"code": "WORKSPACE_NOT_FOUND"
}
{
"code": "BLOB_UPLOAD_EXPIRED"
}
{
"code": "BLOB_UPLOAD_INCOMPLETE"
}
{
"code": "BLOB_CHECKSUM_MISMATCH"
}
{
"code": "BLOCKED_FILE_TYPE"
}