curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"fileName": "<string>",
"contentType": "<string>",
"sizeBytes": 2
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets"
payload = {
"fileName": "<string>",
"contentType": "<string>",
"sizeBytes": 2
}
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({fileName: '<string>', contentType: '<string>', sizeBytes: 2})
};
fetch('https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets', 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.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets",
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([
'fileName' => '<string>',
'contentType' => '<string>',
'sizeBytes' => 2
]),
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.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets"
payload := strings.NewReader("{\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sizeBytes\": 2\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.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sizeBytes\": 2\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets")
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 \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sizeBytes\": 2\n}"
response = http.request(request)
puts response.read_body{
"asset": {
"id": "<string>",
"fileName": "<string>",
"contentType": "<string>",
"sizeBytes": 123,
"status": "pending",
"createdMs": 123
},
"uploadUrl": "<string>",
"uploadExpiresAtMs": 123
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Create recommendation asset upload
Registers a media asset (screenshot or video) on the error’s recommendation and returns a short-lived presigned URL the browser PUTs the file to directly — the API never proxies the bytes. The upload must send exactly the declared content type. After uploading, call the confirm endpoint to verify the object and make the asset servable. Fails when no assets store is configured in this environment.
Required API key permission: observe:errors:write.
curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"fileName": "<string>",
"contentType": "<string>",
"sizeBytes": 2
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets"
payload = {
"fileName": "<string>",
"contentType": "<string>",
"sizeBytes": 2
}
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({fileName: '<string>', contentType: '<string>', sizeBytes: 2})
};
fetch('https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets', 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.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets",
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([
'fileName' => '<string>',
'contentType' => '<string>',
'sizeBytes' => 2
]),
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.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets"
payload := strings.NewReader("{\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sizeBytes\": 2\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.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sizeBytes\": 2\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/errors/{errorID}/recommendation/assets")
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 \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sizeBytes\": 2\n}"
response = http.request(request)
puts response.read_body{
"asset": {
"id": "<string>",
"fileName": "<string>",
"contentType": "<string>",
"sizeBytes": 123,
"status": "pending",
"createdMs": 123
},
"uploadUrl": "<string>",
"uploadExpiresAtMs": 123
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Authorizations
Use an Observe API key from the management console. The key selects the project and must grant the permission listed on the operation. Keep this key on your server.
Path Parameters
Error ID (format err-<number>).
Body
Original file name, for display.
MIME type of the upload. Allowed: image/png, image/jpeg, image/webp, image/gif (up to 20 MB) and video/mp4, video/webm, video/quicktime (up to 500 MB). The presigned URL is bound to it — the browser's PUT must send exactly this content type.
Exact file size in bytes; verified against the uploaded object on confirm.
x >= 1Response
The pending asset plus the presigned upload URL.
One user-uploaded media file (screenshot or video). The bytes live in a private object store; viewing goes through short-lived presigned URLs (see the downloadUrl endpoint), so this object carries metadata only.
Show child attributes
Show child attributes
Presigned URL to PUT the file to, with the declared content type as the Content-Type header.
When the upload URL stops working, in milliseconds since epoch.
Was this page helpful?