---
title: "Plan a v2 deploy"
method: POST
path: "/apply/v1/plans"
tags: ["Apply"]
---

# Plan a v2 deploy

`POST /apply/v1/plans`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/apply/v1/plans`
- Auth: API key in header `SIGN-IN-WITH-X`

Plan a release. Returns the agent-deploy-observability v2 plan envelope: plan_id, operation_id, gateway-computed manifest_digest, missing_content, top-level resource diff buckets, expected_events, warnings, and optional payment_required. Body limit 5 MB; bytes go direct-to-S3. Body mode=reviewed_plan creates a short-lived require-able reviewed plan with plan_fingerprint/plan_expires_at and operation_id=null. Body required_plan verifies a reviewed plan before upload. Legacy ?dry_run=true computes the same envelope without creating plan or operation rows and is not require-able.

## Query parameters

- `dry_run` boolean

## Headers

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

## Request body

- DeployPlanRequest
  - `spec` ReleaseSpec, required
    - `base` union
      - object
        - `release` 'current' | 'empty', required
      - object
        - `release_id` string, required
    - `database` object
      - `migrations` MigrationSpec[]
        - `id` string, required
        - `checksum` string, required
        - `sql` string
        - `sql_ref` ContentRef
          - `sha256` string, required
          - `size` integer, required
          - `content_type` string: MIME type used by content/upload endpoints.
          - `integrity` string
        - `transaction` 'required' | 'none'
      - `expose` object
      - `zero_downtime` boolean
    - `secrets` object: Secret values are NEVER carried in the deploy spec (capability secrets-isolation, 2026-05-03). Set secrets out-of-band via POST /projects/v1/admin/{project_id}/secrets with body { key, value }, then declare them as required for the deploy via require[]. Use delete[] to remove keys atomically with the deploy. Plan-time response includes a MISSING_REQUIRED_SECRET warning if any require[] key is missing; commit-time gating raises a hard error if a required secret was deleted between plan and commit. Secret keys must match ^[A-Z_][A-Z0-9_]{0,127}$.
      - `require` string[]: Keys that must already exist in the project's secret store at commit time. Plan-time emits a MISSING_REQUIRED_SECRET warning for absent keys; commit-time hard-errors with HTTP 422.
      - `delete` string[]: Keys to delete from the project's secret store atomically with the activate phase. Unknown keys hard-error at commit-time gating.
    - `functions` object
      - `replace` object
      - `patch` object
        - `set` object
        - `delete` string[]
    - `site` union: Static site asset changes plus optional public-path authoring. `site.replace` and `site.patch` are mutually exclusive. `site.public_paths.mode: "explicit"` requires a complete `replace` map; once explicit, later site patches inherit that mode and do not publish newly added assets unless `public_paths.replace` is supplied. `mode: "implicit"` restores filename-derived public reachability. `site.embedding.frame_ancestors` (catalog keys, e.g. `["localhost"]`) lets the named embedders frame the site: omitted on a later apply carries the base release's declaration forward, `null` returns to the default deny. `site.sign_in_path` (a same-origin path such as `/join`) is the app's own sign-in page for MCP authorization, with the same carry-forward and `null` semantics. `site.noindex: true` sends `X-Robots-Tag: noindex, nofollow, nosnippet` on every response of the host, with the same carry-forward semantics (`null` or `false` clears).
      - object
        - `replace` FileSet: Map of relative file path to content-addressed blob reference.
        - `public_paths` SitePublicPathsSpec: Static public-path authoring. Implicit mode derives public paths from file names for backwards compatibility. Explicit mode publishes only the complete replace table and carries forward when omitted from later site patches.
          - `mode` 'implicit' | 'explicit', required
          - `replace` object: Complete public-path table, required when mode is explicit and rejected when mode is implicit. Keys are absolute public paths such as /events; query strings, fragments, encoded separators, dot segments, duplicate canonical paths, and internal Run402 namespaces are rejected.
        - `embedding` SiteEmbeddingSpec: Framing opt-in for the site (tenant-site-embedding). `frame_ancestors` lists platform embedding catalog KEYS, never raw origins; the gateway expands them into the `Content-Security-Policy: frame-ancestors` the host sends and drops `X-Frame-Options`. Catalog: `localhost` (http://localhost:* and http://127.0.0.1:*). Omitted on a later apply = carry the base release's declaration forward; `null` = clear back to the default deny. Unknown keys, duplicates, an empty array, or any other member fail with INVALID_SPEC naming the valid keys.
          - `frame_ancestors` string[], required
        - `sign_in_path` string, nullable: The app's own same-origin sign-in page (e.g. `/join`) that MCP authorization sends a signed-out user to, with `returnTo` appended. Omitted = carry forward; `null` = the hosted `/auth/sign-in`.
        - `noindex` boolean, nullable: `true` = every response of the host carries `X-Robots-Tag: noindex, nofollow, nosnippet` so search engines do not index it (previews, demos, unlisted sites). Omitted = carry forward; `null` or `false` = indexable (the default).
      - object
        - `patch` object
          - `put` FileSet: Map of relative file path to content-addressed blob reference.
          - `delete` string[]
        - `public_paths` SitePublicPathsSpec: Static public-path authoring. Implicit mode derives public paths from file names for backwards compatibility. Explicit mode publishes only the complete replace table and carries forward when omitted from later site patches.
          - `mode` 'implicit' | 'explicit', required
          - `replace` object: Complete public-path table, required when mode is explicit and rejected when mode is implicit. Keys are absolute public paths such as /events; query strings, fragments, encoded separators, dot segments, duplicate canonical paths, and internal Run402 namespaces are rejected.
        - `embedding` SiteEmbeddingSpec: Framing opt-in for the site (tenant-site-embedding). `frame_ancestors` lists platform embedding catalog KEYS, never raw origins; the gateway expands them into the `Content-Security-Policy: frame-ancestors` the host sends and drops `X-Frame-Options`. Catalog: `localhost` (http://localhost:* and http://127.0.0.1:*). Omitted on a later apply = carry the base release's declaration forward; `null` = clear back to the default deny. Unknown keys, duplicates, an empty array, or any other member fail with INVALID_SPEC naming the valid keys.
          - `frame_ancestors` string[], required
        - `sign_in_path` string, nullable: The app's own same-origin sign-in page (e.g. `/join`) that MCP authorization sends a signed-out user to, with `returnTo` appended. Omitted = carry forward; `null` = the hosted `/auth/sign-in`.
        - `noindex` boolean, nullable: `true` = every response of the host carries `X-Robots-Tag: noindex, nofollow, nosnippet` so search engines do not index it (previews, demos, unlisted sites). Omitted = carry forward; `null` or `false` = indexable (the default).
      - object
        - `public_paths` SitePublicPathsSpec: Static public-path authoring. Implicit mode derives public paths from file names for backwards compatibility. Explicit mode publishes only the complete replace table and carries forward when omitted from later site patches.
          - `mode` 'implicit' | 'explicit', required
          - `replace` object: Complete public-path table, required when mode is explicit and rejected when mode is implicit. Keys are absolute public paths such as /events; query strings, fragments, encoded separators, dot segments, duplicate canonical paths, and internal Run402 namespaces are rejected.
        - `embedding` SiteEmbeddingSpec: Framing opt-in for the site (tenant-site-embedding). `frame_ancestors` lists platform embedding catalog KEYS, never raw origins; the gateway expands them into the `Content-Security-Policy: frame-ancestors` the host sends and drops `X-Frame-Options`. Catalog: `localhost` (http://localhost:* and http://127.0.0.1:*). Omitted on a later apply = carry the base release's declaration forward; `null` = clear back to the default deny. Unknown keys, duplicates, an empty array, or any other member fail with INVALID_SPEC naming the valid keys.
          - `frame_ancestors` string[], required
        - `sign_in_path` string, nullable: The app's own same-origin sign-in page (e.g. `/join`) that MCP authorization sends a signed-out user to, with `returnTo` appended. Omitted = carry forward; `null` = the hosted `/auth/sign-in`.
        - `noindex` boolean, nullable: `true` = every response of the host carries `X-Robots-Tag: noindex, nofollow, nosnippet` so search engines do not index it (previews, demos, unlisted sites). Omitted = carry forward; `null` or `false` = indexable (the default).
    - `subdomains` object
      - `set` string[]
      - `add` string[]
      - `remove` string[]
    - `routes` ReleaseSpecRoutes, nullable: Replace-mode Web Routes table for function routes and exact static URL aliases. Absent or null carries forward base routes; {"replace": []} clears the route table. Entries are array-based, not path-keyed maps. Exact routes beat prefix routes, longest prefix wins, query strings are ignored for selection, matched route failures fail closed, and behavior is identical on managed subdomains, deployment hosts, and verified custom domains. Same normalized paths are allowed only with disjoint effective methods. Function entries may declare fixed tenant x402 pricing with pricing.mode=always and pay_to=org_default_payout; static aliases cannot be priced. Static aliases currently count toward the same temporary combined 100-entry route cap as function routes. Route failure codes include ROUTE_MANIFEST_LOAD_FAILED, ROUTED_INVOKE_WORKER_SECRET_MISSING, ROUTED_INVOKE_AUTH_FAILED, ROUTED_ROUTE_STALE, ROUTE_METHOD_NOT_ALLOWED, STATIC_ROUTE_TARGET_NOT_FOUND, ROUTED_RESPONSE_TOO_LARGE, PAYOUT_WALLET_REQUIRED, PAYOUT_WALLET_AMBIGUOUS, PAYOUT_WALLET_UNRESOLVED, LIGHTNING_RECEIVER_REQUIRED, LIGHTNING_RECEIVER_UNAVAILABLE, PAYMENT_PROOF_MISMATCH, and payment_insufficient_funds.
      - `replace` RouteSpec[], required
        - `pattern` string, required: Exact absolute path such as /admin or /events, or final prefix wildcard such as /admin/*. Static targets are exact-only and reject /* prefix patterns. Use both /admin and /admin/* when the shell path and nested admin paths should both route to a function. Query strings, regex, and mid-pattern wildcards are rejected. Max 256 bytes.
        - `methods` string[]: Uppercase HTTP methods. Function targets may omit methods for all supported methods. Static targets require explicit methods and accept only ["GET"] or ["GET", "HEAD"], both materialized as GET+HEAD. GET routes also match HEAD. A path match with a disallowed method returns 405 instead of falling through to static HTML.
        - `target` union, required: Web Route target. Function targets invoke a same-release Node 22 function. Static targets are exact static URL aliases that serve a materialized same-release static site file without redirecting or rewriting the browser URL.
          - object
            - `type` 'function', required
            - `name` string, required: Name of a materialized function in the same release.
          - object
            - `type` 'static', required
            - `file` string, required: Relative materialized static-site file path in the same release. No leading slash, query, fragment, backslash, empty segment, . segment, .. segment, or directory shorthand.
        - `pricing` RoutePricingSpec: Fixed-price tenant x402 policy for a public function web route. Supported only when target.type is function; static aliases cannot be priced. A challenge advertises maxTimeoutSeconds=300. After proof verification, Idempotency-Key (1-255 visible ASCII bytes) is scoped by verified payer, project, route pattern, and method, then bound to the normalized request contract. Same-payer/same-request/same-key retry is the only status/recovery operation. Successful routed responses expose platform-owned X-Run402-Payment-* headers and the x402 v2 PAYMENT-RESPONSE settlement header (base64 JSON SettleResponse: success, transaction, network, payer), on a first delivery and on a deduplicated replay alike; pending and conflict responses use a canonical error envelope. Tenant execution is at-least-once, so handlers should deduplicate durable effects by paymentId.
          - `mode` 'always', required: Every matching request must settle x402 before the handler runs.
          - `amount_usd_micros` integer, required: USD micro-units charged per matching request. 250000 means $0.25.
          - `amount_msat` integer: Pins the Lightning price in millisatoshis for the route's lightning / lightning-testnet entries. Valid only when networks names a Lightning value. Absent, each challenge quotes amount_usd_micros into whole satoshis through the platform Lightning quote; amount_usd_micros stays the ledger amount either way.
          - `pay_to` 'org_default_payout', required: Resolve the owning organization default payout wallet at activation and serve time.
          - `networks` RoutePricingNetwork[]: Optional supported network set. Omitted means mainnet only. A Lightning value requires the organization's Lightning receiver on that network (LIGHTNING_RECEIVER_REQUIRED at activation); a Lightning-only route needs no EVM payout wallet. receipt cannot be combined with a Lightning value.
          - `receipt` 'on_fulfillment': Receipt authoring intent from the public ReleaseSpec contract. Run402 Cloud currently rejects this field at routes.replace.<index>.pricing.receipt before mutation until an interoperable delegated-signer authorization carrier is published; ordinary priced routes remain supported.
          - `discovery` RoutePricingDiscovery: Opt-in x402 Bazaar discovery metadata. When present, the route's 402 challenge carries it (resource description, mimeType, serviceName, tags, iconUrl, extensions.bazaar) plus platform attribution (the tag run402 and accepts[].extra.hosted_by), and the first paid call through the CDP facilitator lists the route in the Bazaar. Allowed only on an exact route with exactly one HTTP method (GET may include HEAD). Canonical JSON of this object is at most 4096 bytes.
            - `description` string, required: What the endpoint does and when to call it. Used verbatim.
            - `mime_type` string: Response media type. Default application/json.
            - `service_name` string: Printable ASCII.
            - `tags` string[]: Printable ASCII. The Bazaar keeps five tags; the platform appends run402 as the fifth.
            - `icon_url` string, uri: Absolute https URL on a public host name, without credentials.
            - `input` object: A GET, HEAD, or DELETE route describes query parameters (query_params_schema, object example); a POST, PUT, or PATCH route describes its body (body_type required). Schemas may reference only local # fragments.
              - …
            - `output` object
              - …
    - `checks` object
    - `project_id` string, required: Run402 project ID. Public IDs are not UUIDs.
  - `manifest_ref` ContentRef
    - `sha256` string, required
    - `size` integer, required
    - `content_type` string: MIME type used by content/upload endpoints.
    - `integrity` string
  - `idempotency_key` string
  - `mode` 'reviewed_plan': Create a short-lived, require-able reviewed plan. Distinct from legacy ?dry_run=true, which creates no plan row.
  - `required_plan` ReviewedPlanRequirement
    - `plan_id` string, required: Reviewed plan id returned by POST /apply/v1/plans with mode=reviewed_plan.
    - `plan_fingerprint` string: Optional reviewed fingerprint returned with the reviewed plan. Send it when available for an exact client-visible identity check.
  - `vault` VaultCaptureDeclaration: Plan-time capture declaration for a project whose `vault_policy` is `required`. The gateway journals it on the operation and the activation-token mint compares the `capture_receipt` against it. The server never sees a commit id — the snapshot is named only by a keyed commitment.
    - `capture_id` string, required: Fresh per deploy; the head's `capture_binding` carries it back.
    - `snapshot_oid_hmac` string, required: Keyed commitment to the snapshot the head will bind. Never a plaintext commit id.

## Response `201`

Plan created, reviewed plan created, required-plan preflight verified, or legacy dry-run computed. Reviewed plans return plan_fingerprint/plan_expires_at and operation_id as null; legacy dry-run returns plan_id and operation_id as null. On a project with a `vault_policy` the response carries a `vault` rider; when the policy is `required` it also carries a `vault_push` next_action with the exact `capture_binding` values.

- DeployPlanResponse: Agent-deploy-observability v2 plan response. In legacy dry-run mode (`POST /apply/v1/plans?dry_run=true`), `plan_id` and `operation_id` are null and no plan/operation rows are created. In reviewed-plan mode, `plan_id` is non-null, `operation_id` is null, and `plan_fingerprint`/`plan_expires_at` make the plan require-able.
  - `static_continuity` StaticContinuity: Automatic one-hour origin availability for previously direct-public non-HTML paths absent from the active release. Cached copies may outlive the deadline. Plans omit the deadline until supersession.
    - `mode` 'absent_public_paths', required
    - `origin_retention_seconds` 3600, required
    - `scope` 'previously_public_non_html', required
    - `source_release_id` string, nullable, required
    - `source_release_generation` integer, nullable, required
    - `retained_path_count` integer, required
    - `origin_available_until` string, date-time, nullable: Omitted for candidate supersessions; null for an active release.
  - `kind` 'plan_response', required
  - `schema_version` 'agent-deploy-observability.v1', required
  - `actor` OperationActorSnapshot
    - `schema_version` 1, required
    - `principal` PrincipalSnapshot, required
      - `principal_id` string, required
      - `principal_type` string, required
      - `display_name_at_capture` string, nullable, required
      - `linked_identities_at_capture` LinkedIdentitySnapshot[], required
        - `identity_link_id` string, required
        - `kind` string, required
        - `public_subject` string, required
        - `display_subject` string, required
        - `verified_at` string, date-time, required
        - `status_at_capture` 'active', required
    - `authenticator` AuthenticatorSnapshot, required
      - `authenticator_id` string, required
      - `kind` string, required
      - `public_subject` string, required
    - `authority` AuthoritySnapshot, required: Discriminated immutable action-time authority: organization_membership, project_grant, grant_key, ci, system, legacy, or a preserved future kind. A link is never an authority kind.
      - `kind` string, required
      - `org_id` string, nullable
      - `project_id` string, nullable
      - `membership_id` string, nullable
      - `grant_id` string, nullable
      - `grant_key_id` string, nullable
      - `credential_id` string, nullable
      - `role` string, nullable
      - `scope` string[]
      - `reason_code` string, nullable
    - `captured_at` string, date-time, required
  - `plan_id` string, nullable, required: Null only for legacy ?dry_run=true. Reviewed-plan mode returns a non-null require-able plan id.
  - `operation_id` string, nullable, required: Null for legacy ?dry_run=true and reviewed-plan mode. Ordinary apply plans create an operation id.
  - `base_release_id` string, nullable, required
  - `manifest_digest` string, required
  - `is_noop` boolean, required
  - `summary` string, required: Pre-rendered counts in prose. User identifiers live in warnings[].affected.
  - `warnings` WarningEntry[], required: Plan-time non-blocking warnings. Always present; includes MISSING_REQUIRED_SECRET, deploy-observability warnings such as destructive removals and migration heuristics, Web Route warnings, and exact static URL alias warnings.
    - `code` string, required: Machine-readable warning discriminator. Known values include `MISSING_REQUIRED_SECRET`, `DESTRUCTIVE_FUNCTION_REMOVAL`, `DESTRUCTIVE_SUBDOMAIN_REMOVAL`, `DESTRUCTIVE_SITE_BULK_REMOVAL`, `DESTRUCTIVE_SECRET_REMOVAL`, `MIGRATION_NON_TRANSACTIONAL`, migration heuristic warnings, `ZERO_*`, `SITE_ENTRYPOINT_REMOVED`, `DIFF_TRUNCATED`, `FIRST_DEPLOY`, Web Route warnings such as `PUBLIC_ROUTED_FUNCTION` and `ROUTE_TABLE_NEAR_LIMIT`, and exact static URL alias warnings such as `STATIC_ALIAS_SHADOWS_STATIC_PATH`, `STATIC_ALIAS_RELATIVE_ASSET_RISK`, `STATIC_ALIAS_DUPLICATE_CANONICAL_URL`, `STATIC_ALIAS_EXTENSIONLESS_NON_HTML`, and `STATIC_ALIAS_TABLE_NEAR_LIMIT`. New codes may be added without a wire-version bump; agents should default to surfacing unknown codes verbatim.
    - `severity` 'info' | 'warn' | 'high', required: Suggested UI prominence. Info = informational; warn = recommend review; high = strongly recommend action before commit.
    - `requires_confirmation` boolean, required: When true, agents SHOULD surface to the user and require explicit confirmation before commit. Plan still returns 201 either way.
    - `message` string, required: Human-readable explanation. Safe to show verbatim in UIs.
    - `affected` string[]: Optional list of affected entity identifiers (e.g., secret keys, file paths). Code-specific shape; consult the discriminator before parsing.
    - `details` object: Optional code-specific structured payload. For `MISSING_REQUIRED_SECRET`: `{ missing_keys: string[] }`. For `STATIC_ALIAS_TABLE_NEAR_LIMIT`: `{ static_aliases, limit, limit_scope: "combined_routes_temporary" }`.
    - `confidence` 'heuristic': Optional confidence in the warning. Reserved for heuristic warnings; deterministic warnings like `MISSING_REQUIRED_SECRET` omit this.
  - `expected_events` string[], required: Operation event types expected during commit, in execution order.
  - `missing_content` object[], required
    - `sha256` string, required
    - `size` integer, required
    - `present` boolean, required
  - `payment_required` DeployPaymentRequired
    - `amount` string, required
    - `asset` string, required
    - `payTo` string, required
    - `reason` string, required
  - `rehearsal` PlanRehearsalEnvelope
    - `available` boolean, required: True for a persisted plan that carries at least one migration the commit would actually run, on a project that has a live release to protect. `run402 up` rehearses automatically when this is true and commits directly otherwise; clients never re-derive the decision.
    - `rehearse_url` string, nullable, required
    - `reason` 'no_migrations' | 'no_live_release' | 'migrations_unchanged' | 'ci_session_unsupported' | 'null', nullable, required: Why rehearsal is not offered, evaluated in this order: `no_migrations` (the spec declares none), `no_live_release` (a first deploy has nothing to branch from — commit directly), `migrations_unchanged` (the spec declares migrations but every one is already applied with an identical checksum — `migrations.new` is empty — so the commit runs no SQL and there is nothing to rehearse; commit directly), or `ci_session_unsupported` (the plan was made by a CI session, and the rehearse route refuses CI sessions with `403 REHEARSAL_CI_UNSUPPORTED` — commit directly, or rehearse with a wallet, sign-in session, or scoped agent grant key). `null` when `available` is true, or when only a persisted plan id is missing.
    - `next_actions` object[]: Present with `reason: "no_live_release"`, `reason: "migrations_unchanged"`, or `reason: "ci_session_unsupported"`: one `commit_plan` entry pointing at the plan's commit call and saying why the plan is not rehearsed.
      - `type` string, required
      - `command` string
      - `why` string, required
  - `migrations` object, required
    - `new` object[], required
      - `id` string, required
      - `checksum_hex` string, required
      - `transaction` 'default' | 'none', required
    - `noop` object[], required
      - `id` string, required
      - `checksum_hex` string, required
  - `site` object, required
    - `added` object[], required
      - `path` string, required
      - `sha256` string, required
      - `content_type` string, required
    - `removed` string[], required
    - `changed` object[], required
      - `path` string, required
      - `sha256_old` string, required
      - `sha256_new` string, required
      - `content_type_old` string, required
      - `content_type_new` string, required
      - `content_type_inferred` boolean, nullable
    - `totals` object: Present only when any site diff bucket was truncated.
      - `added` integer
      - `removed` integer
      - `changed` integer
  - `functions` object, required
    - `added` string[], required
    - `removed` string[], required
    - `changed` object[], required
      - `name` string, required
      - `fields_changed` string[], required
  - `secrets` object, required: NO changed slot; secrets diff exposes keys only.
    - `added` string[], required
    - `removed` string[], required
  - `subdomains` object, required
    - `added` string[], required
    - `removed` string[], required
  - `routes` RoutesDiff, required
    - `added` RouteEntry[], required
      - `pattern` string, required
      - `kind` 'exact' | 'prefix', required
      - `prefix` string, nullable, required
      - `methods` string[], nullable, required: Null means all supported methods.
      - `target` union, required: Web Route target. Function targets invoke a same-release Node 22 function. Static targets are exact static URL aliases that serve a materialized same-release static site file without redirecting or rewriting the browser URL.
        - object
          - `type` 'function', required
          - `name` string, required: Name of a materialized function in the same release.
        - object
          - `type` 'static', required
          - `file` string, required: Relative materialized static-site file path in the same release. No leading slash, query, fragment, backslash, empty segment, . segment, .. segment, or directory shorthand.
      - `pricing` RoutePricingSpec: Fixed-price tenant x402 policy for a public function web route. Supported only when target.type is function; static aliases cannot be priced. A challenge advertises maxTimeoutSeconds=300. After proof verification, Idempotency-Key (1-255 visible ASCII bytes) is scoped by verified payer, project, route pattern, and method, then bound to the normalized request contract. Same-payer/same-request/same-key retry is the only status/recovery operation. Successful routed responses expose platform-owned X-Run402-Payment-* headers and the x402 v2 PAYMENT-RESPONSE settlement header (base64 JSON SettleResponse: success, transaction, network, payer), on a first delivery and on a deduplicated replay alike; pending and conflict responses use a canonical error envelope. Tenant execution is at-least-once, so handlers should deduplicate durable effects by paymentId.
        - `mode` 'always', required: Every matching request must settle x402 before the handler runs.
        - `amount_usd_micros` integer, required: USD micro-units charged per matching request. 250000 means $0.25.
        - `amount_msat` integer: Pins the Lightning price in millisatoshis for the route's lightning / lightning-testnet entries. Valid only when networks names a Lightning value. Absent, each challenge quotes amount_usd_micros into whole satoshis through the platform Lightning quote; amount_usd_micros stays the ledger amount either way.
        - `pay_to` 'org_default_payout', required: Resolve the owning organization default payout wallet at activation and serve time.
        - `networks` RoutePricingNetwork[]: Optional supported network set. Omitted means mainnet only. A Lightning value requires the organization's Lightning receiver on that network (LIGHTNING_RECEIVER_REQUIRED at activation); a Lightning-only route needs no EVM payout wallet. receipt cannot be combined with a Lightning value.
        - `receipt` 'on_fulfillment': Receipt authoring intent from the public ReleaseSpec contract. Run402 Cloud currently rejects this field at routes.replace.<index>.pricing.receipt before mutation until an interoperable delegated-signer authorization carrier is published; ordinary priced routes remain supported.
        - `discovery` RoutePricingDiscovery: Opt-in x402 Bazaar discovery metadata. When present, the route's 402 challenge carries it (resource description, mimeType, serviceName, tags, iconUrl, extensions.bazaar) plus platform attribution (the tag run402 and accepts[].extra.hosted_by), and the first paid call through the CDP facilitator lists the route in the Bazaar. Allowed only on an exact route with exactly one HTTP method (GET may include HEAD). Canonical JSON of this object is at most 4096 bytes.
          - `description` string, required: What the endpoint does and when to call it. Used verbatim.
          - `mime_type` string: Response media type. Default application/json.
          - `service_name` string: Printable ASCII.
          - `tags` string[]: Printable ASCII. The Bazaar keeps five tags; the platform appends run402 as the fifth.
          - `icon_url` string, uri: Absolute https URL on a public host name, without credentials.
          - `input` object: A GET, HEAD, or DELETE route describes query parameters (query_params_schema, object example); a POST, PUT, or PATCH route describes its body (body_type required). Schemas may reference only local # fragments.
            - `query_params_schema` object
            - `body_type` 'json' | 'form-data' | 'text'
            - `body_schema` object
            - `example` unknown
          - `output` object
            - `example` unknown
            - `schema` object
    - `removed` RouteEntry[], required
      - `pattern` string, required
      - `kind` 'exact' | 'prefix', required
      - `prefix` string, nullable, required
      - `methods` string[], nullable, required: Null means all supported methods.
      - `target` union, required: Web Route target. Function targets invoke a same-release Node 22 function. Static targets are exact static URL aliases that serve a materialized same-release static site file without redirecting or rewriting the browser URL.
        - object
          - `type` 'function', required
          - `name` string, required: Name of a materialized function in the same release.
        - object
          - `type` 'static', required
          - `file` string, required: Relative materialized static-site file path in the same release. No leading slash, query, fragment, backslash, empty segment, . segment, .. segment, or directory shorthand.
      - `pricing` RoutePricingSpec: Fixed-price tenant x402 policy for a public function web route. Supported only when target.type is function; static aliases cannot be priced. A challenge advertises maxTimeoutSeconds=300. After proof verification, Idempotency-Key (1-255 visible ASCII bytes) is scoped by verified payer, project, route pattern, and method, then bound to the normalized request contract. Same-payer/same-request/same-key retry is the only status/recovery operation. Successful routed responses expose platform-owned X-Run402-Payment-* headers and the x402 v2 PAYMENT-RESPONSE settlement header (base64 JSON SettleResponse: success, transaction, network, payer), on a first delivery and on a deduplicated replay alike; pending and conflict responses use a canonical error envelope. Tenant execution is at-least-once, so handlers should deduplicate durable effects by paymentId.
        - `mode` 'always', required: Every matching request must settle x402 before the handler runs.
        - `amount_usd_micros` integer, required: USD micro-units charged per matching request. 250000 means $0.25.
        - `amount_msat` integer: Pins the Lightning price in millisatoshis for the route's lightning / lightning-testnet entries. Valid only when networks names a Lightning value. Absent, each challenge quotes amount_usd_micros into whole satoshis through the platform Lightning quote; amount_usd_micros stays the ledger amount either way.
        - `pay_to` 'org_default_payout', required: Resolve the owning organization default payout wallet at activation and serve time.
        - `networks` RoutePricingNetwork[]: Optional supported network set. Omitted means mainnet only. A Lightning value requires the organization's Lightning receiver on that network (LIGHTNING_RECEIVER_REQUIRED at activation); a Lightning-only route needs no EVM payout wallet. receipt cannot be combined with a Lightning value.
        - `receipt` 'on_fulfillment': Receipt authoring intent from the public ReleaseSpec contract. Run402 Cloud currently rejects this field at routes.replace.<index>.pricing.receipt before mutation until an interoperable delegated-signer authorization carrier is published; ordinary priced routes remain supported.
        - `discovery` RoutePricingDiscovery: Opt-in x402 Bazaar discovery metadata. When present, the route's 402 challenge carries it (resource description, mimeType, serviceName, tags, iconUrl, extensions.bazaar) plus platform attribution (the tag run402 and accepts[].extra.hosted_by), and the first paid call through the CDP facilitator lists the route in the Bazaar. Allowed only on an exact route with exactly one HTTP method (GET may include HEAD). Canonical JSON of this object is at most 4096 bytes.
          - `description` string, required: What the endpoint does and when to call it. Used verbatim.
          - `mime_type` string: Response media type. Default application/json.
          - `service_name` string: Printable ASCII.
          - `tags` string[]: Printable ASCII. The Bazaar keeps five tags; the platform appends run402 as the fifth.
          - `icon_url` string, uri: Absolute https URL on a public host name, without credentials.
          - `input` object: A GET, HEAD, or DELETE route describes query parameters (query_params_schema, object example); a POST, PUT, or PATCH route describes its body (body_type required). Schemas may reference only local # fragments.
            - `query_params_schema` object
            - `body_type` 'json' | 'form-data' | 'text'
            - `body_schema` object
            - `example` unknown
          - `output` object
            - `example` unknown
            - `schema` object
    - `changed` object[], required
      - `pattern` string, required
      - `fields_changed` string[], required
      - `from` RouteEntry: Materialized route entry exposed in release/deployment inventory and route diffs. This is the canonicalized form used by managed domains and custom-domain Worker manifests. Static alias entries carry target { type: "static", file } and exact GET+HEAD effective methods. Function entries may carry canonical pricing.
        - `pattern` string, required
        - `kind` 'exact' | 'prefix', required
        - `prefix` string, nullable, required
        - `methods` string[], nullable, required: Null means all supported methods.
        - `target` union, required: Web Route target. Function targets invoke a same-release Node 22 function. Static targets are exact static URL aliases that serve a materialized same-release static site file without redirecting or rewriting the browser URL.
          - object
            - `type` 'function', required
            - `name` string, required: Name of a materialized function in the same release.
          - object
            - `type` 'static', required
            - `file` string, required: Relative materialized static-site file path in the same release. No leading slash, query, fragment, backslash, empty segment, . segment, .. segment, or directory shorthand.
        - `pricing` RoutePricingSpec: Fixed-price tenant x402 policy for a public function web route. Supported only when target.type is function; static aliases cannot be priced. A challenge advertises maxTimeoutSeconds=300. After proof verification, Idempotency-Key (1-255 visible ASCII bytes) is scoped by verified payer, project, route pattern, and method, then bound to the normalized request contract. Same-payer/same-request/same-key retry is the only status/recovery operation. Successful routed responses expose platform-owned X-Run402-Payment-* headers and the x402 v2 PAYMENT-RESPONSE settlement header (base64 JSON SettleResponse: success, transaction, network, payer), on a first delivery and on a deduplicated replay alike; pending and conflict responses use a canonical error envelope. Tenant execution is at-least-once, so handlers should deduplicate durable effects by paymentId.
          - `mode` 'always', required: Every matching request must settle x402 before the handler runs.
          - `amount_usd_micros` integer, required: USD micro-units charged per matching request. 250000 means $0.25.
          - `amount_msat` integer: Pins the Lightning price in millisatoshis for the route's lightning / lightning-testnet entries. Valid only when networks names a Lightning value. Absent, each challenge quotes amount_usd_micros into whole satoshis through the platform Lightning quote; amount_usd_micros stays the ledger amount either way.
          - `pay_to` 'org_default_payout', required: Resolve the owning organization default payout wallet at activation and serve time.
          - `networks` RoutePricingNetwork[]: Optional supported network set. Omitted means mainnet only. A Lightning value requires the organization's Lightning receiver on that network (LIGHTNING_RECEIVER_REQUIRED at activation); a Lightning-only route needs no EVM payout wallet. receipt cannot be combined with a Lightning value.
          - `receipt` 'on_fulfillment': Receipt authoring intent from the public ReleaseSpec contract. Run402 Cloud currently rejects this field at routes.replace.<index>.pricing.receipt before mutation until an interoperable delegated-signer authorization carrier is published; ordinary priced routes remain supported.
          - `discovery` RoutePricingDiscovery: Opt-in x402 Bazaar discovery metadata. When present, the route's 402 challenge carries it (resource description, mimeType, serviceName, tags, iconUrl, extensions.bazaar) plus platform attribution (the tag run402 and accepts[].extra.hosted_by), and the first paid call through the CDP facilitator lists the route in the Bazaar. Allowed only on an exact route with exactly one HTTP method (GET may include HEAD). Canonical JSON of this object is at most 4096 bytes.
            - `description` string, required: What the endpoint does and when to call it. Used verbatim.
            - `mime_type` string: Response media type. Default application/json.
            - `service_name` string: Printable ASCII.
            - `tags` string[]: Printable ASCII. The Bazaar keeps five tags; the platform appends run402 as the fifth.
            - `icon_url` string, uri: Absolute https URL on a public host name, without credentials.
            - `input` object: A GET, HEAD, or DELETE route describes query parameters (query_params_schema, object example); a POST, PUT, or PATCH route describes its body (body_type required). Schemas may reference only local # fragments.
              - …
            - `output` object
              - …
      - `to` RouteEntry: Materialized route entry exposed in release/deployment inventory and route diffs. This is the canonicalized form used by managed domains and custom-domain Worker manifests. Static alias entries carry target { type: "static", file } and exact GET+HEAD effective methods. Function entries may carry canonical pricing.
        - `pattern` string, required
        - `kind` 'exact' | 'prefix', required
        - `prefix` string, nullable, required
        - `methods` string[], nullable, required: Null means all supported methods.
        - `target` union, required: Web Route target. Function targets invoke a same-release Node 22 function. Static targets are exact static URL aliases that serve a materialized same-release static site file without redirecting or rewriting the browser URL.
          - object
            - `type` 'function', required
            - `name` string, required: Name of a materialized function in the same release.
          - object
            - `type` 'static', required
            - `file` string, required: Relative materialized static-site file path in the same release. No leading slash, query, fragment, backslash, empty segment, . segment, .. segment, or directory shorthand.
        - `pricing` RoutePricingSpec: Fixed-price tenant x402 policy for a public function web route. Supported only when target.type is function; static aliases cannot be priced. A challenge advertises maxTimeoutSeconds=300. After proof verification, Idempotency-Key (1-255 visible ASCII bytes) is scoped by verified payer, project, route pattern, and method, then bound to the normalized request contract. Same-payer/same-request/same-key retry is the only status/recovery operation. Successful routed responses expose platform-owned X-Run402-Payment-* headers and the x402 v2 PAYMENT-RESPONSE settlement header (base64 JSON SettleResponse: success, transaction, network, payer), on a first delivery and on a deduplicated replay alike; pending and conflict responses use a canonical error envelope. Tenant execution is at-least-once, so handlers should deduplicate durable effects by paymentId.
          - `mode` 'always', required: Every matching request must settle x402 before the handler runs.
          - `amount_usd_micros` integer, required: USD micro-units charged per matching request. 250000 means $0.25.
          - `amount_msat` integer: Pins the Lightning price in millisatoshis for the route's lightning / lightning-testnet entries. Valid only when networks names a Lightning value. Absent, each challenge quotes amount_usd_micros into whole satoshis through the platform Lightning quote; amount_usd_micros stays the ledger amount either way.
          - `pay_to` 'org_default_payout', required: Resolve the owning organization default payout wallet at activation and serve time.
          - `networks` RoutePricingNetwork[]: Optional supported network set. Omitted means mainnet only. A Lightning value requires the organization's Lightning receiver on that network (LIGHTNING_RECEIVER_REQUIRED at activation); a Lightning-only route needs no EVM payout wallet. receipt cannot be combined with a Lightning value.
          - `receipt` 'on_fulfillment': Receipt authoring intent from the public ReleaseSpec contract. Run402 Cloud currently rejects this field at routes.replace.<index>.pricing.receipt before mutation until an interoperable delegated-signer authorization carrier is published; ordinary priced routes remain supported.
          - `discovery` RoutePricingDiscovery: Opt-in x402 Bazaar discovery metadata. When present, the route's 402 challenge carries it (resource description, mimeType, serviceName, tags, iconUrl, extensions.bazaar) plus platform attribution (the tag run402 and accepts[].extra.hosted_by), and the first paid call through the CDP facilitator lists the route in the Bazaar. Allowed only on an exact route with exactly one HTTP method (GET may include HEAD). Canonical JSON of this object is at most 4096 bytes.
            - `description` string, required: What the endpoint does and when to call it. Used verbatim.
            - `mime_type` string: Response media type. Default application/json.
            - `service_name` string: Printable ASCII.
            - `tags` string[]: Printable ASCII. The Bazaar keeps five tags; the platform appends run402 as the fifth.
            - `icon_url` string, uri: Absolute https URL on a public host name, without credentials.
            - `input` object: A GET, HEAD, or DELETE route describes query parameters (query_params_schema, object example); a POST, PUT, or PATCH route describes its body (body_type required). Schemas may reference only local # fragments.
              - …
            - `output` object
              - …
  - `static_assets` StaticAssetsDiff, required
    - `unchanged` integer, required
    - `changed` integer, required
    - `added` integer, required
    - `removed` integer, required
    - `newly_uploaded_cas_bytes` integer, required
    - `reused_cas_bytes` integer, required
    - `deployment_copy_bytes_eliminated` integer, required
    - `legacy_immutable_warnings` object[], required
      - `path` string, required
      - `sha256` string, required
      - `reason` string, required
    - `previous_immutable_failures` object[], required
      - `path` string, required
      - `previous_sha256` string, required
      - `candidate_sha256` string, required
    - `cas_authorization_failures` string[], required
  - `plan_fingerprint` string, nullable: Versioned fingerprint binding reviewed intent: normalized spec digest, manifest digest, concrete base identity, planner semantics, confirmation-required warnings, destructive sets, and diff identity. Present for reviewed-plan mode.
  - `plan_expires_at` string, date-time, nullable: Reviewed plan expiration timestamp. Present for reviewed-plan mode; current TTL is one hour.
  - `planner_semantics_version` string, nullable: Planner semantics version bound into plan_fingerprint.
  - `base_identity` string, nullable: Concrete base identity bound into plan_fingerprint, e.g. empty or release:<release_id>.
  - `next_actions` object[]: Structured recovery/apply actions. CLI actions include command and argv; direct HTTP callers can branch on type/path/method fields when present.
  - `vault` VaultPlanRider: Present on a plan response only when the project has a `vault_policy`; ABSENT for vaultless projects, whose responses stay byte-identical to pre-vault ones. When `activation_required` is true the response also carries a `vault_push` next_action naming the exact binding values to sign into the head.
    - `policy` 'required' | 'grandfathered'
    - `capture_id` string, nullable: Echo of the declaration, or null when none was declared.
    - `apply_plan_sha256` string: The canonical plan digest the head's `capture_binding` must carry.
    - `snapshot_oid_hmac` string, nullable
    - `activation_required` boolean: True iff `policy` is `required` — the commit must consume an activation token or an authorized override.
  - `effective_access` object[]: Declared access preview; pending until PostgreSQL rehearsal/apply. Custom operations may be null. Row checks remain runtime checks.
    - `table` string
    - `role` 'anon' | 'authenticated'
    - `declared_operations` string[], nullable
    - `required_privileges` string[], nullable
    - `validation` 'pending' | 'structural'
    - `row_checks` 'runtime'
    - `findings` string[]

## Other responses

- `400`: INVALID_SPEC, SUBDOMAIN_MULTI_NOT_SUPPORTED, etc.
- `403`: Wallet does not own this project, OR project is past_due/frozen/dormant (lifecycleGate). Envelope `code` distinguishes — `FORBIDDEN` for ownership, `PROJECT_PAST_DUE` / `PROJECT_FROZEN` / `PROJECT_DORMANT` for lifecycle.
- `409`: Reviewed-plan mismatch or stale reviewed plan, including REVIEWED_PLAN_EXPIRED, REVIEWED_PLAN_SPEC_MISMATCH, REVIEWED_PLAN_FINGERPRINT_MISMATCH, PLAN_SEMANTICS_VERSION_UNSUPPORTED, REVIEWED_PLAN_BASE_DRIFT, REQUIRED_PLAN_NOT_REVIEWED, or REVIEWED_PLAN_ALREADY_COMMITTED.

---

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