---
title: "Bookmark a version as a stable one (or remove the bookmark)"
method: POST
path: "/builder/sessions/{sessionId}/turns/{turnId}/bookmark"
tags: ["Builder"]
---

# Bookmark a version as a stable one (or remove the bookmark)

`POST /builder/sessions/{sessionId}/turns/{turnId}/bookmark`

## Request

- Base URL: `https://hostingsurge.com/api/v1`
- URL: `https://hostingsurge.com/api/v1/builder/sessions/{sessionId}/turns/{turnId}/bookmark`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `__Host-hs_session`

## Path parameters

- `sessionId` string, uuid, required
- `turnId` string, uuid, required

## Request body

- object
  - `bookmarked` boolean, required

## Response `200`

The version

- object
  - `id` string, uuid, required
  - `seq` integer, required
  - `prompt` string, required
  - `status` 'queued' | 'running' | 'done' | 'failed' | 'canceled', required
  - `summary` string, nullable, required: The assistant's chat reply for this turn
  - `model` string, required: The model that produced the final attempt (the stronger model when the turn escalated)
  - `escalated` boolean, required: Whether the turn used the stronger (escalation) model
  - `escalation` 'auto' | 'user' | 'null', nullable, required
  - `attempt` integer, required: Model calls used, including auto-fix retries
  - `usage` object, required
    - `inputTokens` integer, required
    - `outputTokens` integer, required
    - `cachedInputTokens` integer, required: Input tokens served from the provider's prompt cache (included in inputTokens)
    - `costEstimateMicros` integer, required: Model cost in micro-USD (1,000,000 = $1): the tokens of every model call × the configured price of the model it used
    - `millicredits` integer, required: Build Credits × 1000 this turn costs (rounded up per turn)
    - `credits` number, required: Build Credits this turn costs (millicredits / 1000); metered, charged only once billing is enabled
    - `byModel` object[], required: Usage per model (the default and, after an escalation, the stronger model)
      - `model` string, required
      - `attempts` integer, required: Model calls made with this model
      - `inputTokens` integer, required
      - `outputTokens` integer, required
      - `cachedInputTokens` integer, required
      - `costMicros` integer, required: Model cost in micro-USD (1,000,000 = $1)
  - `filesChanged` integer, required
  - `changedFiles` object[]: Builder v2: the files this turn changed, so a client can rebuild the chat after a reload without fetching diffs
    - `path` string, required
    - `change` 'added' | 'modified' | 'deleted', required
  - `commitSha` string, nullable, required
  - `kind` 'chat' | 'manual' | 'restore' | 'code' | 'github' | 'merge' | 'start': What made the version: chat (default), manual (edits made in the preview), restore, code (code mode), github (from the linked GitHub branch), merge (a draft kept) or start
  - `editCount` integer, nullable: manual: the number of preview edits this version collects
  - `bookmarked` boolean: The customer bookmarked this version as a stable one
  - `error` object, nullable, required
    - `code` string, required
    - `message` string, required
  - `qa` object, nullable: The automatic browser test of the result of this turn (null: none ran; migration 197)
    - `status` 'passed' | 'fixed' | 'issues' | 'skipped', required: passed: nothing to fix; fixed: every problem found was fixed by the builder; issues: problems remain; skipped: no test ran
    - `reason` 'unavailable' | 'not_running' | 'no_browser' | 'failed' | 'time' | 'canceled' | 'null', nullable, required: skipped: why (the preview has no browser test on this server, the preview did not run, the test failed or ran out of time)
    - `pages` integer, required: Pages tested
    - `viewports` integer[], required: Widths tested, in CSS pixels (1280 = desktop, 390 = phone)
    - `clicks` integer, required: Buttons and links clicked
    - `forms` integer, required: Forms filled in and sent in a dry run (nothing leaves the preview)
    - `passes` integer, required: Browser tests run in this turn
    - `found` integer, required: Problems the first test found
    - `fixed` integer, required: Of those, fixed by the builder before the turn ended
    - `remaining` object[], required: Problems the last test still found (most important first)
      - `id` string, required: Stable id of the problem (the same problem keeps its id between tests)
      - `kind` 'exception' | 'console_error' | 'request_failed' | 'page_not_found' | 'broken_link' | 'broken_anchor' | 'control_error' | 'form_error' | 'a11y' | 'contrast' | 'visual' | 'page_error', required
      - `severity` 'error' | 'warning', required: error: the builder fixes it by itself; warning: shown, not fixed automatically
      - `page` string, nullable, required: The page (path) where it happened
      - `viewport` 'desktop' | 'mobile' | 'both' | 'null', nullable, required
      - `target` string, nullable, required: What: the label of a button, a link, a file or a rule
      - `detail` string, required: The exact message, in English (page text: untrusted)
      - `count` integer, required
    - `remainingCount` integer, required
    - `stopped` 'attempts' | 'no_progress' | 'time' | 'budget' | 'canceled' | 'failed' | 'null', nullable, required: Why the builder stopped fixing while problems remained: its attempts for this message were used up, a fix changed nothing, the time or the spending limit for automatic fixes was reached, the turn was stopped, or the fix or its test could not be finished (failed)
    - `visual` 'off' | 'done' | 'failed', required: The vision check of the screenshots (only where it is switched on)
    - `durationMs` integer, required
  - `createdAt` string, required: ISO-8601 timestamp
  - `startedAt` string, nullable, required: ISO-8601 timestamp
  - `finishedAt` string, nullable, required: ISO-8601 timestamp

## Other responses

- `400`: Invalid request (code invalid_request; details.reason: invalid, unmappable, not_found, secret_detected, unsupported, too_large, unreadable)
- `401`: Missing or invalid session/token (code unauthorized)
- `403`: Authenticated but not permitted (code forbidden)
- `404`: Not found or not visible to the caller (code not_found)
- `409`: Refused for now (code conflict; details.reason: turn_running while the AI changes the site, stale when the files changed since the editor loaded them (details.version: the current one), previewing_version, archived, in_use)
- `429`: Too many changes at once (code rate_limited); see Retry-After
- `501`: Not available on this server (code not_configured)

---

[API](https://skmtc.dev/hostingsurge/apis/hostingsurge-platform-api.md) · [All operations](https://skmtc.dev/hostingsurge/apis/hostingsurge-platform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/hostingsurge/apis/hostingsurge-platform-api/revisions/4d08337cdff1?raw)
