---
title: "Preview Chat Action"
method: GET
path: "/api/v1/chat-actions/{action_id}/preview"
tags: ["chat-actions"]
---

# Preview Chat Action

`GET /api/v1/chat-actions/{action_id}/preview`

Ghost-preview read (spec §6): the CURRENT plan for a pending
place_measurements row. Auth is decide-aligned per the user ruling on
2026-08-16: only users who could decide the card may see its ghosts. This
calls the resolver BELOW the decide-time recheck: live_action_state treats
an empty plan as ConflictError and the decide routes flip such rows failed,
but here empty is data — the ghost layer draws nothing, and the user learns
the real reason at decide time. A plan that no longer resolves at all
(source/target deleted), or whose stored request is malformed/stale,
previews as empty for the same reason.

## Path parameters

- `action_id` string, uuid, required

## Response `200`

Successful Response

- ActionPreviewResponse — GET /chat-actions/{id}/preview — the CURRENT plan for a pending place_measurements row (spec §6), recomputed on every call; nothing is persisted and the row is never transitioned. Empty placements covers BOTH "everything already placed" and "the plan no longer resolves" (source or target condition deleted): the ghost layer draws nothing either way, and flipping the row failed stays the decide routes' job. already_placed distinguishes the two for tests and observability (covered-empty > 0, broken-empty == 0). Both fields are required — no defaults — so the generated TS properties are non-optional.
  - `placements` ActionPreviewPlacement[], required
    - `page_id` string, uuid, required
    - `x` number, required
    - `y` number, required
  - `already_placed` integer, required

## Other responses

- `4XX` — Client Error
- `5XX` — Server Error

---

[API](https://skmtc.dev/rudus/apis/rudus-api.md) · [All operations](https://skmtc.dev/rudus/apis/rudus-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/rudus/rudus-api/revisions/f149124a4d80/schema)
