Upload Blob Object in One Request
curl --request POST \
--url https://blob.vertracloud.app/v1/objects \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://blob.vertracloud.app/v1/objects"
payload = {}
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({})
};
fetch('https://blob.vertracloud.app/v1/objects', 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/objects",
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([
]),
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://blob.vertracloud.app/v1/objects"
payload := strings.NewReader("{}")
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://blob.vertracloud.app/v1/objects")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://blob.vertracloud.app/v1/objects")
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 = "{}"
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"
}
}
Objects
Upload Blob Object in One Request
Uploads one file of up to 100 MB in a single multipart request and stores it at a path. Accepts an upload token.
POST
/
v1
/
objects
Upload Blob Object in One Request
curl --request POST \
--url https://blob.vertracloud.app/v1/objects \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://blob.vertracloud.app/v1/objects"
payload = {}
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({})
};
fetch('https://blob.vertracloud.app/v1/objects', 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/objects",
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([
]),
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://blob.vertracloud.app/v1/objects"
payload := strings.NewReader("{}")
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://blob.vertracloud.app/v1/objects")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://blob.vertracloud.app/v1/objects")
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 = "{}"
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 one file of up to 100 MB in a single
multipart/form-data request and stores it at path. For larger files use Start a direct upload. Responds with 201 and the new object.
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
Destination path in the account. 1–1024 characters, up to 16 segments of
[A-Za-z0-9._ -] (1–255 characters each), no leading or trailing /, no // or .., and an extension is required. Rejected paths return VALIDATION_ERROR; they are never changed silently.string
public or private. Default private.boolean
Replace an object already at
path. Default false: a taken path returns BLOB_OBJECT_ALREADY_EXISTS.boolean
When
path is taken, save the file as name-1.ext, name-2.ext and so on (the first free name) instead of returning BLOB_OBJECT_ALREADY_EXISTS. The response carries the final path. Default false. Can’t be combined with overwrite, and has no effect with an upload token that fixes the path.boolean
Insert
- plus 10 random base-36 characters before the extension. Default false. Forced to true for an upload token without a fixed path.string
immutable, max-age=<seconds> or no-cache (Enterprise-26 or higher). Default: no value set.string
inline or attachment. Default: inline. Files ending in .html, .htm, .svg, .xml or .xhtml are always served as attachment.string
Name suggested when the file is downloaded. Default: the last segment of
path.string
ISO 8601 timestamp when the object is deleted. Must be at least the plan’s minimum from now (7 days, or 1 hour on Enterprise-26 or higher).
string
JSON object, URL-encoded: up to 5 keys, each
[a-z0-9-]{1,64}, 512 bytes in total. Enterprise-12 or higher.string
Hex SHA-256 of the file. A mismatch returns
BLOB_CHECKSUM_MISMATCH and nothing is stored.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.file
required
The file, as the only
multipart/form-data part. Up to 100 MB; the plan’s limit applies.Example request
curl -X POST "https://blob.vertracloud.app/v1/objects?path=images/logo.png&visibility=public" \
-H "Authorization: Bearer $VERTRA_API_KEY" \
-F "file=@./logo.png"
Response
object
The stored object. Responds with
201.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_CHECKSUM_MISMATCH(400): Thechecksum_sha256doesn’t match the file. Nothing was stored.VALIDATION_ERROR(400): A query parameter failed validation, for example an invalidpath.details.pathnames it.BLOB_OBJECT_ALREADY_EXISTS(409):pathis taken andoverwriteis nottrue.BLOB_FILE_TOO_LARGE(413): The file is above 100 MB, the plan’s limit for this route, or the rule’s or token’smax_size.STORAGE_QUOTA_EXCEEDED(403): Storing the file would exceed the plan’s blob quota.BLOCKED_FILE_TYPE(422): The extension is blocked, not allowed by the folder rule or token, or the file is an executable.BLOB_NAMESPACE_SUSPENDED(403): The account’s blob storage is suspended, or its plan expired. Uploads and other writes are refused from the start of the 72-hour grace period.PLAN_RESTRICTED_FEATURE(403):metadata(below Enterprise-12) orcache_control: no-cache(below Enterprise-26) isn’t included in the plan.BLOB_UPLOAD_TOKEN_CONSTRAINT(403): Only with an upload token: the path, folder or another option the token fixes doesn’t match the request.BLOB_UPLOAD_TOKEN_EXHAUSTED(401): Only with an upload token: it has used all its uses.RATE_LIMIT_EXCEEDED(429): Below Enterprise-12, uploads are limited to 1 per second.details.retry_aftergives the wait.
{
"code": "AUTH_TOKEN_MISSING"
}
{
"code": "API_KEY_SCOPE_DENIED"
}
{
"code": "WORKSPACE_NOT_FOUND"
}
{
"code": "BLOB_CHECKSUM_MISMATCH"
}
{
"code": "BLOB_OBJECT_ALREADY_EXISTS"
}
{
"code": "BLOB_FILE_TOO_LARGE"
}
{
"code": "BLOCKED_FILE_TYPE"
}
{
"code": "RATE_LIMIT_EXCEEDED"
}