curl --request POST \
--url https://api.trycarhub.com/v1/uploads \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"endpoint": "vehicle.pose",
"content_type": "image/jpeg",
"filename": "vehicle-front-left.jpg"
}
'import requests
url = "https://api.trycarhub.com/v1/uploads"
payload = {
"endpoint": "vehicle.pose",
"content_type": "image/jpeg",
"filename": "vehicle-front-left.jpg"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
endpoint: 'vehicle.pose',
content_type: 'image/jpeg',
filename: 'vehicle-front-left.jpg'
})
};
fetch('https://api.trycarhub.com/v1/uploads', 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.trycarhub.com/v1/uploads",
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([
'endpoint' => 'vehicle.pose',
'content_type' => 'image/jpeg',
'filename' => 'vehicle-front-left.jpg'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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.trycarhub.com/v1/uploads"
payload := strings.NewReader("{\n \"endpoint\": \"vehicle.pose\",\n \"content_type\": \"image/jpeg\",\n \"filename\": \"vehicle-front-left.jpg\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.trycarhub.com/v1/uploads")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"endpoint\": \"vehicle.pose\",\n \"content_type\": \"image/jpeg\",\n \"filename\": \"vehicle-front-left.jpg\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.trycarhub.com/v1/uploads")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"endpoint\": \"vehicle.pose\",\n \"content_type\": \"image/jpeg\",\n \"filename\": \"vehicle-front-left.jpg\"\n}"
response = http.request(request)
puts response.read_body{
"object": "upload",
"upload_ref": "upl_7Qm2hV9tXbLd",
"url": "<string>",
"expires_at": 1755300000,
"max_bytes": 524288000,
"method": "PUT",
"headers": {},
"fields": {}
}Create a presigned upload
With size_bytes, reserves that exact amount and returns a signed PUT whose
Content-Length must match. Without it, reserves the endpoint maximum and returns
a signed multipart POST with a content-length-range policy. Send every returned
header or field, then pass upload_ref to a model endpoint. The first use performs
a storage HEAD and verifies accounting automatically; no completion call is needed.
A POST link keeps its maximum capacity accounted until expiry because it can still
overwrite the object. The next use after expiry reconciles its actual size.
Required for videos and batches; optional (but cheaper on retries) for images. The reference belongs to one organisation and remains reusable until manual deletion or retention. An unused slot stays reserved after URL expiry; delete it to release quota.
curl --request POST \
--url https://api.trycarhub.com/v1/uploads \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"endpoint": "vehicle.pose",
"content_type": "image/jpeg",
"filename": "vehicle-front-left.jpg"
}
'import requests
url = "https://api.trycarhub.com/v1/uploads"
payload = {
"endpoint": "vehicle.pose",
"content_type": "image/jpeg",
"filename": "vehicle-front-left.jpg"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
endpoint: 'vehicle.pose',
content_type: 'image/jpeg',
filename: 'vehicle-front-left.jpg'
})
};
fetch('https://api.trycarhub.com/v1/uploads', 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.trycarhub.com/v1/uploads",
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([
'endpoint' => 'vehicle.pose',
'content_type' => 'image/jpeg',
'filename' => 'vehicle-front-left.jpg'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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.trycarhub.com/v1/uploads"
payload := strings.NewReader("{\n \"endpoint\": \"vehicle.pose\",\n \"content_type\": \"image/jpeg\",\n \"filename\": \"vehicle-front-left.jpg\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.trycarhub.com/v1/uploads")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"endpoint\": \"vehicle.pose\",\n \"content_type\": \"image/jpeg\",\n \"filename\": \"vehicle-front-left.jpg\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.trycarhub.com/v1/uploads")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"endpoint\": \"vehicle.pose\",\n \"content_type\": \"image/jpeg\",\n \"filename\": \"vehicle-front-left.jpg\"\n}"
response = http.request(request)
puts response.read_body{
"object": "upload",
"upload_ref": "upl_7Qm2hV9tXbLd",
"url": "<string>",
"expires_at": 1755300000,
"max_bytes": 524288000,
"method": "PUT",
"headers": {},
"fields": {}
}Authorizations
Organisation API key, sent as Authorization: Bearer chk_live_….
chk_live_…— production inference, live credits,livemode: true,is_staging: false.chk_stg_…— real inference classified as staging, the same live credits,livemode: true,is_staging: true.chk_test_…— identical pipeline against the fixture backend, test credits,livemode: false.
Keys are stored hashed; only the prefix (chk_live_a1b2…) is ever displayed again. A key
may be scoped to a subset of endpoint families — calling outside its scopes is a 403.
Headers
Client-generated key (a UUID is ideal) making this POST safe to retry. The first request
is executed and its response stored for 24 h; any replay with the same key returns that
stored response with Idempotent-Replayed: true and bills nothing. Reusing a key with a
different payload is a 409 idempotency_key_reuse.
8 - 255Body
Endpoint the upload is destined for. Required: the size ceiling written into the presigned URL is per endpoint, and a video asset is not held to the same limit as a plate photo.
vehicle.pose, vehicle.segment, vehicle.identify, vin.read, driver_license.read, plate.read, document.read, dashboard.read, engine.detect, damage.detect, damage.severity, tire.read, tire.detect, parts.recognize, parts.segment, parts.decompose, pricing.vehicle, pricing.repair, render.background, render.enhance, render.plate, render.360, render.video, render.hotspots MIME type of the file you are about to upload. Sent verbatim on the PUT.
"video/mp4"
"image/jpeg"
"audio/wav"
Kept for your own traceability; never interpreted.
255Optional exact byte length. Omit it to receive a bounded multipart POST.
x >= 1Response
Upload slot created.
"upload"Pass this as input_ref / input_refs once the bytes are uploaded.
^upl_[1-9A-HJ-NP-Za-km-z]{8,32}$"upl_7Qm2hV9tXbLd"
Presigned PUT target or POST form action.
After this instant the URL is refused and a new upload must be created.
1755300000
Hard ceiling enforced by storage, in bytes.
x >= 1524288000
PUT, POST Signed headers required for a PUT response.
Show child attributes
Show child attributes
Form fields to append before file for a POST response.
Show child attributes
Show child attributes