# Snapshots and reproducibility (/guides/snapshots-and-reproducibility)



A [snapshot](/concepts/snapshots) freezes the logical state a response ran against so you can reproduce
it later. It pins the branch head, the branch version, the ordered block manifest, and the
prompt-compiler revision. Paired with an immutable [alias release](/concepts/model-aliases), a snapshot
is what makes a Zumik response reproducible: you can point at exactly what it ran against, not an
ever-moving "latest".

## What a snapshot fixes [#what-a-snapshot-fixes]

A snapshot is a stable definition of the logical inputs to a generation. It pins:

| Pinned                         | Why it matters                                                                                              |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `branch_id` + `branch_version` | The exact head of the event line at capture time, so later appends do not change what you replay.           |
| `ordered_block_manifest`       | The order the logical blocks were assembled in, since reordering changes the prefix and the cache.          |
| `prompt_compiler_revision`     | The compiler that rendered the logical state, so a future compiler change cannot silently alter the prompt. |

A snapshot pins *logical* state. The physical model-visible rendering also depends on the resolved
model revision, tokenizer, and engine - those are pinned by the [alias release](/concepts/model-aliases)
and recorded on the response, not by the snapshot. A snapshot plus an alias release together fix the
full picture. See [handles and fingerprints](/concepts/handles-and-fingerprints) for how the three
identity layers separate.

## Pin a snapshot [#pin-a-snapshot]

`POST /v2/sessions/{session_id}/branches/{branch_id}/snapshots`. Both fields are optional;
`prompt_compiler_revision` defaults to `pc_1` and `ordered_block_manifest` defaults to empty. The
branch version is captured for you at creation time.

```bash
curl https://api.zumik.ai/v2/sessions/ses_01jy…/branches/br_01jy…/snapshots \
  -H "Authorization: Bearer zk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt_compiler_revision": "pc_11",
    "ordered_block_manifest": ["blk_policy", "blk_tools", "blk_schema", "evt_017"]
  }'
```

```json title="Response"
{
  "id": "snp_01jy…",
  "object": "snapshot",
  "session_id": "ses_01jy…",
  "branch_id": "br_01jy…",
  "branch_version": 17,
  "prompt_compiler_revision": "pc_11",
  "ordered_block_manifest": ["blk_policy", "blk_tools", "blk_schema", "evt_017"],
  "created_at": "2026-06-15T12:00:00Z"
}
```

Retrieve it later with `GET /v2/snapshots/{snapshot_id}`. The snapshot id resolves only within the
project that created it.

<Note>
  Snapshots are immutable. Appending more events to the branch after a snapshot does not change the
  snapshot - it still points at `branch_version: 17`. Capture a new snapshot to pin a later head.
</Note>

## A response pins one snapshot and one alias release [#a-response-pins-one-snapshot-and-one-alias-release]

Every response records exactly what it ran against: one snapshot for logical state and one alias
release for model resolution. That pairing is the reproducibility contract.

* The **snapshot** fixes the ordered logical inputs and the compiler revision.
* The **alias release** (`alr_…`) fixes the resolved provider, model, and model revision. A
  provider-model revision change creates a *new* release - a frozen alias never silently changes
  target. See [Model aliases](/concepts/model-aliases).

Both ids appear on the trace record, so you can always answer "what produced this output" without
guessing.

## Replay against a pinned snapshot and release [#replay-against-a-pinned-snapshot-and-release]

The replay system re-runs recorded traffic against a candidate configuration. Each trace can pin a
`snapshot_id` and an `alias_release_id`, so a replay reproduces the logical state and the model
resolution rather than whatever is current:

```json
{
  "trace_schema_version": "2026-06-01",
  "trace_id": "trc_…",
  "privacy_mode": "tokenized",
  "request": {
    "api_surface": "v1_responses",
    "snapshot_id": "snp_01jy…",
    "ordered_block_manifest": ["blk_policy", "blk_tools", "blk_schema", "evt_017"],
    "prompt_compiler_revision": "pc_11",
    "alias_release_id": "alr_2026_06_09_003"
  }
}
```

Feed that trace to [`POST /v2/replay-runs`](/guides/replay) to compare a baseline against a candidate
under the *same* pinned state - a clean apples-to-apples comparison, since neither the prompt nor the
resolved model can drift between runs. The signed report records the trace schema version, the alias
release, the resolved model revision, and the prompt-compiler revision, so the provenance of the
comparison is verifiable.

<CardGroup cols="2">
  <Card title="Replay" icon="repeat" href="/guides/replay">
    The trace envelope, replay classes, and the signed report.
  </Card>

  <Card title="Model aliases" icon="tag" href="/concepts/model-aliases">
    How a live alias resolves to one immutable release.
  </Card>
</CardGroup>
