---
title: "Save several app files"
method: POST
path: "/api/apps/{app_id}/coding/write-batch"
---

# Save several app files

`POST /api/apps/{app_id}/coding/write-batch`

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Saves several files into the app in one call, the way the builder's visual editor does.

Send up to 50 files. Every path is validated before anything is written, so one bad path fails the whole batch with a 400 and leaves the app untouched. An empty `files` list is a 400 rather than a no-op.

This route never deploys, so it refuses backend code: a path under `functions/` answers 422 rather than writing source that the live function wouldn't match. Use [Save an app file](/api-reference/save-an-app-file) for those, one at a time, and this one for the frontend files a visual edit touches.

Internally this route can follow a feature branch, but the parameter that selects one isn't part of the public API, so writes through this endpoint land on the app's main line.

The response is the app document as it stands after the change, the same shape [Get app](/api-reference/get-app) returns.

<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>

## Path parameters

- `app_id` string, required — ID of the app whose code to change.

## Request body

- object
  - `files` object[], required — The files to save, 1 to 50 of them. All are validated before any is written.
    - `file_path` string, required — Path of the file, relative to the app root.
    - `content` string, required — Full contents to save. An empty string deletes the file, which is this endpoint's convention rather than a way to blank one.

## Response `200`

The app after the writes.

- AppSummary — An app in a workspace, limited to the properties the caller requested.
  - `id` string, nullable — ID of the app.
  - `name` string, nullable — Display name of the app.
  - `slug` string, nullable — URL slug for the app, auto generated from the name and app ID or set to a custom value, or `null` if the app has no slug yet. The published URL is built from it.
  - `user_description` string, nullable — Description of the app, or `null` if none was set. On a newly created app this holds the original prompt text.
  - `created_by` string, nullable — Email of the user who created the app.
  - `created_date` string, date-time, nullable — Time the app was created, as a UTC timestamp in ISO 8601 format.
  - `updated_date` string, date-time, nullable — Time the app document was last written, as a UTC timestamp in ISO 8601 format.
  - `status` AppStatusResponse — The app's current build status.
    - `state` 'ready' | 'processing' | 'error', required — Where the app is in its build lifecycle. Ready means idle with no build in progress, processing means the app is being generated or modified, and error means the last build failed. This tracks building, not publishing.
    - `details` string, nullable — Human readable note about the current state, such as what is being processed or why it failed, or `null` when there is nothing to report.
    - `request_id` string, nullable — ID of the request that last changed the status, or `null` if the status has never changed. Useful when reporting an issue.
    - `last_updated_date` string, date-time, nullable — Time the status was last updated, as a UTC timestamp in ISO 8601 format.
    - `error_source` string, nullable — Where the failure originated when `state` is `error`, or `null` otherwise. A value of `paywall` means the work was blocked because the app's workspace has no credits left.
    - `paywall_context` PaywallStatusContextResponse
      - `billing_organization_id` string, required — ID of the billing organization the paywall was evaluated against.
      - `user_id` string, required — ID of the user the paywall was evaluated for.
      - `evaluated_at` string, date-time, required — Time the paywall condition was evaluated, as a UTC timestamp in ISO 8601 format.
  - `last_deployed_at` string, date-time, nullable — Time the app was last published, as a UTC timestamp in ISO 8601 format, or `null` if it has never been published.
  - `screenshot_url` string, nullable — URL of a screenshot of the published app. Captured shortly after each publish, so it can briefly lag or be `null` right after publishing.
  - `preview_screenshot_url` string, nullable — URL of a preview screenshot taken before publishing, distinct from `screenshot_url`, or `null` if none has been captured.

## Other responses

- `400` — `files` is empty, holds more than 50 entries, has a path that escapes the app or uses a legacy schema, workflow or email location, or a configuration file with invalid JSON.
- `401` — Missing or invalid credentials.
- `403` — You don't have access to this app, or you used a workspace API key.
- `404` — App not found.
- `409` — The app is on a branch this change can't be made on: a branch that has been merged or closed, a protected main, or a path the branch refuses.
- `422` — One of the paths is backend code, which this route can't write because it never redeploys.

## Changes

- **2026-09-03** `173e4e9c63c2` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/adexad/apis/base44-app-management-api/changes/api/apps/:app_id/coding/write-batch/post.md)

---

[API](https://skmtc.dev/adexad/apis/base44-app-management-api.md) · [All operations](https://skmtc.dev/adexad/apis/base44-app-management-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/adexad/base44-app-management-api/revisions/173e4e9c63c2/schema)
