SlideKiln
REST API

A PowerPoint-to-HTML API built for pipelines

POST a .pptx — or a URL to one — and get back a self-contained HTML5 package with animations, fonts and charts intact. Poll the job or receive an HMAC-signed webhook. No PowerPoint anywhere in the stack.

Quickstart

Create an API key in the dashboard, then three calls take a deck from .pptx to a downloadable web package:

API=https://slidekiln.com/backend

# 1. Create a job (multipart, files up to 32 MB)
JOB=$(curl -s -X POST $API/v1/jobs \
  -H "Authorization: Bearer sfk_YOUR_API_KEY" \
  -F file=@deck.pptx | jq -r .jobId)

# 2. Poll until the state is terminal
curl -s $API/v1/jobs/$JOB | jq .state
# queued → processing → uploading → succeeded

# 3. Download the self-contained HTML5 package
curl -L -o package.zip $API/v1/jobs/$JOB/download
# Already host the file? Point the API at it (https only):
curl -s -X POST $API/v1/jobs \
  -H "Authorization: Bearer sfk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceUrl": "https://your-host/deck.pptx",
    "callbackUrl": "https://your-app.example/webhooks/slidekiln",
    "options": { "externalId": "abc123" }
  }'

All creation paths return 202 with a jobId and statusUrl. The full request and response shapes and error codes are in the API reference.

Webhooks: skip the polling

In a pipeline you usually don't want a polling loop. Pass a callbackUrl when creating the job (as in the example above) and SlideKiln POSTs your endpoint exactly once per terminal state — succeeded, failed or expired — with the same JSON shape as the status endpoint, package URL included:

POST /webhooks/slidekiln HTTP/1.1
Content-Type: application/json
X-Ppt-Timestamp: 2026-06-15T10:00:02.500Z
X-Ppt-Signature: sha256=a4d29141ed7d02dd374675265128cced…

{
  "jobId": "8f3c1a2e-…",
  "status": "succeeded",
  "externalId": "abc123",
  "package": {
    "url": "<signed download url — fetch it directly>",
    "expiresAt": "2026-06-16T10:00:02.500Z",
    "sizeBytes": 859830
  },
  "stats": { "slides": 24, "durationMs": 1500, "warnings": [] }
}

Verifying the signature

Every delivery is HMAC-signed with your organization's webhook secret (whsk_… — reveal or rotate it on the API keys page). Compute HMAC-SHA256 over timestamp + "." + rawBody and compare in constant time:

import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const app = express();

// Use the RAW body — verify before JSON.parse.
app.post("/webhooks/slidekiln", express.raw({ type: "application/json" }), (req, res) => {
  const ts = req.header("X-Ppt-Timestamp") ?? "";
  const sig = req.header("X-Ppt-Signature") ?? "";
  const expected =
    "sha256=" +
    createHmac("sha256", process.env.SLIDEKILN_WEBHOOK_SECRET) // whsk_… from the dashboard
      .update(`${ts}.${req.body}`)
      .digest("hex");
  const fresh = Math.abs(Date.now() - Date.parse(ts)) < 5 * 60_000; // replay guard
  const valid =
    expected.length === sig.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
  if (!valid || !fresh) return res.sendStatus(401);

  const job = JSON.parse(req.body);
  if (job.status === "succeeded") {
    // job.package.url is a signed, directly-downloadable zip — valid until
    // job.package.expiresAt, no extra API call needed.
  }
  res.sendStatus(200); // non-2xx (or >10 s) is retried with backoff
});
  • Retries: a non-2xx response or a 10-second timeout is retried up to 5 times with exponential backoff (≈1 s, 2 s, 4 s, 8 s), re-signed with a fresh timestamp on every attempt.
  • Safety net: a job that somehow never finishes still delivers a final status: "expired" webhook when retention cleanup sweeps it, so your records never hang in “pending”.
  • Source of truth: callback delivery is tracked separately from job state — if your endpoint is down past the retries, the job is still there; GET /v1/jobs/:id always has the answer.

Full header reference and a worked signing example you can verify from a shell are in the API reference.

What the API gives you

Three ways to submit

Multipart file upload (≤ 32 MB), signed upload URLs for files up to 500 MB, or a sourceUrl the service fetches server-side.

HMAC-signed webhooks

Pass a callbackUrl and receive one signed POST when the job reaches a terminal state — succeeded, failed or expired — with the package URL inside.

Built for pipelines

Idempotency keys, externalId and metadata echoed back on every response, correlation IDs, and per-key usage attribution for teams.

Actionable results

Structured error codes (INVALID_PPTX, DOWNLOAD_FAILED, …) and per-job warnings such as font substitutions, plus slide count and timing stats.

Curious what the output looks like? See what survives PPTX-to-HTML5 conversion and how teams embed the result in their own sites.

Frequently asked questions

What does the API return when a conversion finishes?

A signed URL to a zip package containing index.html plus SVG, CSS, JS and media assets — a complete, self-contained web version of the deck. The status response also includes stats such as slide count, processing time and any warnings (for example FONT_SUBSTITUTED: Calibri → Carlito).

How do I convert large PowerPoint files?

Multipart uploads are capped at 32 MB. For anything bigger — up to 500 MB — POST /v1/uploads returns a signed URL; PUT the raw .pptx bytes to it, then create the job referencing the returned uploadKey.

Do I have to poll, or are there webhooks?

Both work. Poll GET /v1/jobs/:id until the state is terminal, or pass a callbackUrl when creating the job and SlideKiln sends one HMAC-signed POST when the job succeeds, fails or expires — the same JSON shape as the status endpoint.

How fast are conversions?

Typical conversions finish in seconds — a 100-slide deck takes about a second of processing time. States progress queued → processing → uploading → succeeded.

How long are converted packages kept?

Uploads and outputs are stored privately with time-limited signed download URLs and deleted after the retention window (24 hours by default), so download the package promptly or fetch it from your webhook handler.

How is API usage priced?

The free plan includes 2 conversions a month. Pro is $29/month with 35 conversions included ($1.00 each after), Scale is $149/month with 350 included ($0.55 after). Every plan uses the identical rendering engine, and API keys support per-key usage attribution.

From .pptx to the web in one POST.

Sign up, create an API key and convert your first deck free — 2 conversions a month included, no credit card.

Start converting free