POST /v1/platform/slides
Creates a slide-deck request. The AI generates a structured TipTap-based deck (with image placeholders that auto-resolve when the editor opens), uploads the content to S3, and returns edit and preview URLs.
The endpoint is fully self-contained: one POST returns everything an agent
needs to either hand the deck off to a human (previewUrl) or render a public
landing page (publicUrl). No follow-up calls are required for the happy path.
Agent Quick Reference
| You want… | Call this | Result |
|---|---|---|
| A finished deck right now | POST /slides with mode: "sync" | Response in 15–40 s with both URLs |
| To kick off generation in the background | POST /slides with mode: "async" | Returns immediately. Poll GET /slides/:id |
| Idempotent retry safety | Add Idempotency-Key header | Same key returns the original response |
| To edit the deck programmatically | Use editToken from the response | See Edit by Token |
| To export to PowerPoint | POST /slides/edit/:editToken/pptx | Returns S3 URL of the .pptx file |
| To regenerate a single image | POST /slides/edit/:editToken/generate/image | Returns S3 key for the new image |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Prompt describing the deck to generate. Specific prompts produce better decks (include audience, tone, key facts). |
title | string | No | Override the AI-generated title. |
description | string | No | Override the AI-generated description. |
language | string | No | Output language (default: en). Use ISO 639-1 codes such as en, ko, ja, zh-CN. |
slideCount | number | No | Target number of slides (1–100, default: 10). The AI may produce slightly fewer if the topic is narrow. |
theme | string | No | Visual theme hint (e.g. modern, minimal, dark). Affects styling tokens applied to the deck. |
tier | string | No | Accepted for backwards compatibility but has no effect on price (single-tier pricing post-v2). |
mode | string | No | sync (default) or async. See Async Mode. |
idempotencyKey | string | No | Prevents duplicate processing. Can also be sent as Idempotency-Key HTTP header. |
Pricing
The Slide API is billed per slide for the base creation flow. AI image generation is an optional sub-event billed independently when invoked via the image-generation endpoint — most slides are text-only, so customers pay only for what they actually generate.
| Event | Endpoint | Price |
|---|---|---|
| Base slide generation | POST /slides (per slide) | $0.03 / slide |
| AI image generation | POST /slides/edit/.../generate/image | $0.07 / image |
Slide content is generated as a single HTML blob, not N rows; the slideCount
parameter is the requested target and serves as the billable unit count for
the base creation event.
The priceSnapshot on the create response covers only the base creation.
Each call to the image-generation endpoint produces its own
PlatformUsageRecord row with a slide_image priceSnapshot.
Legacy
tierfield: still accepted but has no effect on price.
Example Request
curl -X POST https://api.tutorflow.io/v1/platform/slides \
-H "Authorization: Bearer tf_platform_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "Introduction to the Mediterranean diet for adults: foods, health benefits, and a sample weekly meal plan",
"language": "en",
"slideCount": 6
}'Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Slide request ID. Use this to poll, list, or refresh. |
slideId | string | null | Internal slide content ID. null until generation completes. |
status | string | PENDING, PROCESSING, COMPLETED, or FAILED. |
isTerminal | boolean | true once status is COMPLETED or FAILED. Stop polling. |
title | string | null | AI-generated or overridden title. |
description | string | null | Short summary of the deck. |
language | string | null | Output language (echoed). |
theme | string | null | Visual theme (echoed). |
slideCount | number | null | Number of slides actually produced. |
slug | string | null | URL-friendly slug. |
tier | string | Pricing tier used. |
mode | string | null | sync or async. |
priceSnapshot | object | null | Pricing details captured at request time. |
shareToken | string | null | Permanent token for the public read-only URL. |
editToken | string | null | Hourly-rotating token used by the editor and edit-time mutations. |
editTokenExpiresAt | string | null | ISO 8601 timestamp the current editToken expires at. Refresh by calling any GET /v1/platform/slides/edit/:editToken route — expiry is extended automatically (sliding window). |
previewUrl | string | null | Editor URL — /{locale}/platform/slides/edit/{editToken}. Anyone with the link can edit without logging in. |
publicUrl | string | null | Public read-only viewer — /{locale}/platform/slides/{shareToken}. Permanent. |
pollAfterMs | number | null | Suggested polling interval (async only). |
idempotencyKey | string | null | Echoed idempotency key. |
idempotentReplay | boolean | null | true if this is a replay, false for a fresh request, null when no key was supplied. |
createdAt | string | ISO 8601 timestamp. |
completedAt | string | null | ISO 8601 timestamp generation finished. |
Example Response
{
"id": "61f1b111-0df3-4de3-a83f-d4b10304949d",
"slideId": "669b977b-0007-40b9-8339-2b4e1d8e7c8e",
"status": "COMPLETED",
"isTerminal": true,
"title": "The Mediterranean Diet, Explained",
"description": "A 6-slide overview of the Mediterranean diet's foods, health benefits, and a one-week meal plan.",
"language": "en",
"theme": null,
"slideCount": 6,
"slug": "the-mediterranean-diet-explained-1602dc2c",
"tier": "default",
"mode": "sync",
"priceSnapshot": {
"category": "slide",
"catalogKey": "slide.default",
"tier": "default",
"unit": "slide",
"unitPrice": 0.03,
"units": 6,
"amountUsd": 0.18,
"currency": "USD",
"source": "platform_pricing_catalog_v2"
},
"shareToken": "fda025e5ea364bf30ea46671582d6859",
"editToken": "187d1d98e7cfe5190da4e2cc24bbf65d",
"editTokenExpiresAt": "2026-04-25T14:14:40.890Z",
"previewUrl": "https://tutorflow.io/en/platform/slides/edit/187d1d98e7cfe5190da4e2cc24bbf65d",
"publicUrl": "https://tutorflow.io/en/platform/slides/fda025e5ea364bf30ea46671582d6859",
"idempotencyKey": null,
"idempotentReplay": null,
"createdAt": "2026-04-25T04:14:15.756Z",
"completedAt": "2026-04-25T04:14:40.940Z",
"pollAfterMs": null
}Preview URL vs Public URL
| URL | Purpose | Auth | Expiry |
|---|---|---|---|
previewUrl | Editor — change title, content, outline, regenerate images, export PPTX | None (token-gated) | editToken extends on each access; effectively never expires while you keep using it |
publicUrl | Public read-only viewer | None | Permanent |
The previewUrl is the link you hand to a human reviewer or paste into a chat
reply. Opening it loads a full TipTap editor with image placeholders that
auto-fill when first viewed.
Image Generation in the Editor
AI-generated decks contain <img data-prompt="..." data-auto-image-id="...">
placeholders. When a human (or your own headless browser) opens the editor for
the first time, each placeholder triggers a call to:
POST /v1/platform/slides/edit/:editToken/generate/imageThe image generator calls Gemini, uploads the result to a workspace-scoped S3
path, and patches the <img> node in place. The editor handles this for
you — you do not need to call the endpoint manually unless you want to
regenerate a specific image.
To regenerate manually:
curl -X POST https://api.tutorflow.io/v1/platform/slides/edit/{editToken}/generate/image \
-H "Content-Type: application/json" \
-d '{
"content": "Olive oil, fresh vegetables, and whole grains arranged on a wooden board",
"aspectRatio": "3:4",
"imageStyle": "photorealistic"
}'| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes (or prompt) | Slide text content used to derive an image prompt automatically. |
prompt | string | No | Explicit prompt. Overrides content-based derivation. |
aspectRatio | string | No | 21:9, 3:4, 1:1, etc. Defaults based on layout. |
isInfographic | boolean | No | Generates an infographic-style image when true. |
themeName | string | No | Theme hint passed to the image prompt builder. |
imageStyle | string | No | default, photorealistic, minimalist, isometric. |
Response:
{ "data": [{ "key": "platform/{workspaceId}/slides/{slideId}/images/...png" }] }Use the returned key with the STORAGE URL prefix (see
Authentication) to
display the image.
PPTX Export
Convert any deck to a downloadable .pptx file:
curl -X POST https://api.tutorflow.io/v1/platform/slides/edit/{editToken}/pptx \
-H "Content-Type: application/json" \
-d '{
"html": "<slide>...</slide><slide>...</slide>",
"theme": { "backgroundColor": "#ffffff", "textColor": "#0f172a", "...": "..." }
}'The endpoint reuses the same PPTX builder as the classroom slide editor. The
html field is the rendered slide HTML — typically captured from the editor
DOM. Captured base64 images can also be supplied via capturedImages,
capturedMathImages, and capturedContentImages fields for high-fidelity
export of charts and math.
Response:
{
"url": "https://{bucket}.s3.{region}.amazonaws.com/platform/{workspaceId}/slides/{slideId}/pptx/1714060000.pptx",
"key": "platform/{workspaceId}/slides/{slideId}/pptx/1714060000.pptx"
}The URL is publicly readable. Stream it to the user or store the key for later retrieval.
Edit Token API
The edit URL provides access to these public endpoints (no API key needed):
| Method | Path | Description |
|---|---|---|
GET | /v1/platform/slides/edit/:editToken | Get slide content (TipTap JSON), title, outline, metadata. |
PATCH | /v1/platform/slides/edit/:editToken | Update title, description, outline, theme, content, thumbnail, visibility, metadata. |
POST | /v1/platform/slides/edit/:editToken/generate/image | Generate a single AI image. |
POST | /v1/platform/slides/edit/:editToken/pptx | Export the deck to PowerPoint. |
Update Slide
curl -X PATCH https://api.tutorflow.io/v1/platform/slides/edit/{editToken} \
-H "Content-Type: application/json" \
-d '{
"title": "The Mediterranean Diet — Updated",
"outline": "1. Foundations\n2. Daily Habits\n3. Weekly Plan"
}'| Field | Type | Description |
|---|---|---|
title | string | Updated deck title. |
description | string | null | Updated description. |
outline | string | null | Plain-text outline (markdown supported). |
theme | string | Visual theme. |
content | string | Full TipTap content (JSON string). Server uploads to S3 and bumps contentVersion. |
thumbnail | string | null | Thumbnail HTML snippet (rendered first slide). |
visibility | string | PUBLIC or PRIVATE. |
metadata | object | Free-form metadata. |
Response is the full SlideEditResDto — see Get Slide for the
shape.
Async Mode
Set mode: "async" to queue generation as a background job. The response
returns immediately with status: "PENDING" and a pollAfterMs value. Poll
GET /v1/platform/slides/:id until isTerminal is true.
Idempotency
Pass an idempotencyKey in the request body or Idempotency-Key header to
prevent duplicate slide generation. Reusing the same key returns the original
response with idempotentReplay: true.