---
title: "Publish a presentation from HTML"
method: POST
path: "/api/v1/presentations/create"
tags: ["Publish"]
---

# Publish a presentation from HTML

`POST /api/v1/presentations/create`

## Request

- Base URL: `https://bisque.today`
- URL: `https://bisque.today/api/v1/presentations/create`
- Auth: HTTP bearer

Versioned address of `/api/presentations/create` — the same endpoint and contract. Send the `html-presentation/v1` file. Bisque parses it, narrates each slide server-side, and publishes to `webUrl`. The reply is immediate (202); narration finishes in the background — poll `statusUrl`. Re-send with the same `slug` to republish. With `dryRun: true` the same checks run and nothing is written: the reply is a 200 with status `validated` (the sandbox).

## Headers

- `Idempotency-Key` string

## Request body

- CreatePresentationRequest
  - `indexHtml` string, required: The whole `html-presentation/v1` file.
  - `title` string: Overrides the title parsed from the HTML.
  - `slug` string: URL slug under your channel. Reuse it to republish.
  - `presentationId` string: Republish an existing presentation by id instead of slug.
  - `handle` string: Which of your channels to publish on; defaults to your account's.
  - `dryRun` boolean: Sandbox: run every check a real create runs (format, slug and org conflicts, tier cap, credit preflight) and write nothing. The reply is a 200 with status `validated`.
  - `visibility` 'public' | 'unlisted' | 'private' | 'org': Who can open the watch link. Unlisted unless you say otherwise; `org` publishes into the bisque.team organization named by `org`.
  - `org` string: bisque.team organization slug; publishes into the company space.
  - `contextMd` string: Agent-facing background shipped with the bundle as `context.md`.
  - `designMd` string: Design notes shipped with the bundle.
  - `voiceId` string: Narration voice tag; defaults to the account's saved voice.
  - `madeWith` string[]: Models credited with making this presentation — e.g. ["Claude Opus 5", "Kokoro af_heart"]. Free text, shown to viewers in the watch page's ⋯ menu. Omit to keep what a previous publish recorded; send `[]` to clear it.
  - `description` string: Plain-text description shown under the player on the watch page, with web URLs linked — a summary, then the sources. Omit to keep what a previous publish sent; send an empty string to clear it.
  - `speechSpeed` number: Narration speed. 1.0 is normal.
  - `voiceStability` number: How closely the voice keeps to its reference, for voices that take it. Omit for the voice's default.
  - `assets` object: Page assets (fonts, images) keyed by bundle path.

## Response `200`

Dry run passed (`dryRun: true`): every check succeeded and nothing was published. `webUrl` is where a real create would publish.

- CreatePresentationResult
  - `requestId` string, required
  - `presentationId` string, required
  - `webUrl` string, uri, required: The watch link, e.g. https://bisque.today/p/{handle}/{slug}.
  - `status` 'ready' | 'queued' | 'validated', required: `queued`: narration running, poll `statusUrl`. `ready`: live now. `validated`: a dry run passed and nothing was written.
  - `statusUrl` string, uri, required
  - `narration` object
  - `dryRun` boolean: Present and true only on a dry-run reply.

## Other responses

- `202`: Accepted; narration queued.
- `400`: The HTML or a field failed validation; `error.message` names the problem.
- `401`: Missing or invalid credential.
- `409`: A create for the same presentation is still running (`CREATE_IN_PROGRESS`).

---

[API](https://skmtc.dev/bisque/apis/bisque-api.md) · [All operations](https://skmtc.dev/bisque/apis/bisque-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bisque/apis/bisque-api/revisions/5dd7a3c62937?raw)
