Create an Upload Token for Browser Uploads
curl --request POST \
--url https://blob.vertracloud.app/v1/upload-tokens \
--header 'Content-Type: application/json' \
--data '
{
"path": "<string>",
"prefix": "<string>",
"visibility": "<string>",
"max_size": 123,
"allowed_extensions": [
"<string>"
],
"cache_control": "<string>",
"content_disposition": "<string>",
"expires_at": "<string>",
"metadata": {},
"overwrite": true,
"expires_in": 123,
"max_uses": 123
}
'import requests
url = "https://blob.vertracloud.app/v1/upload-tokens"
payload = {
"path": "<string>",
"prefix": "<string>",
"visibility": "<string>",
"max_size": 123,
"allowed_extensions": ["<string>"],
"cache_control": "<string>",
"content_disposition": "<string>",
"expires_at": "<string>",
"metadata": {},
"overwrite": True,
"expires_in": 123,
"max_uses": 123
}
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({
path: '<string>',
prefix: '<string>',
visibility: '<string>',
max_size: 123,
allowed_extensions: ['<string>'],
cache_control: '<string>',
content_disposition: '<string>',
expires_at: '<string>',
metadata: {},
overwrite: true,
expires_in: 123,
max_uses: 123
})
};
fetch('https://blob.vertracloud.app/v1/upload-tokens', 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/upload-tokens",
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([
'path' => '<string>',
'prefix' => '<string>',
'visibility' => '<string>',
'max_size' => 123,
'allowed_extensions' => [
'<string>'
],
'cache_control' => '<string>',
'content_disposition' => '<string>',
'expires_at' => '<string>',
'metadata' => [
],
'overwrite' => true,
'expires_in' => 123,
'max_uses' => 123
]),
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/upload-tokens"
payload := strings.NewReader("{\n \"path\": \"<string>\",\n \"prefix\": \"<string>\",\n \"visibility\": \"<string>\",\n \"max_size\": 123,\n \"allowed_extensions\": [\n \"<string>\"\n ],\n \"cache_control\": \"<string>\",\n \"content_disposition\": \"<string>\",\n \"expires_at\": \"<string>\",\n \"metadata\": {},\n \"overwrite\": true,\n \"expires_in\": 123,\n \"max_uses\": 123\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://blob.vertracloud.app/v1/upload-tokens")
.header("Content-Type", "application/json")
.body("{\n \"path\": \"<string>\",\n \"prefix\": \"<string>\",\n \"visibility\": \"<string>\",\n \"max_size\": 123,\n \"allowed_extensions\": [\n \"<string>\"\n ],\n \"cache_control\": \"<string>\",\n \"content_disposition\": \"<string>\",\n \"expires_at\": \"<string>\",\n \"metadata\": {},\n \"overwrite\": true,\n \"expires_in\": 123,\n \"max_uses\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://blob.vertracloud.app/v1/upload-tokens")
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 \"path\": \"<string>\",\n \"prefix\": \"<string>\",\n \"visibility\": \"<string>\",\n \"max_size\": 123,\n \"allowed_extensions\": [\n \"<string>\"\n ],\n \"cache_control\": \"<string>\",\n \"content_disposition\": \"<string>\",\n \"expires_at\": \"<string>\",\n \"metadata\": {},\n \"overwrite\": true,\n \"expires_in\": 123,\n \"max_uses\": 123\n}"
response = http.request(request)
puts response.read_body{
"response": {
"token": "vcup_9f2c7d1e4b8a6035c2f1d9e7a4b3c8d2",
"expires_at": "2026-10-14T12:15:00.000Z",
"max_uses": 1
}
}
Account
Create an Upload Token for Browser Uploads
Creates a short-lived upload token that fixes the path or folder, size, extensions and visibility. Use it in browsers instead of your API key.
POST
/
v1
/
upload-tokens
Create an Upload Token for Browser Uploads
curl --request POST \
--url https://blob.vertracloud.app/v1/upload-tokens \
--header 'Content-Type: application/json' \
--data '
{
"path": "<string>",
"prefix": "<string>",
"visibility": "<string>",
"max_size": 123,
"allowed_extensions": [
"<string>"
],
"cache_control": "<string>",
"content_disposition": "<string>",
"expires_at": "<string>",
"metadata": {},
"overwrite": true,
"expires_in": 123,
"max_uses": 123
}
'import requests
url = "https://blob.vertracloud.app/v1/upload-tokens"
payload = {
"path": "<string>",
"prefix": "<string>",
"visibility": "<string>",
"max_size": 123,
"allowed_extensions": ["<string>"],
"cache_control": "<string>",
"content_disposition": "<string>",
"expires_at": "<string>",
"metadata": {},
"overwrite": True,
"expires_in": 123,
"max_uses": 123
}
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({
path: '<string>',
prefix: '<string>',
visibility: '<string>',
max_size: 123,
allowed_extensions: ['<string>'],
cache_control: '<string>',
content_disposition: '<string>',
expires_at: '<string>',
metadata: {},
overwrite: true,
expires_in: 123,
max_uses: 123
})
};
fetch('https://blob.vertracloud.app/v1/upload-tokens', 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/upload-tokens",
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([
'path' => '<string>',
'prefix' => '<string>',
'visibility' => '<string>',
'max_size' => 123,
'allowed_extensions' => [
'<string>'
],
'cache_control' => '<string>',
'content_disposition' => '<string>',
'expires_at' => '<string>',
'metadata' => [
],
'overwrite' => true,
'expires_in' => 123,
'max_uses' => 123
]),
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/upload-tokens"
payload := strings.NewReader("{\n \"path\": \"<string>\",\n \"prefix\": \"<string>\",\n \"visibility\": \"<string>\",\n \"max_size\": 123,\n \"allowed_extensions\": [\n \"<string>\"\n ],\n \"cache_control\": \"<string>\",\n \"content_disposition\": \"<string>\",\n \"expires_at\": \"<string>\",\n \"metadata\": {},\n \"overwrite\": true,\n \"expires_in\": 123,\n \"max_uses\": 123\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://blob.vertracloud.app/v1/upload-tokens")
.header("Content-Type", "application/json")
.body("{\n \"path\": \"<string>\",\n \"prefix\": \"<string>\",\n \"visibility\": \"<string>\",\n \"max_size\": 123,\n \"allowed_extensions\": [\n \"<string>\"\n ],\n \"cache_control\": \"<string>\",\n \"content_disposition\": \"<string>\",\n \"expires_at\": \"<string>\",\n \"metadata\": {},\n \"overwrite\": true,\n \"expires_in\": 123,\n \"max_uses\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://blob.vertracloud.app/v1/upload-tokens")
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 \"path\": \"<string>\",\n \"prefix\": \"<string>\",\n \"visibility\": \"<string>\",\n \"max_size\": 123,\n \"allowed_extensions\": [\n \"<string>\"\n ],\n \"cache_control\": \"<string>\",\n \"content_disposition\": \"<string>\",\n \"expires_at\": \"<string>\",\n \"metadata\": {},\n \"overwrite\": true,\n \"expires_in\": 123,\n \"max_uses\": 123\n}"
response = http.request(request)
puts response.read_body{
"response": {
"token": "vcup_9f2c7d1e4b8a6035c2f1d9e7a4b3c8d2",
"expires_at": "2026-10-14T12:15:00.000Z",
"max_uses": 1
}
}
Creates an upload token that a browser or another untrusted client can use to upload, without your API key. The token fixes every constraint you send: a
path for one file or a prefix for any file in a folder, the largest size, the allowed extensions and the visibility. It works only on the upload routes, and it stops working when the API key that created it is revoked. The token is shown once.
Scope: blob:write.
string
Exact path the token can upload to. Use this or
prefix, not both.string
Folder the token can upload into. The random suffix is forced. Use this or
path, not both.string
public or private. Fixed by the token.number
Largest file the token accepts, in bytes.
string[]
1–20 extensions, lowercase, without the dot.
string
Fixed
cache_control for the uploaded objects.string
Fixed
inline or attachment for the uploaded objects.string
ISO 8601 timestamp for deletion of the uploaded objects.
object
Fixed metadata for the uploaded objects. Enterprise-12 or higher.
boolean
Let uploads with this token replace an existing file. Default
false: without it, the token never overwrites a file that already exists.number
Seconds the token stays valid, 60–3600. Default
900.number
How many uploads the token allows, 1–100. Default
1.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/upload-tokens" \
-H "Authorization: Bearer $VERTRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prefix":"avatars/","max_size":5000000,"allowed_extensions":["png","jpg","webp"],"expires_in":900}'
Response
object
{
"response": {
"token": "vcup_9f2c7d1e4b8a6035c2f1d9e7a4b3c8d2",
"expires_at": "2026-10-14T12:15:00.000Z",
"max_uses": 1
}
}
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_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.VALIDATION_ERROR(400): A parameter or body field failed validation.details.pathnames the field.PLAN_RESTRICTED_FEATURE(403):metadatawas sent on a plan below Enterprise-12.RATE_LIMIT_EXCEEDED(429): More than 60 tokens created in a minute;details.retry_aftersays how many seconds to wait.
{
"code": "AUTH_TOKEN_MISSING"
}
{
"code": "API_KEY_SCOPE_DENIED"
}
{
"code": "WORKSPACE_NOT_FOUND"
}
{
"code": "VALIDATION_ERROR"
}