---
title: "Promote (pointer-swap) a prior release"
method: POST
path: "/apply/v1/releases/{release_id}/promote"
tags: ["Apply"]
---

# Promote (pointer-swap) a prior release

`POST /apply/v1/releases/{release_id}/promote`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/apply/v1/releases/{release_id}/promote`
- Auth: API key in header `SIGN-IN-WITH-X`

Pointer-swap recovery: atomically point `live_release_id` and every project-owned subdomain at a prior release's materialized deployment without re-running the apply pipeline. Refuses non-promotable/no-op targets and targets whose release-state snapshot or deployment row no longer survives (`PROMOTE_DEPLOYMENT_UNSERVABLE`). Computes a diff vs the current release and emits structured warnings (`MIGRATIONS_NOT_REVERSIBLE`, `FUNCTION_VERSION_MISMATCH`) the caller can opt into via `allow_warning_codes`. Durable edge-pointer updates run immediately after commit and remain retryable; the response's `edge.verify_url` composes with `run402 deploy verify --wait`. Flushes `ssr_cache` and logs to operation history as `operation_kind: "promote"`. On success with a public site, `urls.console` joins `urls.site` and `next_actions` carries the same `poll` and (once, until answered) `hand_to_member` promotion-offer entries a commit response carries — see `DeployCommitResponse`.

## Path parameters

- `release_id` string, required

## Headers

- `SIGN-IN-WITH-X` string, required

## Request body

- object
  - `allow_warning_codes` string[]: Warning codes the caller opts into. Promote fails with 409 if the warning set is not a subset of allowed codes.
  - `project_id` string, required: Project id of the release's owning project (cross-check).

## Response `200`

Promote committed. Origin state is active; public serving may still be converging as reported by `edge`.

- object
  - `status` 'ok'
  - `release_id` string
  - `operation_id` string
  - `previous_release_id` string, nullable
  - `warnings` object[]
  - `urls` object: `site` is the project's public URL, present when the project owns at least one subdomain. `console` is the project's page in the console, present exactly when `site` is present.
  - `next_actions` NextAction[]: When `urls.site` is present and the project has no recorded promotion consent, includes one `hand_to_member` entry — see `DeployCommitResponse.next_actions`.
    - `type` string, required: Advisory machine-readable action type such as retry, authenticate, submit_payment, fund_wallet, renew_tier, check_usage, resume_deploy, edit_request, edit_migration, contact_support, upgrade_client, poll (poll the project events feed — appended when the envelope carries a correlated_platform_incident), or hand_to_member (on a commit/promote response that just activated a public site: show the human urls.site and urls.console, relay Run402's offer to promote the build on @run402com, and ask yes or no — see `credited_as` below).
    - `method` string
    - `path` string
    - `auth` string
    - `why` string
    - `credited_as` string, nullable: `hand_to_member` only. The caller's own live presence name in the project's default room, else that room's most recently active live presence, else `null` — in which case `why` tells the agent that joining the room with a name (`run402 rooms join --name ...`) is how the promotion credits it.
    - `body` object: `hand_to_member` only. A ready-to-send `POST /feedback/v1` template: `{ project_id, message: "promote: yes", handle? }`.
  - `edge` EdgeBlock
    - `checked_at` string, date-time: When the activation-time edge summary was computed.
    - `state` 'converging' | 'coherent' | 'unknown' | 'not_applicable', required
    - `expected_max_lag_seconds` integer, nullable
    - `pointer_updates` EdgePointerUpdates, required: Map keyed by edge pointer target. Missing keys mean no outbox row exists for that target.
    - `verify_url` string, nullable
  - `functions` object: Functions re-deployed to the target release's code after the pointer swap (run402-private#816).
    - `redeployed` string[], required: Functions now running the target release's code.
    - `failed` object[], required: Functions that keep the code they had before (also a FUNCTION_REDEPLOY_FAILED warning).
      - `name` string, required
      - `code` 'FUNCTION_SOURCE_NOT_FOUND' | 'FUNCTION_REDEPLOY_FAILED', required
      - `message` string, required

## Other responses

- `400`: PROMOTE_PROJECT_MISMATCH — request body `project` does not match the release's owning project.
- `403`: FORBIDDEN — wallet does not own this release's project.
- `404`: PROMOTE_TARGET_NOT_FOUND — release does not exist.
- `409`: PROMOTE_RELEASE_NOT_READY, PROMOTE_NO_OP, PROMOTE_PROJECT_NO_LIVE_RELEASE, PROMOTE_WARNING_REQUIRES_ACK, or PROMOTE_DEPLOYMENT_UNSERVABLE. The unservable error's `details.missing` distinguishes `release_state_snapshot` from `deployment`, and `next_actions` points at re-applying the original spec.

---

[API](https://skmtc.dev/run402/apis/run402-api.md) · [All operations](https://skmtc.dev/run402/apis/run402-api/llms.txt) · [OpenAPI document](https://skmtc.dev/run402/apis/run402-api/revisions/893607557f50?raw)
