@bisquecloud/sdk is the TypeScript client for the Bisque API: publish a
presentation from a script, a CI job, or a server, and read any shared one back
as text. It has zero dependencies and runs anywhere fetch does: Node 18 and
later, Bun, Deno, and browsers. The current version is 0.2.0, under the MIT
license.
npm install @bisquecloud/sdk
Read a presentation, no sign-in
import { Bisque } from "@bisquecloud/sdk";
const bisque = new Bisque();
const ctx = await bisque.presentations.context({
url: "https://bisque.today/p/siderakis/claude-system-card",
});
console.log(ctx.title);
for (const slide of ctx.transcript) console.log(slide.startSec, slide.text);
Public and unlisted presentations need no credential. ctx.contextMd carries
the presentation's context.md when it has one.
Publish
Publishing needs a credential: an API key from
bisque.cloud/setup/keys, or an OAuth 2.1
token for a hosted agent. auth.md walks through
both. Pass the key as apiKey when you construct the client; it is sent as a
bearer token on every call that needs one.
const bisque = new Bisque({ apiKey: process.env.BISQUE_API_KEY });
// 1. Read the authoring format. Never write html-presentation/v1 from memory.
const spec = await bisque.presentations.spec();
// 2. Write index.html to that spec (your agent does this), then publish.
const created = await bisque.presentations.create({
indexHtml,
slug: "release-notes-2026-08",
visibility: "unlisted",
});
// 3. Narration finishes in the background; wait for it.
const status = await bisque.presentations.waitUntilReady(
created.presentationId,
);
console.log(status.webUrl); // https://bisque.today/p/<handle>/release-notes-2026-08
create sends an Idempotency-Key on every call (a UUID unless you pass
{ idempotencyKey }), so a retried request replays the first result instead
of publishing twice.
The calls
| Call | What it does |
|---|---|
presentations.spec(part?) | The authoring format, or one module of it (charts, macos, and so on), as markdown. |
presentations.context(ref) | Metadata, per-slide transcript, and context.md for any shared presentation. |
presentations.list(params?) / listAll() | Your presentations, one page or every page. |
presentations.create(req) | Create from HTML and have the server narrate it. |
presentations.status(id) / waitUntilReady(id) | Narration and publish progress. |
presentations.startCreation(req) | Create from HTML and narrate it yourself. The slides are viewable immediately, silent. |
presentations.addSlideAudio(id, slideKey, audio) | Give one slide its narration; anyone watching sees it appear. |
presentations.finalize(id) | Finish the creation and claim its version. |
presentations.publishNarrated(req) | Deprecated. One call, but nothing is viewable until every slide is narrated. |
oembed(url) | The oEmbed payload for a watch URL. |
ask(query) | A natural-language question over the docs and public presentations. |
openapi() | The OpenAPI 3.1 document this SDK wraps. |
Every method takes an optional { signal } for cancellation. The client talks
to https://bisque.today unless you pass baseUrl.
Errors and rate limits
Failures throw BisqueError with status, the API's stable code
(NOT_FOUND, CONTEXT_RATE_LIMITED, and so on), and a requestId to quote
in a bug report. A 429 or 5xx is retried twice, honouring Retry-After;
maxRetries changes that. The rate-limit headers from the latest response are
on bisque.lastRateLimit.
Reference
- The REST API it wraps: API, and the OpenAPI document at bisque.today/openapi.json
- Credentials: bisque.today/auth.md
- The same tools over MCP: MCP
- Source: github.com/bisque-cloud/presenter/tree/main/sdk