Bulk upsert photos
curl --request POST \
--url https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"externalSourceId": "supasite:photo-page:page_123",
"sourceApp": "supasite",
"projectId": "proj_ckproject123456789012345678",
"folderName": "Site walkthrough - 2026-06-11",
"photos": [
{
"externalSourceId": "<string>",
"fileName": "<string>",
"contentType": "<string>",
"sequenceId": 123,
"imageUrl": "<string>",
"width": 123,
"height": 123,
"capturedAt": "2023-11-07T05:31:56Z"
}
],
"folderId": "<string>"
}
'import requests
url = "https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert"
payload = {
"externalSourceId": "supasite:photo-page:page_123",
"sourceApp": "supasite",
"projectId": "proj_ckproject123456789012345678",
"folderName": "Site walkthrough - 2026-06-11",
"photos": [
{
"externalSourceId": "<string>",
"fileName": "<string>",
"contentType": "<string>",
"sequenceId": 123,
"imageUrl": "<string>",
"width": 123,
"height": 123,
"capturedAt": "2023-11-07T05:31:56Z"
}
],
"folderId": "<string>"
}
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({
externalSourceId: 'supasite:photo-page:page_123',
sourceApp: 'supasite',
projectId: 'proj_ckproject123456789012345678',
folderName: 'Site walkthrough - 2026-06-11',
photos: [
{
externalSourceId: '<string>',
fileName: '<string>',
contentType: '<string>',
sequenceId: 123,
imageUrl: '<string>',
width: 123,
height: 123,
capturedAt: '2023-11-07T05:31:56Z'
}
],
folderId: '<string>'
})
};
fetch('https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert', 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.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert",
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([
'externalSourceId' => 'supasite:photo-page:page_123',
'sourceApp' => 'supasite',
'projectId' => 'proj_ckproject123456789012345678',
'folderName' => 'Site walkthrough - 2026-06-11',
'photos' => [
[
'externalSourceId' => '<string>',
'fileName' => '<string>',
'contentType' => '<string>',
'sequenceId' => 123,
'imageUrl' => '<string>',
'width' => 123,
'height' => 123,
'capturedAt' => '2023-11-07T05:31:56Z'
]
],
'folderId' => '<string>'
]),
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.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert"
payload := strings.NewReader("{\n \"externalSourceId\": \"supasite:photo-page:page_123\",\n \"sourceApp\": \"supasite\",\n \"projectId\": \"proj_ckproject123456789012345678\",\n \"folderName\": \"Site walkthrough - 2026-06-11\",\n \"photos\": [\n {\n \"externalSourceId\": \"<string>\",\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sequenceId\": 123,\n \"imageUrl\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"capturedAt\": \"2023-11-07T05:31:56Z\"\n }\n ],\n \"folderId\": \"<string>\"\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.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"externalSourceId\": \"supasite:photo-page:page_123\",\n \"sourceApp\": \"supasite\",\n \"projectId\": \"proj_ckproject123456789012345678\",\n \"folderName\": \"Site walkthrough - 2026-06-11\",\n \"photos\": [\n {\n \"externalSourceId\": \"<string>\",\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sequenceId\": 123,\n \"imageUrl\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"capturedAt\": \"2023-11-07T05:31:56Z\"\n }\n ],\n \"folderId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert")
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 \"externalSourceId\": \"supasite:photo-page:page_123\",\n \"sourceApp\": \"supasite\",\n \"projectId\": \"proj_ckproject123456789012345678\",\n \"folderName\": \"Site walkthrough - 2026-06-11\",\n \"photos\": [\n {\n \"externalSourceId\": \"<string>\",\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sequenceId\": 123,\n \"imageUrl\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"capturedAt\": \"2023-11-07T05:31:56Z\"\n }\n ],\n \"folderId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"folderId": "<string>",
"createdAttachmentIds": [
"<string>"
],
"skippedExternalSourceIds": [
"<string>"
],
"failedPhotos": [
{}
]
}Photos
Bulk upsert photos
Bulk upsert external photos into a BuildPass photo folder idempotently.
POST
/
builders
/
{builderId}
/
photo-gallery
/
bulk-upsert
Bulk upsert photos
curl --request POST \
--url https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"externalSourceId": "supasite:photo-page:page_123",
"sourceApp": "supasite",
"projectId": "proj_ckproject123456789012345678",
"folderName": "Site walkthrough - 2026-06-11",
"photos": [
{
"externalSourceId": "<string>",
"fileName": "<string>",
"contentType": "<string>",
"sequenceId": 123,
"imageUrl": "<string>",
"width": 123,
"height": 123,
"capturedAt": "2023-11-07T05:31:56Z"
}
],
"folderId": "<string>"
}
'import requests
url = "https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert"
payload = {
"externalSourceId": "supasite:photo-page:page_123",
"sourceApp": "supasite",
"projectId": "proj_ckproject123456789012345678",
"folderName": "Site walkthrough - 2026-06-11",
"photos": [
{
"externalSourceId": "<string>",
"fileName": "<string>",
"contentType": "<string>",
"sequenceId": 123,
"imageUrl": "<string>",
"width": 123,
"height": 123,
"capturedAt": "2023-11-07T05:31:56Z"
}
],
"folderId": "<string>"
}
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({
externalSourceId: 'supasite:photo-page:page_123',
sourceApp: 'supasite',
projectId: 'proj_ckproject123456789012345678',
folderName: 'Site walkthrough - 2026-06-11',
photos: [
{
externalSourceId: '<string>',
fileName: '<string>',
contentType: '<string>',
sequenceId: 123,
imageUrl: '<string>',
width: 123,
height: 123,
capturedAt: '2023-11-07T05:31:56Z'
}
],
folderId: '<string>'
})
};
fetch('https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert', 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.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert",
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([
'externalSourceId' => 'supasite:photo-page:page_123',
'sourceApp' => 'supasite',
'projectId' => 'proj_ckproject123456789012345678',
'folderName' => 'Site walkthrough - 2026-06-11',
'photos' => [
[
'externalSourceId' => '<string>',
'fileName' => '<string>',
'contentType' => '<string>',
'sequenceId' => 123,
'imageUrl' => '<string>',
'width' => 123,
'height' => 123,
'capturedAt' => '2023-11-07T05:31:56Z'
]
],
'folderId' => '<string>'
]),
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.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert"
payload := strings.NewReader("{\n \"externalSourceId\": \"supasite:photo-page:page_123\",\n \"sourceApp\": \"supasite\",\n \"projectId\": \"proj_ckproject123456789012345678\",\n \"folderName\": \"Site walkthrough - 2026-06-11\",\n \"photos\": [\n {\n \"externalSourceId\": \"<string>\",\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sequenceId\": 123,\n \"imageUrl\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"capturedAt\": \"2023-11-07T05:31:56Z\"\n }\n ],\n \"folderId\": \"<string>\"\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.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"externalSourceId\": \"supasite:photo-page:page_123\",\n \"sourceApp\": \"supasite\",\n \"projectId\": \"proj_ckproject123456789012345678\",\n \"folderName\": \"Site walkthrough - 2026-06-11\",\n \"photos\": [\n {\n \"externalSourceId\": \"<string>\",\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sequenceId\": 123,\n \"imageUrl\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"capturedAt\": \"2023-11-07T05:31:56Z\"\n }\n ],\n \"folderId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.buildpass.global/builders/{builderId}/photo-gallery/bulk-upsert")
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 \"externalSourceId\": \"supasite:photo-page:page_123\",\n \"sourceApp\": \"supasite\",\n \"projectId\": \"proj_ckproject123456789012345678\",\n \"folderName\": \"Site walkthrough - 2026-06-11\",\n \"photos\": [\n {\n \"externalSourceId\": \"<string>\",\n \"fileName\": \"<string>\",\n \"contentType\": \"<string>\",\n \"sequenceId\": 123,\n \"imageUrl\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"capturedAt\": \"2023-11-07T05:31:56Z\"\n }\n ],\n \"folderId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"folderId": "<string>",
"createdAttachmentIds": [
"<string>"
],
"skippedExternalSourceIds": [
"<string>"
],
"failedPhotos": [
{}
]
}Requires
write:photos. Bulk upserts photos into a project gallery folder. projectId is required and must belong to the builder.
Upload attachment bytes first with POST /builders/{builderId}/attachments/bulk-upload, then pass each returned { key, regionId, type } as photos[].imageAttachment. This endpoint accepts JSON only.
Approved HTTPS imageUrl imports are supported only for hosts reviewed and enabled by BuildPass. If you want to import photos from your own image URL hosts, please contact BuildPass. The endpoint is idempotent by metadata.externalSourceId, so repeated sends skip existing photos.
When syncing the same external source again, pass the previously returned folderId so new photos are added to the same BuildPass folder. Omit folderId for a different external source, even when the folder name and date are the same.
Use DELETE /builders/{builderId}/photo-gallery/bulk-upsert with the same externalSourceId, projectId, and optional folderId to archive photos created for that external source and remove the now-empty folder when possible.Authorizations
The access token received from the authorization server in the OAuth 2.0 flow.
Path Parameters
Body
application/json
Example:
"supasite:photo-page:page_123"
Example:
"supasite"
Example:
"proj_ckproject123456789012345678"
Example:
"Site walkthrough - 2026-06-11"
Show child attributes
Show child attributes
Existing BuildPass photo folder id for this external source. Omit on first upsert to create a new folder.
⌘I