curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"path": "src/pages/About.jsx",
"content": "export default function About() {\n return <h1>About</h1>;\n}\n"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file"
payload = {
"path": "src/pages/About.jsx",
"content": "export default function About() {
return <h1>About</h1>;
}
"
}
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({
path: 'src/pages/About.jsx',
content: 'export default function About() {\n return <h1>About</h1>;\n}\n'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file', 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://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file",
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([
'path' => 'src/pages/About.jsx',
'content' => 'export default function About() {
return <h1>About</h1>;
}
'
]),
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://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file"
payload := strings.NewReader("{\n \"path\": \"src/pages/About.jsx\",\n \"content\": \"export default function About() {\\n return <h1>About</h1>;\\n}\\n\"\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://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"path\": \"src/pages/About.jsx\",\n \"content\": \"export default function About() {\\n return <h1>About</h1>;\\n}\\n\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file")
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 \"path\": \"src/pages/About.jsx\",\n \"content\": \"export default function About() {\\n return <h1>About</h1>;\\n}\\n\"\n}"
response = http.request(request)
puts response.read_body{
"path": "src/pages/About.jsx",
"bytes_written": 214,
"created": true,
"overwritten": false,
"warnings": []
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Write an app file
Writes a whole file into the app’s sandbox, creating it or replacing it.
Every sandbox-bridge endpoint runs against the app’s live sandbox, the same filesystem the AI edits, so a change here is visible in the app editor immediately.
The default refuses to clobber: writing a path that already exists answers 409 unless you send overwrite: true. Read created and overwritten on the response to see which happened.
content cannot be empty. The app editor’s write path reads empty content as a delete, so this endpoint rejects it rather than deleting a file behind a write call. To create an empty file, run touch through Run a sandbox command; to change part of a file, use Edit an app file, which is cheaper than sending the whole thing.
Base44 refuses paths outside the app and a set of protected paths, both with a 400. A file over the 6 MB cap answers 413.
An entity file is named after its entity: base44/entities/TeamMember.jsonc defines TeamMember. A file that would define a new entity whose name isn’t letters, numbers and underscores answers 400 with INVALID_ENTITY_NAME, since it would never become a table; the message names the file to write instead.
This endpoint is limited to 60 requests per minute per app, shared with the other sandbox-bridge endpoints that change files.
CREDITS_EXHAUSTED. Otherwise the bridge needs a Builder plan or higher on the app’s workspace, and answers 402 below that (PREMIUM_REQUIRED). Workspace API keys are not authorized and are rejected with a 403, and it is unavailable for agent apps. A personal API key works as-is. An OAuth access token needs the sandbox:write scope.extra_data.code alongside the human-readable message. Branch on the code rather than on the message text or the status.curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"path": "src/pages/About.jsx",
"content": "export default function About() {\n return <h1>About</h1>;\n}\n"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file"
payload = {
"path": "src/pages/About.jsx",
"content": "export default function About() {
return <h1>About</h1>;
}
"
}
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({
path: 'src/pages/About.jsx',
content: 'export default function About() {\n return <h1>About</h1>;\n}\n'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file', 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://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file",
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([
'path' => 'src/pages/About.jsx',
'content' => 'export default function About() {
return <h1>About</h1>;
}
'
]),
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://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file"
payload := strings.NewReader("{\n \"path\": \"src/pages/About.jsx\",\n \"content\": \"export default function About() {\\n return <h1>About</h1>;\\n}\\n\"\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://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"path\": \"src/pages/About.jsx\",\n \"content\": \"export default function About() {\\n return <h1>About</h1>;\\n}\\n\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/sandbox-bridge/write_file")
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 \"path\": \"src/pages/About.jsx\",\n \"content\": \"export default function About() {\\n return <h1>About</h1>;\\n}\\n\"\n}"
response = http.request(request)
puts response.read_body{
"path": "src/pages/About.jsx",
"bytes_written": 214,
"created": true,
"overwritten": false,
"warnings": []
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Authorizations
Personal access token, sent as Authorization: Bearer <token>. Each operation lists the permission it needs: read_only or full_access. A full-access token can call every operation. A read-only token can call only the operations that list read_only; anything else returns 403 read_only_credential_route_not_allowed.
Path Parameters
ID of the app whose sandbox to operate on.
Body
File path relative to the app root.
Full file content to write. To create an empty file (e.g. .gitkeep) use run_command (touch) — the platform write path treats empty content as a delete.
1 - 6000000Optional Base44 branch ID. Omit to operate on main.
Must be true to overwrite an existing file (destructive). Default false never clobbers.
Response
The file was written.
What the write did.
The normalized path that was written.
"src/pages/About.jsx"
Size of the content written, in bytes.
214
true when the file did not exist before this call.
true
true when the file existed and was replaced. Never true unless you sent overwrite.
false
Advisories about the write that did not stop it, such as writing somewhere the app editor treats specially. Empty when there are none.
[]
Was this page helpful?