---
id: preview-mode
title: 'Preview Mode'
description: 'Use ?previewOnly=true to stop a Bulk API request after the projection phase and inspect exactly what it would do.'
sidebar_label: 'Preview Mode'
sidebar_position: 10
tags: ['api', 'bulk', 'asset', 'preview']
---

Adding `?previewOnly=true` stops the request after the
[projection phase](/apis/asset-bulk/aliases-and-projection). Nothing is
written. The response is the projection itself — the flat, aliased list of
commands the server would have executed.

```text
POST ~/api/asset?previewOnly=true
```

## What You Get Back

The response is **not** the usual envelope. It is a JSON array of aliased
commands.

```json title="Request"
{
  "AssetType": "Story",
  "Scope": "Scope:0",
  "Name": "Story Time",
  "Children": [{ "AssetType": "Test", "Name": "Test Time" }]
}
```

```json title="Response"
[
  {
    "@ffa2169f-bd6b-44d2-9cca-05c37a725dfd$auto-aliased": {
      "AssetType": "Story",
      "Scope": "Scope:0",
      "Name": "Story Time"
    }
  },
  {
    "@31e11be0-525c-4ee1-9002-4e6ee4453641$auto-aliased": {
      "AssetType": "Test",
      "Name": "Test Time",
      "#ContextOid": "@ffa2169f-bd6b-44d2-9cca-05c37a725dfd$auto-aliased"
    }
  }
]
```

## The Projection Round-Trips

The projection is itself a valid request body. Posting it back to `api/asset`
without `?previewOnly=true` produces the same result as posting your original
payload would have.

This works because the projected content is already user-aliased from the
server's point of view, so the second request forwards each command through to
processing essentially unchanged.

That makes preview mode a **review gate** for risky bulk edits:

1. Post the request with `?previewOnly=true`.
2. Read the projection and confirm it targets what you expect.
3. Delete or edit any commands you do not want.
4. Post the edited array back to `api/asset`.

:::tip This is the answer to unbounded query-scoped updates
A command like `{ "from": "Story", "update": { "Status": "Done" } }` matches
every Story you can see. Previewing it expands the query into one explicit
per-asset command, so you can count them, inspect them, and trim the list
before committing. See
[Updating assets](/apis/asset-bulk/updating-assets#updating-everything-that-matches-a-query).

Preview is what lets you confirm the blast radius before it commits: the
expanded list is either the size you expected or it is thousands of rows, and
you see which before anything is written. The same applies to a mass `execute`
of `Close` or `Delete` — and unlike updates, those
[cannot carry a `ChangeComment`](/apis/asset-bulk/request-format#processing-model-large-batches-run-to-completion),
so previewing is the guard rail you have before the fact.
:::

## What the Projection Reveals

Preview is the only way to observe several behaviors directly:

| Behavior | What you see |
| --- | --- |
| [Auto-aliasing](/apis/asset-bulk/aliases-and-projection#auto-aliasing) | GUID aliases, with `$auto-aliased` on create commands |
| [Hierarchy flattening](/apis/asset-bulk/aliases-and-projection#request-hierarchy-flattening) | Nested creates become siblings linked by `#ContextOid` |
| [Update normalisation](/apis/asset-bulk/updating-assets#scalar-and-single-value-relation-attributes) | Every attribute update becomes `{ "add": [...], "remove": [] }` |
| [Query expansion](/apis/asset-bulk/updating-assets#updating-everything-that-matches-a-query) | A `where`-scoped update becomes one command per matched asset, each with a literal `oid` |
| [Deferral](/apis/asset-bulk/aliases-and-projection#deferring-commands-until-the-processing-phase) | User-aliased commands stay unflattened and unexpanded |
| [Find and replace](/apis/asset-bulk/find-and-replace#on-multi-value-relations) | `find` / `replace` is preserved rather than resolved to `add` / `remove` |

## Caveats

:::warning Preview mode is not covered by tests
The `previewOnly` parameter, the response shape and the resubmit round trip
described on this page are not asserted by any executable test. Treat the exact
projection formats as illustrative, and verify behaviour against your instance
before relying on preview ahead of a destructive change.
:::

Preview evaluates queries in order to expand them, so a preview of a
query-scoped update still costs a read against the database. It is not free,
just non-destructive.

## Related Topics

- [Projection, processing and aliases](/apis/asset-bulk/aliases-and-projection)
- [Updating assets](/apis/asset-bulk/updating-assets)
- [Errors and partial failures](/apis/asset-bulk/errors)
