ZZD public AI gateway · zzd_public_ai_gateway_v3 Resource: 03 · Scoped REST deployment API / 部署接口 Canonical map: https://zzd.show/docs/ai/map.json Full handbook: https://zzd.show/llms-full.txt REST capabilities: https://zzd.show/api/v1/capabilities/ # ZZD scoped REST v1 deployment API First GET the original Show and inspect `runtime.renderer`. Complete local repositories use `capabilities.source_packages.routes`: upload, seal, build, activate with current generation and optional real release.prompt/parent_release_id, then read back runtime.active_version and verify the stable links.blank. MCP supports source-version list/get/build/activate. A ready build is not yet deployed. Classic deployment receipts explicitly say default_run_uses_saved_pieces; when false, their manifest verification does not update default Run. This is the supported platform-shaped REST control plane for an external Codex, Claude Code, or other authorized coding agent. The same scoped operations are also available through the stateless Streamable HTTP MCP endpoint at `https://zzd.show/mcp/v1/`. - API base: `https://zzd.show/api/v1/` - Public discovery: `GET https://zzd.show/api/v1/capabilities/` - Authentication: `Authorization: Bearer ` - Bearer project-route JSON success: `{"ok":true,"data":...}` - Bearer project-route JSON error: `{"ok":false,"error":{"code":"stable_code", "message":"...","details":{...}}}` - Binary exception: the authenticated thumbnail-content route streams image bytes with `Cache-Control: private, no-store`; its errors use the normal JSON error envelope. - Mutation retries: send a stable `Idempotency-Key` of 8–128 safe characters. Keys are recommended for POST, PUT, and PATCH and are replayable for the window reported by capabilities. ## Choose the client branch that fits the AI host ZZD does not require every agent to use one fragile integration. Negotiate live capabilities, then select a route suited to the task. Worker software and Show deployment SDKs have different capabilities: - remote Streamable HTTP MCP — best when the AI host or organization has approved the canonical MCP origin; - platform machine software ZIP — download the current zzd-machine release from `/machines/software/` in the owner browser. Software and private connection ZIPs are separate; this is not a stdio bridge; - typed Python/Node SDK and local stdio source candidates — distinct development components, not current public package installation instructions. Use an already approved installation only; otherwise choose an available transport without bypassing host approval; - direct scoped REST — the high-freedom primitive for advanced clients that preserve the advertised plan/CAS/idempotency rules. These branches are freedom to choose a transport, not freedom to bypass ownership or user intent. Risky operations remain available through explicit capability scopes and signed transactions. Endpoint hostnames live in discovery/profile configuration so deployments can move without hardcoded IPs. Do not hide an IP or hostname by splitting or encoding strings: obfuscation is not security. Use HTTPS, a locally pinned origin allowlist, short-lived scoped credentials, no redirects, and redacted logs. Port 443 is the production default; an explicitly pinned staging/self-hosted origin may use another legal HTTPS port. Discovery never grants that exception or enlarges the local trust root. Remote raw IP literals remain invalid, and loopback HTTP is only for an explicit development profile with an explicit port. ## Transactional deployment protocol For maintained AI work, prefer this cohesive sequence over repeated low-level upload/update calls: 1. read capabilities and inspect Show source plus the asset manifest; 2. submit the complete desired manifest to `deployments/plan/`; 3. follow returned asset actions using `assets/sync/`: identical normalized mount path + SHA-256 is a no-op; replacement requires `replace=true` and the exact current `if_match_sha256`; 4. apply the exact manifest and signed plan token atomically; 5. verify source and asset revisions against the desired manifest; 6. only after verification, finalize asset tokens; then, if the user explicitly asks, call the separate publication state machine. Plan/apply never makes a Show public. If a failure occurs after apply, compensate the source first with its release-backed rollback token, then compensate synchronized assets in reverse forward order. Signed tokens are scoped to the Show and owner and expire at the durations reported by capabilities. If duplicate legacy bindings claim one normalized mount path, the protocol returns `ambiguous_mount_path` and refuses to choose or delete an arbitrary row; repair the bindings explicitly before retrying. The typed REST transaction surface is: - `GET /api/v1/shows/{show_id}/assets/manifest/` - `GET /api/v1/shows/{show_id}/assets/{binding_id}/content/` - `POST /api/v1/shows/{show_id}/assets/sync/` - `POST /api/v1/shows/{show_id}/assets/sync/finalize/` - `POST /api/v1/shows/{show_id}/assets/sync/rollback/` - `POST /api/v1/shows/{show_id}/deployments/plan/` - `POST /api/v1/shows/{show_id}/deployments/apply/` - `POST /api/v1/shows/{show_id}/deployments/verify/` - `POST /api/v1/shows/{show_id}/deployments/rollback/` Always fetch capabilities at the start of a task. This handbook explains adoption; the live capabilities response is authoritative if routes, limits, or feature availability evolve. ## Durable collaboration surface The portable v2 bundle adds an isolated feedback and distributed work control plane. These routes do not replace the Show deployment API; they preserve online memory and atomically fence one complete task lifecycle to one worker/thread: - `GET /api/v1/collaboration/policy/` - `POST /api/v1/collaboration/workers/` - `GET /api/v1/collaboration/workers/` - `GET /api/v1/collaboration/workers/{worker_id}/` - `POST /api/v1/collaboration/workers/{worker_id}/close/` - `GET /api/v1/collaboration/feedback/` - `POST /api/v1/collaboration/feedback/` - `GET /api/v1/collaboration/feedback/{feedback_id}/` - `POST /api/v1/collaboration/feedback/{feedback_id}/events/` - `POST /api/v1/collaboration/feedback/{feedback_id}/artifacts/` - `GET /api/v1/collaboration/tasks/` - `POST /api/v1/collaboration/tasks/` - `GET /api/v1/collaboration/tasks/{task_id}/` - `POST /api/v1/collaboration/tasks/{task_id}/messages/` — requester/maintainer/owner discussion; does not transfer ownership. - `POST /api/v1/collaboration/tasks/{task_id}/requeue/` — maintainer-only, version-fenced recovery after a documented resolution; never steals an active claim. - `POST /api/v1/collaboration/tasks/{task_id}/actions/` - `POST /api/v1/collaboration/tasks/{task_id}/artifacts/` - `GET /api/v1/collaboration/artifacts/{artifact_id}/content/` - `GET /api/v1/collaboration/events/?after={cursor}` Every collaboration mutation requires a stable 8–128 character `Idempotency-Key`. Claim responses return a short-lived lease secret; keep it only in an OS keyring or separate `0600` claim-secret file, never in the ordinary worker-state file. Post-claim writes require the bound credential, worker, session, claim UUID, epoch, state version, lease and expiry to agree. A machine loss therefore cannot silently transfer ownership or erase the server event ledger. Existing authorized browser-login clients retain their independent `browser:login` scope and challenge protocol: - GET /api/v1/browser-auth/protocol/ - POST /api/v1/browser-auth/authorize/ - POST /api/v1/browser-auth/renew/ A machine ZIP does not acquire this scope by migration. Source-only validation remains POST /api/v1/shows/{show_id}/pieces/validate/. ## Canonical-host boundary The developer API and credential control plane accept only the exact canonical `https://zzd.show` Host and return a featureless 404 on an alternate host. Do not call them through `www.zzd.show`, `code.zzd.show`, the server IP, an alternate port, a custom domain, or a proxy-selected Host. Send the bearer only to canonical `https://zzd.show/api/v1/...` endpoints and never forward it across a redirect. `code.zzd.show` is for the editor/preview URLs, not bearer API requests. ## 1. Obtain and store a developer credential Credential creation is a signed-in user control-plane action, not a bearer API bootstrap. The user can use the Credentials UI or these same-origin session-authenticated routes: - `GET /api/v1/developer-credentials/` — list lifecycle metadata and the current scope/expiry catalog; - `POST /api/v1/developer-credentials/create/` — create after CSRF validation from the signed-in browser; - `POST /api/v1/developer-credentials/{credential_id}/revoke/` — revoke one explicitly selected owned manual/legacy credential with CSRF; - `/machines/` — the primary machine connection workspace; - `POST /machines/generate/` — deliberately create a connection/version; - `POST /machines/versions/{version_id}/download/` — retrieve the same stored ZIP bytes; no credential creation, rotation or revocation; - `GET /machines/credentials/{credential_id}/revoke/` — preview impact; - `POST /machines/credentials/{credential_id}/confirm-revoke/` — revoke only the explicitly selected credential after session/CSRF validation; - the former `.zzdmcp` and `ai-bridge.pem` generation endpoints now return 410 with the machine workspace migration path. Do not retry them or silently substitute a credential creation request. Existing legacy credentials, workers and claims keep their original scope/identity rules. Their raw secrets cannot be recovered from a hash. Keep the original file until you deliberately stop using that credential; a new machine ZIP is an independent connection, not a conversion or a Show-permission upgrade. Machine connection credentials are long-lived until individually revoked or the account is restricted. They still share the account's active-key limit. A lost legacy file requires an explicit new connection or a separately authorized scoped token; revoke only the specific old credential after checking its dependent work. The user chooses a label and least-privilege scopes. Credentials default to no calendar expiry; a manual token may have an explicitly chosen finite expiry. The signed-in owner then confirms creation. The raw value is returned exactly once; ZZD stores only its hash and lifecycle metadata. Never ask the user to paste that response into chat. A browser-assisted agent may complete the same-origin UI flow without reading cookies or echoing the token. Keep a manual value in the explicitly selected private credential store (for example, an explicitly authorized OS keychain entry). Separately reviewed SDK source candidates may support an argv-only `credential_command` (for example an OS keyring or organization credential broker); shell command strings are rejected, broker stderr is discarded, and secret output is not serialized. A profile under `~/.config/zzd/` may select `token_env`, the broker argv and pinned origins, but must never contain the bearer itself. The AI Bridge PEM may live in the local project directory solely for controlled access by the authorized local coding agent, but it must be added to `.gitignore`, never committed, and never uploaded to ordinary chat, cloud drives, tickets, or unrelated services. Never place the raw bearer in a prompt, transcript, source file, screenshot, URL, browser Show code, asset, log, or final answer. Available scopes are discovered live. REST v1 currently uses: - `identity:read` — verify account identity with `/me/`; - `shows:read` — list and inspect owned Shows; - `shows:write` — create/update draft metadata and replace Pieces; - `shows:publish` — publish or unpublish after confirmation; - `assets:write` — upload and bind safe asset bytes; - `baas:write` — provision the bounded runtime-identity BaaS profile; - `collections:write` — create a collection and attach owned Shows. Other advertised scopes may support separate surfaces. Do not request a broad scope merely because it exists. A BaaS app/service runtime credential and a legacy CLI secret are different credentials and must never be used as developer bearer tokens. Permanent credentials use `expires_at: null` and remain revocable. The signed-in credential UI/API is authoritative for current lifetime, active status, last use, and revocation. An older connection file's saved date is issuance metadata: verify the current authenticated `/me/` identity and active status instead of forcing the owner to renew or download the same connection every 14 days. Keep the original credential, installation, worker and task identities. Use the current official client for the permanent-lifetime contract; retained older software remains an immutable historical version, not proof of permanent-mode compatibility. ## 2. Discover and verify ```http GET /api/v1/capabilities/ HTTP/1.1 Host: zzd.show Accept: application/json ``` Then verify the selected local credential without printing it: ```http GET /api/v1/me/ HTTP/1.1 Host: zzd.show Authorization: Bearer Accept: application/json ``` Confirm that the returned account ID is the intended ZZD user and that returned scopes cover the planned operations. Stop on a mismatch. ## 3. Create a safe Show draft Develop and test in the user's local workspace first. Keep a local `.zzd/project.json`-style manifest containing non-secret information such as the canonical origin, Show ID, Piece names, source hashes, asset mount paths, and last validation notes. Never put the bearer credential in that manifest. ```http POST /api/v1/shows/ HTTP/1.1 Authorization: Bearer Content-Type: application/json Idempotency-Key: tilt-garden-create-20260801 { "title": "Tilt Garden", "desc": "An IMU-controlled visual study.", "prompt": "Preserve the three live Pieces, keep pointer and keyboard fallbacks, validate orientation permission on mobile, test blank/edit/embed states, and rerun the full matrix after dependency or motion changes.", "context": "tilt-garden", "category": "imu", "tags": ["imu", "interactive"], "cdns": ["https://cdn.jsdelivr.net/npm/three@0.179.1/build/three.module.js"], "esm": ["https://cdn.jsdelivr.net/npm/three@0.179.1/build/three.module.js"] } ``` The server ignores any attempt to choose public state and creates an owned `visibility="private"` draft. Deprecated `private`/`hidden` response aliases mirror that value and are not writable. Unknown/server-controlled fields are rejected. `context` must be unique for the owner when set; `category` is a bounded lowercase slug; dependency URLs must be bounded public HTTPS URLs without credentials or private/local hosts. Save the returned `data.id` and `data.links`. Use `GET /api/v1/shows/` to list owned Shows, `GET /api/v1/shows/{show_id}/` to retrieve one with Pieces/assets/BaaS/ collections, and `PATCH /api/v1/shows/{show_id}/` to update supported metadata. A PATCH returns a published Show to `visibility="private"`. ## 4. Replace authoritative Pieces ```http PUT /api/v1/shows/{show_id}/pieces/ HTTP/1.1 Authorization: Bearer Content-Type: application/json Idempotency-Key: G0_REDACTED { "pieces": [ {"generic":"html","name":"index.html","raw":"

Tilt Garden

"}, {"generic":"css","name":"style.css","raw":"#app { min-height: 100vh; }"}, {"generic":"js","name":"app.js","raw":"document.querySelector('#app').dataset.ready = 'true';"} ] } ``` This is an atomic whole-tree replacement, not a merge. Read the current Show first during maintenance, include every Piece that must remain, and keep a local/known-good copy. At least one HTML Piece is required. Names are unique safe basenames of at most 30 characters; generic is exactly `html`, `css`, or `js`. Compiled/unplug/runtime fields and unsafe wrapper/dynamic execution patterns are rejected. Current size limits are returned by capabilities (currently up to 32 Pieces, 512 KiB each, 2 MiB combined). ### Record meaningful prompt-to-deployment releases After a coherent iteration is deployed and validated, bind the initiating prompt and worker settings to an exact source/config snapshot: ```http POST /api/v1/shows/{show_id}/releases/ Authorization: Bearer Content-Type: application/json { "label": "Mobile interaction pass", "prompt": "The exact user prompt for this iteration.", "worker_client": "codex", "worker_provider": "openai", "worker_model": "gpt-5.6-sol", "reasoning_effort": "high", "duration_ms": 420000, "outcome_summary": "What materially changed and what was validated.", "status": "validated", "locale": "en" } ``` The response includes the localized delivery table and Show identity. Explicitly show both PK (`id`) and Work ID (`public_id`) in user-facing feedback; prefer `data.delivery.feedback_line` and retain the actual status and returned links. It stores the user prompt and observable result, never hidden model reasoning or intermediate chatter. Read the history with `GET /api/v1/shows/{show_id}/releases/` and one exact checkpoint with `GET /api/v1/shows/{show_id}/releases/{release_id}/`. ## 5. Upload and bind safe assets Send exactly one `multipart/form-data` file per request: ```text POST /api/v1/shows/{show_id}/assets/ Authorization: Bearer Idempotency-Key: tilt-garden-asset-hero-v1 file= mount_path=images/hero.webp role=attachment context=Hero image for the ready state ``` The endpoint accepts allowlisted non-executable project asset bytes, creates an owned Upload and StaticFileBinding, and returns its stable `/assets/show/{show_id}/{mount_path}` runtime URL. While the Show is draft, the bytes stay in opaque private storage outside public media. The stable URL is available only through the normal Show-access/ signed-in owner browser check and is streamed with private/no-store; it is not a bearer asset-download API. There is no raw public-media object or public private-storage route to guess. Explicit publication promotes a public presentation copy while retaining the private source. Unpublish or a later Show content, Piece, asset, thumbnail, or BaaS mutation returns the Show to draft and removes promoted public copies after commit. The stable mount URL does not change across those states. Original point-cloud assets include `.ply`, `.sog`, and raw binary `.splat`. Keep the original extension and an explicit relative mount such as `data/camp-ground.splat`; use role `attachment` for runtime access. They remain non-editable binary assets, not JS Pieces or thumbnails. Chunked Creator transfers, direct assets and asset-sync/ deployment descriptors share the current capabilities extension list. Runtime and authorized development ZIP delivery retain exact bytes; a successful upload does not certify a viewer/decoder or grant rights to someone else's scene. Validate the intended viewer separately. The upload route does not fetch remote URLs or accept HTML, JavaScript, credentials, archives, or server code. Filenames and relative mount paths are validated; `..`, absolute paths, duplicate active mount paths, and ownership violations fail closed. Read live capabilities for transport envelopes and storage capacity; there is no per-asset or aggregate product quota. Large original files use the existing resumable upload flow; its chunk limit is not a file limit. For an explicit replacement, use the existing asset-sync protocol with `replace=true` and exact `if_match_sha256` from the current asset manifest. Preserve the original Show, mount and rollback receipt; do not silently overwrite, rename the format, or invent a deletion endpoint. Ordinary upload does not publish the Show or change rights. ## 6. Provision bounded BaaS only when needed REST v1 currently provisions one isolated, private runtime-identity profile for an owned Show. It is not arbitrary Space/database access. ```http POST /api/v1/shows/{show_id}/baas/runtime-identity/ HTTP/1.1 Authorization: Bearer Content-Type: application/json Idempotency-Key: tilt-garden-runtime-state-v1 { "namespaces": ["app-state"], "guest_write": false, "confirm_guest_write": false, "guest_ttl_days": 30 } ``` Namespace names are bounded lowercase slugs. Anonymous guest writes are temporarily disabled in REST v1: `guest_write` must remain `false`. Use authenticated runtime identity for writes until ZZD publishes per-app, per-owner, and per-subject runtime quotas. Provisioning or changing BaaS returns the Show to draft. If a Show already has a backend outside this v1-managed isolated profile, the API returns a conflict instead of taking it over. Use the signed-in BaaS UI for broader supported designs. ## 7. Create and attach a collection when requested ```http POST /api/v1/collections/ HTTP/1.1 Authorization: Bearer Content-Type: application/json Idempotency-Key: imu-collection-create-20260801 {"title":"IMU Experiments","desc":"Motion and orientation Shows."} ``` Collection titles are currently at most 20 characters and unique per owner. Attach an owned Show with an empty JSON object: ```http PUT /api/v1/collections/{collection_id}/shows/{show_id}/ HTTP/1.1 Authorization: Bearer Content-Type: application/json Idempotency-Key: imu-collection-attach-show-42 {} ``` Both collection and Show must belong to the credential owner. REST v1 does not expose collection deletion or detach; use the signed-in UI with explicit target review when those actions are needed. ## 8. Validate, upload the thumbnail, and publish Open the returned blank, edit, embed, and detail links in a browser the user controls. Test desktop/mobile and every meaningful state as specified by `quality` and `handoff`. Server publication preflight checks metadata/source structure; it does not execute the browser experience or prove quality. REST v1 has no browser-validation operation. After the real browser matrix passes, capture a fresh final usable state and upload exactly one verified cover image: ```text POST /api/v1/shows/{show_id}/thumbnail/ Authorization: Bearer Idempotency-Key: G0_REDACTED file= ``` The endpoint accepts a non-animated JPEG, PNG, or WebP whose filename extension matches the decoded format. Current limits are 8 MiB, 320x180 minimum, and 20 megapixels maximum. It uses the account asset quota, replaces the previous developer-managed thumbnail, returns the Show to draft, and stores the bytes privately until publication. Verify the owned draft thumbnail without exposing a storage name: ```http GET /api/v1/shows/{show_id}/thumbnail/content/ HTTP/1.1 Host: zzd.show Authorization: Bearer ``` That response is private/no-store image bytes. It is not public evidence and another owner receives not found. Before publication the Show must have a nonempty description, category, at least one tag, at least one HTML Piece, a procedural prompt of at least 80 characters, and a verified thumbnail, and must pass source preflight. Only after successful validation and explicit user confirmation: ```http POST /api/v1/shows/{show_id}/publication/ HTTP/1.1 Authorization: Bearer Content-Type: application/json Idempotency-Key: tilt-garden-publish-confirmed-v1 {"action":"publish","confirm":true} ``` This is the only REST v1 route that can set public, visible state. A failed preflight returns HTTP 409 with structured failures; fix them rather than bypassing the check. ## 9. Maintain, recover, and unpublish For every update: GET the current detail, record a known-good source and asset manifest, PATCH only intended metadata, PUT the complete intended Piece tree, add versioned assets if needed, rerun the same browser matrix, POST a replacement verified thumbnail when visible output changed, and ask again before republishing. Draft-producing Show mutations set canonical `visibility="private"`, retain private source bytes, and remove promoted public presentation copies; this intentionally stops public access during API maintenance. To withdraw access after explicit confirmation: ```json {"action":"unpublish","confirm":true} ``` Send that body to the same publication endpoint. It returns the Show to `visibility="private"`. REST v1 intentionally exposes no Show, asset, collection, BaaS-data, credential, or Sale deletion. Use the signed-in control plane and exact impact review for unsupported or destructive actions. Streamable HTTP MCP is available at `POST /mcp/v1/` with the same scoped bearer, ownership checks, quotas and draft-first lifecycle as REST v1. `/docs/ai/` is the public human-readable discovery page; the legacy `/mcp/` directory is not a public navigation entry. Clients may use either transport; neither transport bypasses client or organization approval and neither may publish without explicit user confirmation. ## Reuse authorized access / 复用已有授权接入 用户提供项目和部署意图即可。AI先复用用户已经批准的本机连接/私有路径与原身份;新Demo或新线程不等于新token。不要搜索环境中的其它账号秘密,不借用其它worker/claim,不自动选择未知旧安装或扩大scope。 1. 读取 GET https://zzd.show/api/v1/capabilities/,使用实际已有本机凭据 GET https://zzd.show/api/v1/me/(identity:read)核对账号和当前权限。秘密留在0700私有目录/0600文件,不进入prompt、源码或输出。 2. 若已有正常本人网页登录,可 GET https://zzd.show/api/v1/developer-credentials/ 获取本人的非秘密metadata;保留旧credentials字段,并读取reuse.stored_zip_version。遵循pagination.next,has_more=true时不能把首100项称作全库。代理browser session不能管理凭据。 3. 根据已有scopes和用户批准的明确版本选择原ZIP:仅向返回的 /machines/versions/{version_id}/download/ 发送本人session+CSRF的POST,下载原保存字节,不调用create/generate。下载仍检验owner/active/package,metadata不证明本机持有秘密或包必然可下载。没有保存ZIP的旧手工token只可使用已保留的本机秘密,不能从哈希回取。 4. 具有shows:write的原凭据可 POST https://zzd.show/api/v1/shows/ 建立服务端强制visibility="private"草稿;维护使用已有作品及plan/CAS契约。tasks-only不能部署Show;原hightopo可读写本人私有Show/source/assets,但不含shows:publish或browser:login。公开发布仍需独立scope和既有明确发布授权,部署意图本身不等于公开意图。 5. 确无可用授权接入时才走现有登录/明确授权流程。缺少实际本机秘密或权限时准确说明这一缺口,不声称可部署、不为了每个项目重新创建连接。默认长期有效,可由本人单独撤销;显式有限期限凭据仍按本人所选期限,到期后由本人管理原连接,不由本文自动续权。 REST bearer只发精确canonical https://zzd.show/api/v1/(或获准的POST https://zzd.show/mcp/v1/),不跟随redirect转发;session/CSRF下载与bearer REST互不充当认证fallback。已有BaaS绑定仅按目标读取baas/space-sdk契约,不增加匿名BaaS、任意model/provider或外机/费用权利。 每次围绕具体作品的普通用户进度、部署、修复、回退和最终回复,都显式给出作品名称、PK(真实id)、UID/作品号(真实public_id)、API返回的正式打开链接和实际状态。优先复制 data.delivery.feedback_line;英文用 feedback_line_en。PK与作品号须是可复制文本,不能只藏在Run/Edit链接中,也不能只回复名称和链接。缺字段时沿用原身份GET同一已有作品补取,不造号、不建替代作品、不重领或注册worker、不额外向用户要授权。尚未创建就明确标识尚未取得,取得后立即补回;批量逐项给出,修复沿用原标识。私有作品注明需原有访问权限;普通链接和作品号不授予访问权,不附凭据、share token或runtime ticket。不得把计划/保存/清单核验冒称已部署或页面验收通过。交付表中分别写新站点名称与原站/原模板具体名称,未知原站写待核实,不猜测。reply_instruction/delivery仅用于普通回复,不回填manifest或严格JSON工具契约,也不在严格JSON输出前添加自然语言。漏报就补齐回复,不增加服务端拒绝、设备确认或审批流程。 In every normal user-facing progress update, deployment, repair, rollback and final handoff about a Show, explicitly display its title, PK (id), Work ID (public_id), API-returned canonical open link and actual state. Prefer data.delivery.feedback_line_en (feedback_line for Chinese). Both identifiers must be copyable text, not only part of a Run/Edit URL. Retrieve missing fields with the existing authorized GET for the same Show; do not invent IDs, create a replacement, register/claim again or ask for extra authorization. Report each Show separately and retain its identity across revisions. Note when original private access is required. Never append credentials, share tokens or runtime tickets. Distinguish planning, saving, manifest verification and actual browser/deployment results. Name the new site and the specific original site/template separately when tabulating; mark unknown source names unverified. delivery/reply_instruction are presentation guidance, not manifest or strict JSON input fields. Remedy an omitted identifier in the reply without adding a deployment gate.