Skip to main content
POST
/
builds
/
upload-url
Get upload URL for build context
curl --request POST \
  --url https://api.pipecat.daily.co/v1/builds/upload-url \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "region": "us-west"
}
'
import requests

url = "https://api.pipecat.daily.co/v1/builds/upload-url"

payload = { "region": "us-west" }
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({region: 'us-west'})
};

fetch('https://api.pipecat.daily.co/v1/builds/upload-url', 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.pipecat.daily.co/v1/builds/upload-url",
  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([
    'region' => 'us-west'
  ]),
  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.pipecat.daily.co/v1/builds/upload-url"

	payload := strings.NewReader("{\n  \"region\": \"us-west\"\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.pipecat.daily.co/v1/builds/upload-url")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"region\": \"us-west\"\n}")
  .asString();
require 'uri'
require 'net/http'

url = URI("https://api.pipecat.daily.co/v1/builds/upload-url")

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  \"region\": \"us-west\"\n}"

response = http.request(request)
puts response.read_body
{
  "uploadId": "550e8400-e29b-41d4-a716-446655440000",
  "uploadUrl": "https://daily-co-pcc-build-context.s3.amazonaws.com",
  "uploadFields": {
    "key": "org-123/550e8400-e29b-41d4-a716-446655440000.tar.gz",
    "bucket": "daily-co-pcc-build-context-qa-us-west-2",
    "Content-Type": "application/gzip",
    "Policy": "eyJleHBpcmF0aW9uIjoi...",
    "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
    "X-Amz-Credential": "ASIA.../us-west-2/s3/aws4_request",
    "X-Amz-Date": "20260220T120000Z",
    "X-Amz-Security-Token": "FwoGZXIvYXdzE...",
    "X-Amz-Signature": "abc123..."
  },
  "expiresAt": "2026-02-20T12:15:00.000Z"
}
{
  "error": "<string>",
  "code": "<string>"
}

Uploading Your Context

After receiving the upload URL and fields, upload your context archive using a multipart form POST request:
# Get the upload URL
RESPONSE=$(curl -s -X POST "https://api.pipecat.daily.co/v1/builds/upload-url" \
  -H "Authorization: Bearer $PIPECAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"region": "us-west"}')

# Extract fields from response
UPLOAD_URL=$(echo $RESPONSE | jq -r '.uploadUrl')
UPLOAD_ID=$(echo $RESPONSE | jq -r '.uploadId')

# Upload the context archive (must be gzipped tar)
# Note: Field names are case-sensitive (X-Amz-*, not x-amz-*)
curl -X POST "$UPLOAD_URL" \
  -F "key=$(echo $RESPONSE | jq -r '.uploadFields.key')" \
  -F "bucket=$(echo $RESPONSE | jq -r '.uploadFields.bucket')" \
  -F "Content-Type=application/gzip" \
  -F "Policy=$(echo $RESPONSE | jq -r '.uploadFields.Policy')" \
  -F "X-Amz-Algorithm=$(echo $RESPONSE | jq -r '.uploadFields[\"X-Amz-Algorithm\"]')" \
  -F "X-Amz-Credential=$(echo $RESPONSE | jq -r '.uploadFields[\"X-Amz-Credential\"]')" \
  -F "X-Amz-Date=$(echo $RESPONSE | jq -r '.uploadFields[\"X-Amz-Date\"]')" \
  -F "X-Amz-Security-Token=$(echo $RESPONSE | jq -r '.uploadFields[\"X-Amz-Security-Token\"]')" \
  -F "X-Amz-Signature=$(echo $RESPONSE | jq -r '.uploadFields[\"X-Amz-Signature\"]')" \
  -F "file=@context.tar.gz"

echo "Upload ID: $UPLOAD_ID"
The context archive must be a gzipped tar file (.tar.gz). The upload URL validates both the content type and file size.
Field names are case-sensitive. When uploading to S3, you must use the exact field names returned in uploadFields (e.g., X-Amz-Algorithm, not x-amz-algorithm). Using incorrect casing will result in authentication errors.

Creating the Context Archive

Your context archive should contain your Dockerfile and all files needed for the build:
# Create a tar.gz of your project
tar -czvf context.tar.gz \
  --exclude='.git' \
  --exclude='node_modules' \
  --exclude='__pycache__' \
  --exclude='.venv' \
  .
The maximum context size is 500MB. Use a .dockerignore file to exclude unnecessary files and keep your context small for faster uploads and builds.

Authorizations

Authorization
string
header
required

Authentication using a Pipecat Cloud API token.

Body

application/json
region
string
required

Target region for the build. Must be a region where cloud builds are enabled.

Example:

"us-west"

Response

Upload URL generated successfully

uploadId
string<uuid>

Unique identifier for this upload. Use this when creating the build.

Example:

"550e8400-e29b-41d4-a716-446655440000"

uploadUrl
string<uri>

Pre-signed URL to upload your context archive. Use HTTP POST with form-data.

Example:

"https://daily-co-pcc-build-context.s3.amazonaws.com"

uploadFields
object

Form fields that must be included when uploading to the pre-signed URL.

Example:
{
  "key": "org-123/550e8400-e29b-41d4-a716-446655440000.tar.gz",
  "bucket": "daily-co-pcc-build-context-qa-us-west-2",
  "Content-Type": "application/gzip",
  "Policy": "eyJleHBpcmF0aW9uIjoi...",
  "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
  "X-Amz-Credential": "ASIA.../us-west-2/s3/aws4_request",
  "X-Amz-Date": "20260220T120000Z",
  "X-Amz-Security-Token": "FwoGZXIvYXdzE...",
  "X-Amz-Signature": "abc123..."
}
expiresAt
string<date-time>

When the upload URL expires. Upload must complete before this time.

Example:

"2026-02-20T12:15:00.000Z"