---
title: "Prepare an episode's table read (metered — TWO CALLS, price first)."
method: POST
path: "/scripts/{scriptUploadId}/table-read/prepare"
tags: ["Scripts"]
---

# Prepare an episode's table read (metered — TWO CALLS, price first).

`POST /scripts/{scriptUploadId}/table-read/prepare`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/scripts/{scriptUploadId}/table-read/prepare`
- Auth: HTTP bearer

Prepares the read of an episode whose cast is complete (Casting in the Writers Lab, or POST /story-projects/{projectId}/cast): charges its table-read credit — `TABLE_READ_SHORT` up to 60 pages, `TABLE_READ_LONG` above, the same price a job that adopts the screenplay reserves (GET /credits lists both) — and adds the read's scene sound effects and music, the media a Story API read gets. The Writers Lab's "Continue to Table Read" on Casting is the same operation, through the same server core. TWO CALLS: sent without `confirmed` it PRICES the preparation and changes nothing (`status: quoted`, `quotedOnly: true`, `credits`, `balanceAfter`, `sufficient`, and a `note` to show the writer; when `sufficient` is false the caller must not confirm); sent again with `confirmed: true` it charges and queues the media (`status: queued`). ONCE PER SCREENPLAY VERSION: a version already charged for its read, on any surface, answers `status: prepared`, and a read whose media is being made answers `preparing` — both free — so a retry never charges twice. A confirmed call the balance cannot cover is refused with 402 `insufficient_credits` and changes nothing. Refused before the price: 404 `script_not_found` (not the caller's screenplay), 409 `cast_precondition_failed` (`details.reason`: `script_not_finalized` for a draft, `screenplay_coverage_before_cast` when the version's screenplay coverage is under the bar and not acknowledged, still scoring, or missing, `script_not_cast_ready` for a cast that is not complete, with `details.missing`), 409 `project_precondition_failed` (the project gate is closed) and 409 `voice_retired` (a voice the read would speak was retired).  Requires `Idempotency-Key`. Scope: `artifact:publish`.

## Headers

- `Idempotency-Key` string, required

## Request body

- PrepareTableReadRequest
  - `confirmed` boolean: Omit (or false) to get the price without spending anything. true charges the read once for this screenplay version and adds its sound effects and music — only after the writer has seen the price.

## Response `200`

The price (no `confirmed`), the queued preparation, or prepared / preparing.

- TableReadPrepareEnvelope
  - `status` 'quoted' | 'queued' | 'prepared' | 'preparing', required: `quoted`: priced, nothing spent. `queued`: charged (or an earlier attempt's charge used) and its media queued. `prepared`: already prepared for this screenplay version, on any surface — nothing charged. `preparing`: its media is being made now — nothing charged.
  - `quotedOnly` boolean, required: true when this call only PRICED the preparation.
  - `scriptUploadId` string, required
  - `tableReadJobId` string, nullable, required: The read it prepares. Null on a quote for a version that has no read yet; the confirmed call makes one.
  - `opKey` 'TABLE_READ_SHORT' | 'TABLE_READ_LONG', required: The table-read price it is charged at, by the screenplay's pages (GET /credits lists both).
  - `label` string, required: The price list's name for it.
  - `pageUsage` integer, required: The pages it is priced by (0 when the screenplay's length was never measured).
  - `credits` integer, required: The price (quote), what was charged (queued), or 0.
  - `charged` boolean: Not on a quote. true when this call charged the credit; false when nothing was charged.
  - `costLabel` string, nullable: Quote only.
  - `balanceAfter` integer: Quote only. Studio Credits the writer would have left once this is spent: the balance available now (as Billing shows it) minus the price. Negative means they are that many short.
  - `sufficient` boolean: Quote only. True when the available balance covers the price. False means the writer must top up first (the note names the Billing link) — do not confirm; the reserve would refuse.
  - `note` string, required: The price and what it would leave (quote), or what happened. Show it to the writer.

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `402`: Not enough Studio Credits to reserve this job (`insufficient_credits`), and nothing was reserved. `details` is InsufficientCreditsDetails: `required` and `available`, and `autoTopup` when auto top-up is on but did not cover it — `pending` (retry in a moment), `over_cap` (raise the monthly spending cap), `too_small` (buy a bigger pack) or `failed`.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: The named project or source does not exist or is owned by another account.
- `409`: Casting cannot run on this project yet, and nothing was quoted or drawn: nobody castable, nobody with a description to draw from, no passing screenplay coverage, or no image model bound (`cast_precondition_failed`, `details.reason` names which) — or the project itself has not reached casting (`project_precondition_failed`, `details.stage` names the stage to finish). The message says what to do.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/64c1102ccb2f?raw)
