Build
Describe a new flow or supply existing YAML to modify.
The AI native company for Healthcare
Custom workflows
The Planner agent turns a natural-language operating specification into tested activities and a reusable workflow. It is a durable review loop—not a single prompt that silently deploys code.
Mental model
Describe a new flow or supply existing YAML to modify.
Approve the whole plan before step generation or testing, or reject it with revision feedback.
Test each generated step, inspect its results, then explicitly accept or request changes.
Use separate promotion authority to create an organization production definition.
Run once or on a schedule, observe phases, and query analytics.
Capability boundary
| Capability | Availability | What that means |
|---|---|---|
| Build from a Browser Agent recording | Self-service | Copy the displayed Recorded Task ID from an organization-owned recording and send it as context.recorded_task_id. REST, MCP, and the web app use the same FDE Planner path. |
| Portal navigation, extraction, upload, and download | Self-service | The Planner agent creates a browser step; use Studio when it needs direct work. |
| Pure computation and data normalization | Self-service | The Planner agent can create organization-scoped compute activities. |
| OCR and structured document extraction | Self-service | The Planner agent can compose the platform OCR activity and downstream transforms. |
| Sequence, parallel work, loops, branches, child flows, and error handling | Self-service | Describe cardinality, concurrency, stop rules, and failure behavior explicitly. |
| Call an existing integration capability | Existing setup required | The organization must already have an active connection and exposed capability. |
| Create a new HTTP API integration from documentation | Self-service | The Integration Factory discovers, generates, validates, and promotes organization-owned capabilities before the Planner agent uses them. |
| Password, TOTP, PIN, and portal credential use | Password Vault reference | Add the secret in Password Vault or provision it with SYSTEM credentials:write. Discover safe metadata with credentials:read, then bind the exact visible ID with personal or SYSTEM planner:write. Secrets never belong in the Planner agent request. |
| Automated SMS OTP retrieval | Self-service first number | Provision or retrieve the organization default with POST /sms-inbox. Use GET /sms-inbox/messages (sms:read) to complete the portal's initial number validation outside a workflow, then bind a credential with sms_phone_type: voip. Replays return the existing number; only XY can add a second inbox. |
| Automated email OTP retrieval | Existing setup required | Authorize the organization mailbox first, then bind that mailbox connection and a narrow sender or subject query in the workflow. A credential email field alone does not grant inbox access. |
| Specialized queues, entity writeback, webhooks, or typed domain clients | XY-built | Submit the interfaces and acceptance criteria as a custom-agent request. |
| Recurring production execution | Self-service | Promote, then create and operate a workflow schedule. |
| One-off execution of an eligible promoted Planner agent definition | Self-service | POST /workflow-definitions/{workflow_definition_id}/executions starts an idempotent asynchronous run with optional runtime input. No schedule is required. Coalescing singleton definitions use their managed trigger instead. |
| Run phases and governed analytics | Self-service read-only | Read phase projections and approved metrics without changing runtime state. |
| Build reporting in a partner UI | Self-service read-only | Use aggregate metrics or presentation-ready metric views with tile data and visualization metadata. |
| Design an existing workflow's operator queue | Personal or system access | Preview eligible app-managed promoted Planner queues. Approve through human web review or a system key with queue_ui:approve, then publish separately with a live-receipt check. Personal callers need explicit workflow access/configure permission. |
| Propose an analytics dashboard | Release preview | With dashboards:propose, create or edit an immutable proposal using registered governed query refs. Proposals stop at PREVIEW_READY; a human approves and publishes the exact version in XY. |
| Start Browser Studio | Existing Planner agent step required | Studio creates a separate retained authoring Chrome for an existing run_browser_workflow step. |
Tool map
| Need | MCP tools | When to use them |
|---|---|---|
| Create and follow the workflow draft | planner_start_build, planner_get_build, planner_get_events, planner_wait_for_events | Start here for every self-service workflow; reconnect to the same build instead of starting over. |
| Review the whole plan | planner_respond_to_plan | With planner:write, APPROVE before generating or testing steps, or REJECT with a comment requesting a revised plan. This does not accept a step or grant publication. |
| Reuse a demonstrated browser task | planner_start_build | Set context.recorded_task_id to the ID shown on an organization-owned Browser Agent recording. The Planner uses its transcript, action log, and existing code as context; supply credentials separately. |
| Supply login or MFA material | credentials_create, credentials_get, credentials_rotate | Create a secret-backed reference first, then send only credential_id to the Planner agent or Browser Studio. |
| View a Planner agent test browser | planner_get_browser_session, planner_set_browser_control | Observe or briefly control Chrome already running for generation or a step test. These tools do not create a browser. |
| Develop one browser step | browser_create_session, browser_prompt, browser_run_command, browser_apply_draft | Create a retained Studio session only after the Planner agent has proposed a browser step; apply the draft back for a fresh Planner agent test. |
| Review and advance steps | planner_test_step, planner_accept_step, planner_reject_step, planner_retry_step, planner_message_step | Test edits and make every business decision explicitly. |
| Create the production definition | planner_promote | Requires organization-service access with planner:promote after all steps are accepted; promotion does not publish a catalog agent. |
| Run once on demand | workflow_execute | Start an asynchronous run of an eligible promoted definition with optional input. Supply your own idempotency key when the caller may retry. Coalescing singleton definitions return a conflict before the key is reserved and must use their managed trigger. |
| Run on a cadence | schedule_create, schedule_trigger, schedule_pause, schedule_resume | Operate the promoted definition through a recurring schedule or trigger that schedule immediately. |
| Observe and report | workflow_get_phases, workflow_metric_dashboards_list, workflow_metric_dashboard_get, dashboard_agent_query | Read safe run progress, exact approved metric views, or governed natural-language analytics; render returned data in your own UI. |
| Propose a governed analytics dashboard | dashboard_proposal_start, dashboard_proposal_get | Release preview: return a durable run immediately, follow shared progress, and hand the immutable preview to a human in XY. No API or MCP approval or publication. |
Start and follow
# Use an exact Password Vault credential ID visible to this API identity.
# The example works with personal or organization-service planner:write access.
curl --fail-with-body --request POST \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Idempotency-Key: portal-reconciliation-v1" \
--header "Content-Type: application/json" \
"${XY_API_BASE}/planner/builds" \
--data '{
"request": "Sign in, collect open records, normalize them, and return a per-record result.",
"context": {
"portal_url": "https://portal.example.com/open-records",
"credential_id": "cred_REPLACE",
"recorded_task_id": "portal-open-records-v1",
"inputs": [{"name": "cutoff_date", "type": "string"}],
"outputs": [{"name": "results", "type": "array"}]
}
}'# Inspect the same build after a reload or interrupted command.
: "${BUILD_ID:?Use the existing public build ID}"
curl --fail-with-body --max-time 30 \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}"Durable progress
# Use 0 for the first read; otherwise use the last applied event ID.
: "${BUILD_ID:?Use the existing public build ID}"
: "${LAST_EVENT_ID:?Use the cursor saved for this build event stream}"
curl --fail-with-body --max-time 30 \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}/events?after=${LAST_EVENT_ID}"# Reconnect using the last applied SSE ID from this same stream.
: "${BUILD_ID:?Use the existing public build ID}"
: "${LAST_EVENT_ID:?Use the cursor saved for this build event stream}"
curl --fail-with-body --no-buffer --max-time 60 \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Accept: text/event-stream" \
--header "Last-Event-ID: ${LAST_EVENT_ID}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}/events?stream=true"
# A time limit or disconnect ends this read, not the worker.
# Persist applied event IDs before opening another read; never repeat a mutation.The shared projection includes phase_started / phase_completed, content-safe message_delta and cumulative message_snapshot, tool_started / tool_completed, immutable artifact_changed references, approval_required / approval_resolved, and warning, failed, cancelled, or succeeded. Transport heartbeats are not durable progress. Silence alone is not a failure, and a slow observer does not block the worker.
Public progress excludes hidden reasoning, prompts by default, credentials, customer records, unrestricted stdout, raw provider payloads, and unbounded patches. Fetch full diffs only through a separate authorized, size-bounded artifact endpoint. Every replay and artifact fetch rechecks access to the immutable run identity. MCP clients use bounded event polling as the reliable path; host progress notifications are optional. Terminal clients follow the same contract; this guide does not require an installed XY CLI or silently write artifacts into a repository.
Plan review
Read GET /api/v1/planner/builds/{build_id} with planner:read. Plan review is required when status is awaiting_input and planner_state.awaiting_plan_approval is true. Other input gates also use awaiting_input, so check both fields. Inspect the proposed steps, inputs, outputs, and constraints, keeping the returned state_version for the plan you reviewed.
# Use the build ID returned by build creation. Requires planner:read.
if ! BUILD="$(curl --fail-with-body \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}")"; then
exit 1
fi
printf '%s\n' "${BUILD}" | jq '{build_id, status, state_version, planner_state}'
if ! STATE_VERSION="$(printf '%s' "${BUILD}" | jq -er '
select(.status == "awaiting_input" and .planner_state.awaiting_plan_approval == true)
| .state_version | select(type == "number" and . > 0 and . == floor)
')"; then
printf 'No whole plan is awaiting approval with a positive state version.\n' >&2
exit 1
fi
# Stop here and review the proposed plan. Keep this STATE_VERSION for the decision.Send POST /api/v1/planner/builds/{build_id}/plan/actionswith action set to APPROVE or REJECT, a positive integer expected_state_version, and an Idempotency-Key header. A nonblank comment is required for rejection and optional for approval. Personal or SYSTEM keys with planner:write can decide a plan for a build they can access. Approval allows step generation and testing to begin; it grants no workflow promotion, catalog publication, or Queue UI approval/publication authority.
# Requires planner:write.
: "${STATE_VERSION:?Use the state_version of the plan you just reviewed}"
: "${PLAN_DECISION_ID:?Set a unique ID for this logical plan decision; retain it with the body}"
if ! PLAN_BODY="$(jq -en --argjson version "${STATE_VERSION}" '
$version | select(type == "number" and . > 0 and . == floor)
| {action: "APPROVE", expected_state_version: .}
')"; then
exit 1
fi
if ! curl --fail-with-body --request POST \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Idempotency-Key: ${PLAN_DECISION_ID}" \
--header "Content-Type: application/json" \
--data "${PLAN_BODY}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}/plan/actions"; then
printf 'Read the existing build and events before any further decision; do not retry this POST after ambiguous output.\n' >&2
exit 1
fi
# 202 queues a durable command. Poll the existing build and events for its outcome.To request a revised plan, use the same route with the rejection body below instead of sending approval. Replace 7 with the version you reviewed and use a new decision key. Read and review the revised plan before deciding again.
{
"action": "REJECT",
"comment": "Collect only open records and exclude archived records from the plan.",
"expected_state_version": 7
}For MCP, read with planner_get_build, then call planner_respond_to_plan with state_versionset to the version you reviewed; the tool maps it to REST expected_state_version. Replace the illustrative ID, version, and key below. To reject, change action to REJECT and include a nonblank comment in the arguments.
{
"tool": "planner_respond_to_plan",
"arguments": {
"build_id": "pbld_REPLACE",
"action": "APPROVE",
"state_version": 7,
"idempotency_key": "plan-decision-REPLACE"
}
}Queue UI Designer creates a durable design session with immutable versions and previews. A person approves an exact version and hash in the web review page, or an authorized system key records system approval through the separate approval operation. A publish call can then make it live. Publish takes the design version ID and compares the expected live publication receipt ID. Rollback takes a retained publication or captured baseline receipt ID, and each activation returns a fresh live receipt. Personal keys and personal MCP OAuth require Queue UI scopes and explicit workflow access/configure permission. System keys use authorized organization access; only they can hold queue_ui:approve. Custom React, new backend actions, and live editing of Git-managed queues are outside this surface.
Review
BUILD="$(curl --fail-with-body \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}")"
if ! STATE_VERSION="$(printf '%s' "${BUILD}" | jq -er '.state_version | select(type == "number")')"; then
printf 'Planner agent build response did not include state_version.\n' >&2
exit 1
fi
jq -n --argjson expected_state_version "${STATE_VERSION}" \
'{action: "ACCEPT", expected_state_version: $expected_state_version}' |
curl --fail-with-body --request POST \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Idempotency-Key: ${BUILD_ID}-step-1-accept-v1" \
--header "Content-Type: application/json" \
--data-binary @- \
"${XY_API_BASE}/planner/builds/${BUILD_ID}/steps/1/actions"Promote and operate
: "${XY_API_KEY:?Use an authorized key with planner:read to read the build and ETag}"
: "${XY_PROMOTION_SERVICE_KEY:?Use an organization service key with planner:promote}"
BUILD_HEADERS="$(mktemp)"
BUILD="$(curl --fail-with-body --dump-header "${BUILD_HEADERS}" \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}")"
BUILD_ETAG="$(awk 'tolower($1) == "etag:" {print $2; exit}' "${BUILD_HEADERS}" | tr -d '\r')"
rm -f "${BUILD_HEADERS}"
: "${BUILD_ETAG:?The Planner agent build response did not include ETag}"
printf '%s\n' "${BUILD}" | jq '{id, status, state_version}'
curl --fail-with-body --request POST \
--header "Authorization: Bearer ${XY_PROMOTION_SERVICE_KEY}" \
--header "Idempotency-Key: ${BUILD_ID}-promote-v1" \
--header "If-Match: ${BUILD_ETAG}" \
--header "Content-Type: application/json" \
"${XY_API_BASE}/planner/builds/${BUILD_ID}/promote" \
--data '{
"name": "Portal record reconciliation",
"description": "Collects and normalizes open records."
}'# No schedule is required. Use the ID returned by promotion.
: "${BUSINESS_ATTEMPT_ID:?Set a stable unique ID for this logical run}"
RESPONSE_HEADERS="$(mktemp)"
trap 'rm -f "${RESPONSE_HEADERS}"' EXIT
if ! RUN_RESPONSE="$(curl --fail-with-body --request POST \
--dump-header "${RESPONSE_HEADERS}" \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Idempotency-Key: portal-reconciliation-${BUSINESS_ATTEMPT_ID}" \
--header "Content-Type: application/json" \
"${XY_API_BASE}/workflow-definitions/${WORKFLOW_DEFINITION_ID}/executions" \
--data '{
"input": {"region": "west"}
}')"; then
printf '%s\n' "${RUN_RESPONSE}" >&2
exit 1
fi
printf '%s\n' "${RUN_RESPONSE}"
# 202 Accepted
# {
# "workflow_definition_id": "...",
# "workflow_instance_id": "...",
# "status": "running",
# "status_url": "/api/v1/workflow-runs/.../phases"
# }
# Follow the relative status_url returned above without duplicating /api/v1.
STATUS_URL="$(printf '%s' "${RUN_RESPONSE}" | jq -r '.status_url')"
STATUS_PATH="${STATUS_URL#/api/v1}"
POLL_SECONDS="$(awk 'tolower($1) == "retry-after:" {gsub("\r", "", $2); print $2}' "${RESPONSE_HEADERS}")"
POLL_SECONDS="${POLL_SECONDS:-2}"
case "${POLL_SECONDS}" in
*[!0-9]*|"") POLL_SECONDS=2 ;;
esac
if [ "${POLL_SECONDS}" -lt 1 ]; then
POLL_SECONDS=1
fi
# A newly accepted run can return 404 until its projection is created.
# Poll until the documented runtime status is terminal, or fail after 5 minutes.
TERMINAL_STATUS=""
DEADLINE=$(( $(date +%s) + 300 ))
while :; do
REMAINING=$(( DEADLINE - $(date +%s) ))
if [ "${REMAINING}" -le 0 ]; then
break
fi
if PHASE_RESULT="$(curl --silent --show-error \
--max-time "${REMAINING}" \
--write-out '\n%{http_code}' \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}${STATUS_PATH}")"; then
HTTP_STATUS="${PHASE_RESULT##*$'\n'}"
PHASE_RESPONSE="${PHASE_RESULT%$'\n'*}"
if [ "${HTTP_STATUS}" -ge 400 ] && [ "${HTTP_STATUS}" != "404" ]; then
printf '%s\n' "${PHASE_RESPONSE}" >&2
exit 1
fi
if [ "${HTTP_STATUS}" != "404" ]; then
EXECUTION_STATUS="$(printf '%s' "${PHASE_RESPONSE}" | jq -r '.execution_status // .workflow_status // ""')"
case "${EXECUTION_STATUS}" in
COMPLETED|COMPLETED_WITH_FAILURES|FAILED|TIMED_OUT|CANCELED|CANCELLED|TERMINATED)
TERMINAL_STATUS="${EXECUTION_STATUS}"
printf '%s\n' "${PHASE_RESPONSE}"
break
;;
esac
fi
fi
REMAINING=$(( DEADLINE - $(date +%s) ))
if [ "${REMAINING}" -le 0 ]; then
break
fi
SLEEP_SECONDS="${POLL_SECONDS}"
if [ "${SLEEP_SECONDS}" -gt "${REMAINING}" ]; then
SLEEP_SECONDS="${REMAINING}"
fi
sleep "${SLEEP_SECONDS}"
done
if [ -z "${TERMINAL_STATUS}" ]; then
printf 'Run did not reach a terminal state within 5 minutes.\n' >&2
exit 1
fi: "${SCHEDULE_TRIGGER_ATTEMPT_ID:?Set a stable unique ID for this logical trigger}"
curl --fail-with-body --request POST \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Content-Type: application/json" \
"${XY_API_BASE}/workflow-definitions/${WORKFLOW_DEFINITION_ID}/schedule" \
--data '{
"cron_expression": "0 9 * * 1-5",
"input": {"region": "west"},
"overlap_policy": "SKIP",
"note": "Weekday run"
}'
# Trigger that existing schedule now.
curl --fail-with-body --request POST \
--header "Authorization: Bearer ${XY_API_KEY}" \
--header "Idempotency-Key: schedule-trigger-${SCHEDULE_TRIGGER_ATTEMPT_ID}" \
--header "Content-Type: application/json" \
"${XY_API_BASE}/workflow-definitions/${WORKFLOW_DEFINITION_ID}/schedule/trigger" \
--data '{}'Choose an overlap policy, freeze explicit input, and update the schedule when its input contract changes.
Read execution or workflow-run phases without mutating the underlying run.
Read aggregate metrics or presentation-ready tile data, then render the charts and dashboards in your own product.
# XY first grants this API identity analytics-only view access to the exact
# production workflow definition. No catalog agent or installation is required.
curl --fail-with-body \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/workflow-definitions/${WORKFLOW_DEFINITION_ID}/metrics/dashboards"
# Render one approved view as presentation-ready JSON for your own UI.
# FROM_DATE and TO_DATE are YYYY-MM-DD dates and must be supplied together.
curl --fail-with-body \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/workflow-definitions/${WORKFLOW_DEFINITION_ID}/metrics/dashboards/operations?from=${FROM_DATE}&to=${TO_DATE}"Governed analytics
Query existing analytics with dashboards:read. Authoring requires the separate, sensitive, opt-in dashboards:propose scope, absent from default key scopes. Neither scope implies the other. Retired dashboards:writegrants no proposal authority, including on older keys.
useWorkflowAnalytics and useDashboardAuthoring capability access, and configure permission on the exact workflow. Editing also requires dashboard view permission for a non-STAFF caller. Revocation takes effect on subsequent requests.dashboards:propose and the organization must enable Marketplace keys, workflow analytics, and dashboard authoring. It acts as its managed organization service principal and can target eligible organization workflows without a human's capability or per-workflow grants. Mutations are attributed to that principal and key, never an impersonated human. A system key cannot approve or publish.# Personal key: dashboards:propose plus current human authoring permission.
: "${XY_DASHBOARD_PERSONAL_KEY:?Use an authorized personal key}"
: "${WORKFLOW_DEFINITION_ID:?Select the exact active workflow}"
: "${PROPOSAL_ATTEMPT_ID:?Set a stable unique ID for this logical proposal}"
BODY="$(jq -n --arg workflow_definition_id "${WORKFLOW_DEFINITION_ID}" \
'{prompt: "Create an operations dashboard using only registered governed query refs for this workflow.", workflow_definition_id: $workflow_definition_id}')" || exit 1
curl --fail-with-body --max-time 60 --request POST \
--header "Authorization: Bearer ${XY_DASHBOARD_PERSONAL_KEY}" \
--header "Idempotency-Key: ${PROPOSAL_ATTEMPT_ID}" \
--header "Content-Type: application/json" \
--data "${BODY}" \
"${XY_API_BASE}/dashboard-agent/proposals"
# HTTP 202 admits a durable run, not a dashboard. Retain the returned run_id.
# On ambiguous output, inspect existing work; do not automatically repeat this POST.# System key: explicitly delegated proposal capability and workflow access.
: "${XY_DASHBOARD_SYSTEM_KEY:?Use an authorized system key}"
: "${WORKFLOW_DEFINITION_ID:?Select the exact active workflow}"
: "${BASE_DASHBOARD_VERSION_ID:?Use the exact immutable dashboard version you reviewed}"
: "${PROPOSAL_ATTEMPT_ID:?Set a stable unique ID for this logical edit}"
BODY="$(jq -n --arg workflow_definition_id "${WORKFLOW_DEFINITION_ID}" \
--arg base_dashboard_version_id "${BASE_DASHBOARD_VERSION_ID}" \
'{prompt: "Revise the operations dashboard using only its registered governed query refs.", workflow_definition_id: $workflow_definition_id, base_dashboard_version_id: $base_dashboard_version_id}')" || exit 1
curl --fail-with-body --max-time 60 --request POST \
--header "Authorization: Bearer ${XY_DASHBOARD_SYSTEM_KEY}" \
--header "Idempotency-Key: ${PROPOSAL_ATTEMPT_ID}" \
--header "Content-Type: application/json" \
--data "${BODY}" \
"${XY_API_BASE}/dashboard-agent/proposals"
# Stop on a stale base or revoked authority. Re-read and review before a new edit.
# A system key cannot approve or publish the resulting dashboard.# Use the same authorized personal or system key as the proposal caller.
: "${XY_API_KEY:?Use an authorized proposal key}"
: "${RUN_ID:?Use the run_id returned by proposal start}"
curl --fail-with-body --max-time 30 \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/dashboard-agent/proposals/${RUN_ID}"# Read the existing run's bounded event page; do not start another proposal.
: "${XY_API_KEY:?Use an authorized proposal key}"
: "${RUN_ID:?Use the run_id returned by proposal start}"
curl --fail-with-body --max-time 30 \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/dashboard-agent/proposals/${RUN_ID}/events"
# For subsequent pages or SSE, use the shared progress cursor contract
# advertised by the live API reference. Keep the cursor with this run_id.# Explicit cancellation is a mutation, not a way to reconnect.
: "${XY_API_KEY:?Use an authorized proposal key}"
: "${RUN_ID:?Use the run_id of the proposal to cancel}"
curl --fail-with-body --max-time 30 --request POST \
--header "Authorization: Bearer ${XY_API_KEY}" \
"${XY_API_BASE}/dashboard-agent/proposals/${RUN_ID}/cancel"
# Read the same run afterward. A cancel request is not proof of cancellation
# and does not undo a committed dashboard version.HTTP 202 returns a run ID and selected workflow identity, plus a Locationheader for the REST status resource, not a fabricated dashboard or version ID. Follow durable progressfor reconnects. Terminal output may contain committed dashboard ID/slug, immutable version ID,DRAFTING or PREVIEW_READY, a validation summary, missing governed-query requirements, and review_version_id. A finished run is not a LIVE dashboard.
dashboard_proposal_start; MCP returns run_id immediately.dashboard_proposal_get and use bounded polling from the shared progress catalog. Inspect validation; submitted proposals stop at PREVIEW_READY.review_version_id to open the exact proposal in XY's dashboard review screen. An authorized human reviews and approves that immutable version in the web app and completes the publication flow.