---
title: "Edit app content on a draft"
method: PATCH
path: "/api/v2/documents/{identifier}/draft/{draftIdentifier}/app"
tags: ["Documents"]
---

# Edit app content on a draft

`PATCH /api/v2/documents/{identifier}/draft/{draftIdentifier}/app`

## Request

- Base URL: `https://omniapp.co` (the document declares no server; this is the origin it was published from)
- URL: `https://omniapp.co/api/v2/documents/{identifier}/draft/{draftIdentifier}/app`
- Auth: HTTP bearer

**Alpha.** The app sub-resource may change shape without a deprecation cycle while apps mature. The document routes are stable.

Apply targeted edits to the app on an existing draft without resending the HTML body: any subset of `htmlEdits` (exact-substring search/replace operations, applied in order) and `settings` (full replacement). All-or-nothing — an edit that matches zero times, matches more than once without `replaceAll`, or would push the HTML past the 2 MiB cap rejects the whole request with 400 naming the edit, and nothing is applied.

Content addressing buys sequential staleness detection, not concurrency safety: if the text an edit targets moved between your read and your write, the edit fails to match and nothing is applied. There is no version precondition, compare-and-swap, or lock behind it, so two writers patching the same app at the same time can still overwrite each other, orthogonal edits included — serialize writes to one app when that matters. PATCH never creates the app: a draft with no app is a 409 (create it with `PUT`).

A document carries at most one of a dashboard or an app — never both; workbook-only is valid. The app HTML and settings live only at the app sub-resource routes; the document read carries an `app` slice pointing here, and the whole-document PATCH accepts that slice back only as it was read.

## Path parameters

- `draftIdentifier` string, required: Draft workbook identifier (see `PATCH /api/v2/documents/{identifier}/draft`).
- `identifier` string, required: Published document identifier.

## Request body

- union
  - object
    - `htmlEdits` DocumentsV2HtmlEdit[], required: Targeted edits applied in order against the current HTML, all-or-nothing: a failed edit (not found, ambiguous, or result over the 2 MiB cap) rejects the request with 400 naming the edit, and nothing is applied. At most 20 edits per request — each edit scans the whole document.
      - `replace` string, required: Text that replaces the matched `search` text. May be empty (deletion).
      - `replaceAll` boolean: Replace every occurrence of `search` instead of requiring a unique match.
      - `search` string, required: Exact substring of the app's current HTML, copied verbatim with enough surrounding context to be unique (unless `replaceAll`).
    - `settings` object: When present, replaces the app settings; omitted fields take their locked-down defaults. When absent, the current settings are kept.
      - `allowClipboard` boolean
      - `allowDefaultMapProviders` boolean
      - `allowDownloads` boolean
      - `allowExternalNavigation` boolean
      - `allowFullscreen` boolean
      - `allowInternalNavigation` boolean
      - `allowModals` boolean
      - `externalNavOpensInNewTab` boolean
      - `frameDomains` string[]
      - `frameDomainsEnabled` boolean
      - `navAllowedDomains` string[]
      - `navAllowedDomainsEnabled` boolean
      - `safeDomains` string[]
      - `safeDomainsEnabled` boolean
  - object
    - `htmlEdits` DocumentsV2HtmlEdit[]: Targeted edits applied in order against the current HTML, all-or-nothing: a failed edit (not found, ambiguous, or result over the 2 MiB cap) rejects the request with 400 naming the edit, and nothing is applied. At most 20 edits per request — each edit scans the whole document.
      - `replace` string, required: Text that replaces the matched `search` text. May be empty (deletion).
      - `replaceAll` boolean: Replace every occurrence of `search` instead of requiring a unique match.
      - `search` string, required: Exact substring of the app's current HTML, copied verbatim with enough surrounding context to be unique (unless `replaceAll`).
    - `settings` object, required: When present, replaces the app settings; omitted fields take their locked-down defaults. When absent, the current settings are kept.
      - `allowClipboard` boolean
      - `allowDefaultMapProviders` boolean
      - `allowDownloads` boolean
      - `allowExternalNavigation` boolean
      - `allowFullscreen` boolean
      - `allowInternalNavigation` boolean
      - `allowModals` boolean
      - `externalNavOpensInNewTab` boolean
      - `frameDomains` string[]
      - `frameDomainsEnabled` boolean
      - `navAllowedDomains` string[]
      - `navAllowedDomainsEnabled` boolean
      - `safeDomains` string[]
      - `safeDomainsEnabled` boolean

## Response `200`

Edits applied on the draft. `warnings` names any resource hosts the app’s iframe CSP will block until an org admin allows them.

- DocumentsV2AppWriteResponse
  - `description` string, nullable, required: Document description.
  - `draftIdentifier` string, required: Identifier of the draft the patch was applied to.
  - `identifier` string, required: Published document identifier the draft targets.
  - `name` string, required: Document name.
  - `app` DocumentsV2AppState, required
    - `settings` DocumentsV2AppSettings, required: The app's sandbox settings: capability toggles plus the safe-domain and navigation allowlists. A write replaces the whole object; omitted fields take their locked-down defaults. Host lists are normalized (deduped, invalid or Omni-owned hosts dropped) before they are stored.
      - `allowClipboard` boolean
      - `allowDefaultMapProviders` boolean
      - `allowDownloads` boolean
      - `allowExternalNavigation` boolean
      - `allowFullscreen` boolean
      - `allowInternalNavigation` boolean
      - `allowModals` boolean
      - `externalNavOpensInNewTab` boolean
      - `frameDomains` string[]
      - `frameDomainsEnabled` boolean
      - `navAllowedDomains` string[]
      - `navAllowedDomainsEnabled` boolean
      - `safeDomains` string[]
      - `safeDomainsEnabled` boolean
  - `warnings` string[]: Non-blocking warnings — present only when there are any. Currently: external resource hosts the app's iframe CSP will block until an org admin allows them, a map-provider, print-and-dialogs, fullscreen, clipboard, or downloads setting the organization's app policy turns off for every app, and navigation domains outside the organization's navigation policy (links to them are refused). The write itself succeeded. Advisory prose, not a contract: the wording is deliberately volatile, there are no per-warning codes, and consumers must not branch on the content. The render-time CSP is the enforcement.

## Other responses

- `400`: Invalid request body (unknown field, neither `htmlEdits` nor `settings`, or more than 20 `htmlEdits`), or a failed `htmlEdits` entry (not found, ambiguous, or result over the 2 MiB cap). Nothing is applied.
- `401`: Authentication required.
- `403`: Insufficient permissions: no write access to the document, apps not enabled for the organization, or (user-scoped keys) the role does not allow creating apps.
- `404`: Document or draft not found.
- `405`: Method not allowed.
- `409`: The draft has no app to patch.
- `422`: The draft is a dashboard, not an app.

## Changes

- **2026-10-08** `95080ca011e2`: 2 info
  - added the new optional request property `settings/allOf[#/components/schemas/DocumentsV2AppSettings]/allowFullscreen`
  - added the optional property `allOf[subschema #2]/app/settings/allowFullscreen` to the response with the `200` status
- **2026-09-24** `1433c17c0a39`: 1 info
  - api operation id `documentsV2PatchApp` removed and replaced with `documentsPatchApp`
- **2026-09-22** `d3c0855161b1`: 2 info
  - added the new optional request property `settings/allOf[#/components/schemas/DocumentsV2AppSettings]/allowModals`
  - added the optional property `allOf[subschema #2]/app/settings/allowModals` to the response with the `200` status
- **2026-09-10** `93b36f3e1394`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/omniapp/apis/omni-api/changes/api/v2/documents/:identifier/draft/:draftIdentifier/app/patch.md)

---

[API](https://skmtc.dev/omniapp/apis/omni-api.md) - [All operations](https://skmtc.dev/omniapp/apis/omni-api/llms.txt) - [OpenAPI document](https://skmtc.dev/omniapp/apis/omni-api/revisions/76b40ebc2292?raw)
