curl --request POST \
--url https://api.parallellabs.app/api/v0/websites/{websiteId}/assets \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"companyId": "<string>",
"branch": "<string>",
"path": "<string>",
"sourceUrl": "<string>",
"contentBase64": "<string>"
}
'import requests
url = "https://api.parallellabs.app/api/v0/websites/{websiteId}/assets"
payload = {
"companyId": "<string>",
"branch": "<string>",
"path": "<string>",
"sourceUrl": "<string>",
"contentBase64": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
companyId: '<string>',
branch: '<string>',
path: '<string>',
sourceUrl: '<string>',
contentBase64: '<string>'
})
};
fetch('https://api.parallellabs.app/api/v0/websites/{websiteId}/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.parallellabs.app/api/v0/websites/{websiteId}/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([
'companyId' => '<string>',
'branch' => '<string>',
'path' => '<string>',
'sourceUrl' => '<string>',
'contentBase64' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.parallellabs.app/api/v0/websites/{websiteId}/assets"
payload := strings.NewReader("{\n \"companyId\": \"<string>\",\n \"branch\": \"<string>\",\n \"path\": \"<string>\",\n \"sourceUrl\": \"<string>\",\n \"contentBase64\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.parallellabs.app/api/v0/websites/{websiteId}/assets")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"companyId\": \"<string>\",\n \"branch\": \"<string>\",\n \"path\": \"<string>\",\n \"sourceUrl\": \"<string>\",\n \"contentBase64\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.parallellabs.app/api/v0/websites/{websiteId}/assets")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"companyId\": \"<string>\",\n \"branch\": \"<string>\",\n \"path\": \"<string>\",\n \"sourceUrl\": \"<string>\",\n \"contentBase64\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"path": "<string>",
"storage": "<string>",
"bytes": 123,
"contentType": "<string>",
"sha256": "<string>",
"width": 123,
"height": 123,
"optimized": true
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Upload an image, document or media file onto a review branch
Adds a binary file to the site: images (png, jpg, webp, gif, avif, ico), documents (pdf, doc/docx, xls/xlsx, ppt/pptx, zip), audio/video (mp4, webm, mov, mp3, wav, ogg, m4a) and fonts. Pass sourceUrl to have the server fetch a public URL (best for images), or contentBase64 for bytes you already hold; multipart file uploads are also accepted. Images are optimized; documents download when linked. Up to 50MB. The branch is REQUIRED and must not be “main”. Reference the file by the returned path. HTML, CSS, JS and SVG are source files — write those with the write-file endpoint.
curl --request POST \
--url https://api.parallellabs.app/api/v0/websites/{websiteId}/assets \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"companyId": "<string>",
"branch": "<string>",
"path": "<string>",
"sourceUrl": "<string>",
"contentBase64": "<string>"
}
'import requests
url = "https://api.parallellabs.app/api/v0/websites/{websiteId}/assets"
payload = {
"companyId": "<string>",
"branch": "<string>",
"path": "<string>",
"sourceUrl": "<string>",
"contentBase64": "<string>"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
companyId: '<string>',
branch: '<string>',
path: '<string>',
sourceUrl: '<string>',
contentBase64: '<string>'
})
};
fetch('https://api.parallellabs.app/api/v0/websites/{websiteId}/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.parallellabs.app/api/v0/websites/{websiteId}/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([
'companyId' => '<string>',
'branch' => '<string>',
'path' => '<string>',
'sourceUrl' => '<string>',
'contentBase64' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.parallellabs.app/api/v0/websites/{websiteId}/assets"
payload := strings.NewReader("{\n \"companyId\": \"<string>\",\n \"branch\": \"<string>\",\n \"path\": \"<string>\",\n \"sourceUrl\": \"<string>\",\n \"contentBase64\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.parallellabs.app/api/v0/websites/{websiteId}/assets")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"companyId\": \"<string>\",\n \"branch\": \"<string>\",\n \"path\": \"<string>\",\n \"sourceUrl\": \"<string>\",\n \"contentBase64\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.parallellabs.app/api/v0/websites/{websiteId}/assets")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"companyId\": \"<string>\",\n \"branch\": \"<string>\",\n \"path\": \"<string>\",\n \"sourceUrl\": \"<string>\",\n \"contentBase64\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"path": "<string>",
"storage": "<string>",
"bytes": 123,
"contentType": "<string>",
"sha256": "<string>",
"width": 123,
"height": 123,
"optimized": true
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Authorizations
Company API key - scoped to a specific company. Generate from the Integrations page in your dashboard.
Path Parameters
Body
Upload a website asset
Company ID that owns the website
Review branch to read from or write to. Omit (or "main") to read the live site. To propose fixes, write files to a NEW branch name (e.g. "seo-optimizer-2026-09-03") — the branch is created on first write and is reviewed and approved by the owner. Writing to "main" is refused. Required here and must not be "main".
Where the file lives in the site, e.g. /assets/hero.jpg or /assets/files/sales-sheet.pdf. A bare file name lands in /assets/ (documents in /assets/files/). Defaults to the file name from sourceUrl. Raster images are optimized to WebP, so /assets/hero.jpg may be saved as /assets/hero.webp — use the returned path in links and [Image blocked: No description].
Public http(s) URL to fetch the file from (preferred for images: no need to inline bytes). Private/internal addresses are refused. Send exactly one of sourceUrl or contentBase64.
The file bytes, base64-encoded (a data: URL prefix is accepted). Only for files you already hold locally; prefer sourceUrl.
Response
The stored file
Saved path — reference the file by this path
'inline' (small file kept in the project) or 'object' (object storage)
Stored size in bytes
Content hash (object-stored files)
Image width, for the [Image blocked: No description] attribute
Image height, for the [Image blocked: No description] attribute
True when an image was downscaled/re-encoded

