Save an app file
Changed on<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>
Saves a file into the app, the same way saving in the app editor does.
Base44 writes the file, applies it to the app, and takes care of what the path implies: a backend function is redeployed, an entity or agent schema is parsed and validated, and a change that affects row-level security records a revertible notice on the app's chat.
Sending an empty content deletes the file. That is the app editor's own convention for this path, so treat an empty string as a delete rather than as a way to blank a file.
Paths are relative to the app root. Entity and agent schemas, workflows and email templates each have one canonical location, and a legacy shorthand is refused with a 400 naming the canonical form. A path that escapes the app is refused the same way.
Writing a backend function deploys it, so the request takes as long as the deploy and answers 422 carrying the compiler's diagnostics when the code doesn't build. Backend functions also need a Builder plan or higher on the app's workspace. Use Save several app files to write a batch of frontend files in one call.
Set branch_id to save the file on a branch instead of main. The change goes to the branch's own code, and a backend function is deployed for the branch only. On a branch, files that define the app's resources, such as entity and agent schemas, are refused with a 409, except on an app imported from GitHub. To add entities or fields on the branch, use Send chat message with the same branch_id. Other resource changes must be made on main.
The response is the app document as it stands after the change, the same shape Get app returns. On a branch, it's main's app document, so it doesn't show the change.
<Note>This endpoint accepts a personal API key. Workspace API keys are not authorized for it and are rejected with a 403.</Note>
<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time. Send only the fields documented here. Other request fields are not supported and their behavior can change.</Warning>
Request
- Base URL: https://app.base44.com
- URL: https://app.base44.com/api/apps/{app_id}/coding/write
- Auth: HTTP bearer
Path parameters
ID of the app whose code to change.
Query parameters
ID of the branch to work on, as returned by List branches. Omit it to work on the app's main branch.
Request body
Example request
{
"content": "export default function About() {\n return <h1>About</h1>;\n}\n",
"file_path": "src/pages/About.jsx"
}Response
The app after the write. With branch_id, main's app, which a branch write doesn't change.
Example response
{
"id": "6820f3a4e7b91d003c45a1f2",
"created_date": "2026-08-01T09:15:00",
"updated_date": "2026-08-02T14:30:00",
"created_by": "developer@example.com",
"organization_id": "67e0b12c4d8a3f005b21c9e4",
"app_type": "user_app",
"name": "My CRM",
"user_description": "A CRM to track leads and deals",
"logo_url": "https://storage.base44.com/6820f3a4e7b91d003c45a1f2/3f2504e0_logo.png",
"social_image_url": "https://storage.base44.com/6820f3a4e7b91d003c45a1f2/7c9e6679_social.png",
"public_settings": "public_with_login",
"auth_config": {
"enable_username_password": true,
"enable_google_login": true
},
"hide_entity_created_by": true,
"slug": "my-crm-3c45a1f2",
"last_deployed_at": "2026-08-02T14:30:00",
"screenshot_url": "https://storage.base44.com/screenshots/6820f3a4e7b91d003c45a1f2.png",
"preview_screenshot_url": "https://storage.base44.com/previews/6820f3a4e7b91d003c45a1f2.png",
"owner_id": "6706af53b9c1e2004a37d85f",
"status": {
"state": "ready",
"details": "Publishing app",
"request_id": "a1b2c3d4e5f67890abcdef12",
"last_updated_date": "2026-08-02T14:30:00Z",
"error_source": "build",
"paywall_context": {
"billing_organization_id": "67e0b12c4d8a3f005b21c9e4",
"user_id": "6706af53b9c1e2004a37d85f",
"evaluated_at": "2026-08-02T14:30:00Z"
}
}
}Changes
- breaking
queryrequest parameterbranch_idlist-of-types was narrowed by removing typesnull - info
for the
queryrequest parameterbranch_id, the type was generalized from no type tostring
- breaking
- info
added the new optional
queryrequest parameterbranch_id
- info