Publish content from a draft App (v1)
Publishes content from the draft App identified by {appId} into either a brand-new published App (publish-as-new) or an existing published App (publish-into-existing). The mode is selected implicitly by the request body: targetAppId present selects publish-into-existing, absent selects publish-as-new.
Both branches read the draft’s last-run AppVersion (the version referenced by the most recent AppInstance on the draft) as the publish base, optionally apply content (reproducibility) overrides via versionOverrides, then write the result onto the destination App. For publish-as-new the destination is a new App with isDraft=false and vanityUrlNamespace=Apps; for publish-into-existing the destination is the target App identified by targetAppId. The draft App and its version history are NOT touched — the draft persists post-publish so the same workspace can publish again.
Validation:
- The draft must exist and the caller must have publish permission on it. - The draft (and target App, when publishing into an existing app) must have configurationType=Standard.
- When publishing into an existing app, the draft and target must agree on configurationType and entryScript.
- If the draft has never been previewed and
versionOverridesdoes not supply the required content fields (commitId, entryScript, environmentId, hardwareTierId), returns 422. Clients can useversionOverridesto publish without first previewing. - If neither
versionOverrides.entryScriptnor the publish base supplies an entryScript, the published version usesapp.sh.
Bundle continuity:
- Publish-as-new: the new version’s
bundleIdis whatever the caller supplies, otherwise unset. - Publish-into-existing: the new version inherits the target App’s last
bundleIdunless the caller supplies a different one.
Authorizations
Path Parameters
Body
Request body for POST /apps/{appId}/publish. The mode is implicit: the presence of targetAppId selects publish-into-existing, absence selects publish-as-new. vanityUrl is consumed only on publish-as-new (publish-into- existing inherits the target App's vanity); versionOverrides is consumed by both branches and lets the caller override content (reproducibility) fields on a copy of the draft's last-run version before it is written to the destination App. start=true optionally launches the resulting App's newest version after the publish persists, with startOverrides carrying deployment (run-time) overrides.
Publish-as-new only. When true and the draft carried accessStatuses, the granted users on the newly-published App receive an email notification (the published App is a fresh insert, so every Allowed entry is treated as a new grant). Ignored on publish-into-existing, which doesn't touch the target App's accessStatuses. Defaults to false when omitted.
When true, the resulting App's newest version is started after the publish persists. Start initiation failures are surfaced as warnings on the response rather than rolling back the publish.
Deployment-only run-time overrides for the optional start initiation (deployment.hardwareTierId, deployment.autoscalingSpecification, deployment.vanityUrl, deployment.renderIFrame). Consumed only when start=true.
Optional. Present -> publish-into-existing (new version added to the target App; vanityUrl is ignored). Absent -> publish-as-new (a brand- new published App is inserted).
Publish-as-new only. The new published App's vanity in the Apps namespace. If omitted, a random fallback is generated server-side. Ignored when targetAppId is present.
Content (reproducibility) version-field overrides applied to a copy of the draft's last-run AppVersion during publish. Consumed only by PublishAppRequestV1. Override semantics is field-by-field orElse(publishBase.value); an unset override falls back to whatever the draft's last-run version carries.
bundleId interplay with publish flows:
- Publish-as-new: new version's
bundleId = versionOverrides.bundleId.orElse(null). - Publish-into-existing: new version'sbundleId = versionOverrides.bundleId.orElse(targetApp.latestVersion.bundleId), matching the legacy update path's carry-forward semantic so governance continuity is preserved across republishes. Explicit override wins.
A supplied bundleId is validated via the Guardrails API; missing bundle -> 404.
Response
Publish succeeded. Returns the resulting App (newly inserted for publish-as-new, the target App for publish-into-existing) plus the newly inserted version. When start=true the started instance is also returned (or a warning if start initiation failed; the version is durably persisted in either case).
Response body for POST /apps/{appId}/publish. app is the resulting destination App (newly inserted for publish-as-new, the target App for publish-into- existing). version is the newly inserted AppVersion. instance is populated only when request.start=true AND start initiation succeeded; if the version was persisted but start initiation failed, the response carries version plus a non-empty warnings array. The draft App itself is unchanged and is not returned in this response -- clients that still need it can read it via GET /apps/{draftId}.