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.

This is the public viewer endpoint. In normal use, you don’t call it directly — you share the dashboard URL (https://app.slideless.ai/share/{presentationId}?token=...) and the viewer loads this endpoint in an iframe. Read this page only if you’re embedding a deck in a custom iframe or writing an integration.

URL structure

Two forms are supported:

Path-token form (preferred for iframes)

GET https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/{presentationId}/_t/{token}/{assetPath}
assetPath empty (or equal to manifest.entryPath) → serves the entry HTML. assetPath non-empty → serves that asset from the current version’s manifest. The path-token shape matters: when the entry HTML contains <img src="./hero.jpg">, the browser resolves it relative to the iframe’s URL. With the token in the path, the relative request naturally includes it too — so assets resolve without any cookie or URL-rewriting magic.

Flat form (curl-friendly)

GET https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/{presentationId}?token={token}
GET https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/{presentationId}/{assetPath}?token={token}
Same behavior; token in query string instead of path. Use this form for command-line testing.

Auth

No header required. Access is gated by the token — either in the URL path (/_t/{token}/) or in the ?token= query string. The token is 384 bits of entropy (48 random bytes, base64url-encoded), validated with constant-time comparison.

Version resolution

The served version is determined strictly by the token’s versionMode:
  • { type: 'latest' } → the presentation’s currentVersion
  • { type: 'pinned', version: N } → exactly version N
There is no ?v= query parameter to override this. Recipients cannot see any version other than what their token resolves to. Only the owner, via listPresentationVersions / getPresentationVersion, can browse history.

Response

Success (200)

Content-Type is taken from the manifest (text/html; charset=utf-8 for the entry, or whatever the asset declared — image/jpeg, video/mp4, model/gltf-binary, …). Headers:
HeaderValue
Content-TypeAs declared in the manifest
Content-LengthFor non-range responses
ETag"<sha256-of-the-blob>" — enables client caching
Cache-Controlprivate, max-age=300, must-revalidate
Accept-Rangesbytes
X-Content-Type-Optionsnosniff
Content-Security-PolicySet on entry HTML responses (sandbox + same-origin assets + https: CDNs allowed)
Side effects on entry HTML requests (fire-and-forget, not awaited):
  • Increments the matched token’s accessCount
  • Updates the token’s lastAccessedAt
  • Increments the presentation’s totalViews
  • Updates the presentation’s lastViewedAt
Asset requests do NOT increment view counts (prevents over-counting: a deck with 20 assets would bump views by 21 otherwise).

Range (206)

Range: bytes=<start>-<end> is honored on asset responses:
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/824100
Content-Length: 1024
Content-Type: image/jpeg
Accept-Ranges: bytes
Each Range response is capped at 50 MB per request to prevent memory spikes. Video players will automatically send multiple Range requests to stream long files.

ETag (304)

If-None-Match: "<sha256>" triggers a 304 Not Modified response when the asset matches. Browsers cache aggressively across loads.

Errors

StatusCodeWhen
400invalid-argumentMissing presentationId in path
403revokedThe token has been revoked
404not-foundGeneric — also returned for “share doesn’t exist”, “token wrong”, “asset not in manifest”, or “no token provided” (intentionally indistinct to prevent share-ID enumeration)
405method-not-allowedUsed POST/PUT/etc. (only GET + HEAD accepted)
410expiredPresentation has an expiration set and it’s past
416range-not-satisfiableRange header requested a byte range outside the blob
500internalBackend error
Error responses for the entry HTML path are served as minimal HTML (for iframe rendering). Asset paths return plain text.

Examples

Fetch entry HTML (curl)

curl -i "https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/0192f1c3-.../_t/AbCdEf.../"

Fetch an asset (curl)

curl -i "https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/0192f1c3-.../_t/AbCdEf.../images/hero.jpg"

Range request for video (curl)

curl -H "Range: bytes=0-1023" -i \
  "https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/0192f1c3-.../_t/AbCdEf.../video/demo.mp4"

Embedding pattern

If you’re building a custom viewer and want to load the deck into an iframe yourself:
<iframe
  src="https://europe-west1-slideless-ai.cloudfunctions.net/getSharedPresentation/0192f1c3-.../_t/AbCdEf.../"
  sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
  width="100%"
  height="600"
></iframe>
For most use cases, don’t do this — share the Slideless viewer URL instead. It includes fullscreen, download, and the “Made with Slideless” footer.

Why the 404s look the same

To prevent attackers from probing share IDs, Slideless returns a generic 404 not-found for any of:
  • The share ID doesn’t exist
  • The share ID exists but the token is wrong
  • No token was provided
  • An asset path doesn’t resolve in the current manifest
There’s no way to tell from the response which of these happened. This is intentional.

Next