Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.slideless.ai/llms.txt

Use this file to discover all available pages before exploring further.

When to use this

You want to upload a presentation from somewhere other than the dashboard or a Claude session. For example:
  • A CI/CD job that publishes a release deck on every tag
  • A cron task that re-publishes a daily metrics deck
  • Any custom integration
The interactive path is documented in Use Slideless with Claude. This guide is the script-friendly path.

Prerequisites

  • Node.js 20+ on the runner
  • An organization API key with presentations:write (see API keys)
  • The deck you want to share — a folder or a single .html file, within your plan’s size caps (see Presentations)

Install the CLI on the runner

npm install -g slideless
If global install is awkward in your CI environment, use npx slideless ... instead.

Authenticate non-interactively

Pass the key through an environment variable. The CLI reads SLIDELESS_API_KEY automatically when no profile is configured:
export SLIDELESS_API_KEY="cko_your_key_here"
Or save it to a profile in non-interactive mode:
slideless login --api-key "$SLIDELESS_API_KEY" --name ci

Push a folder

slideless push ./my-deck --title "Q4 review" --json
Output:
{
  "success": true,
  "data": {
    "presentationId": "0192f1c3-...",
    "version": 1,
    "role": "owner",
    "isNew": true,
    "assetsUploaded": 5,
    "assetsDeduped": 0,
    "totalBytes": 194000
  }
}
Extract the presentationId:
SHARE_ID=$(slideless push ./my-deck --title "Q4 review" --json | jq -r '.data.presentationId')
echo "Pushed: $SHARE_ID"
A fresh push creates the deck but does not publish it — viewer URLs only exist once you mint a token:
SHARE_URL=$(slideless share "$SHARE_ID" --json | jq -r '.data.shareUrl')
echo "Published: $SHARE_URL"
The exit code is non-zero on failure, so the CI step fails fast if the upload errors.

Single-file alternative

slideless push ./deck.html --title "Q4 review" --json
Both forms work interchangeably — same endpoints, same response shape.

Strict mode for CI

In CI you probably want unresolved references (typos, missing files) to fail the build rather than just warn:
slideless push ./my-deck --title "Q4 review" --strict --json

Re-publish to the same URL

Ensure the deck folder still contains its slideless.json (written by the first push), then:
slideless push ./my-deck --json
The CLI reads slideless.json, sends expectedBaseVersion, and commits a new version to the same presentationId. Only changed files re-upload — the rest are deduplicated. If the CI runner doesn’t persist the working copy between runs (fresh clone each time), you have two choices:
  1. Commit slideless.json into your repo so it comes back with each clone.
  2. Save the presentationId in a CI secret and slideless pull "$SHARE_ID" ./my-deck before running your build step, then slideless push ./my-deck --json.
If another push landed between your last pull and this one, you’ll get 409 conflict. Pull and retry, or pass --force to overwrite.

The upload protocol under the hood

slideless push is a thin wrapper around three HTTP endpoints:
  1. POST /precheckAssets — send a list of SHA-256 hashes, get back which ones the backend doesn’t have.
  2. POST /uploadPresentationAsset — multipart upload of one missing blob (content-addressed; server re-verifies the hash).
  3. POST /commitPresentationVersion — send the manifest (list of {path, sha256, size, contentType}), backend validates every hash exists, writes the manifest, bumps currentVersion.
The CLI runs them in order and prints progress between each step. See the linked API reference pages for the request/response shapes if you ever need to implement the flow in a language that isn’t Node.

Errors

The CLI maps backend HTTP statuses to short codes. Common ones:
codeMeaning
unauthenticatedMissing or invalid key
permission-deniedKey valid but missing presentations:write
payload-too-largeFile or total deck exceeds the plan cap
too-many-filesManifest exceeds the plan’s max file count
blobs-missingManifest references a hash not in the store (shouldn’t happen in normal use — indicates the CLI or a custom client skipped a step)
invalid-argumentBad path, bad sha256 format, etc.
internalBackend 5xx; retry with backoff
Static-scan errors emit invalid-argument with a details list of the offending references (e.g. ../outside/foo.jpg).

Two URLs, two purposes

Don’t confuse the share URL (what you give recipients) with the direct deck URL (what the dashboard iframe embeds):
PurposeURLWhen
Share URL for recipientshttps://app.slideless.ai/share/{presentationId}?token={token}What slideless share <id> returns. Send this to people.
Direct deck URL for iframinghttps://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/{presentationId}/_t/{token}/Loads the entry HTML; relative asset paths (./foo.jpg) resolve natively.
The share URL wraps the direct deck URL in chrome (download button, fullscreen toggle, footer). The direct URL is the raw rendering surface.

Next