POST /v1/platform/slides
Creates a slide-deck request. The AI generates a structured deck, uploads the
content to S3, and returns edit and preview URLs. Generated decks currently
start as <slide>...</slide> HTML blocks. The editor may later save the same
field as its current editor-content string, so treat content as an opaque
editor payload unless you are using TutorFlow's editor contract directly.
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 /v1/platform/slides with mode: "sync" | Response in 15–40 s with both URLs |
| To kick off generation in the background | POST /v1/platform/slides with mode: "async" | Returns immediately. Poll GET /v1/platform/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 /v1/platform/slides/edit/:editToken/pptx | Returns S3 URL of the .pptx file |
| To regenerate a single image | POST /v1/platform/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. |
classroomId | string | No | Classroom UUID to create the slide deck in. Uses the workspace default classroom if omitted. |
tier | string | No | Legacy request value only: basic, standard, or advanced. Omit for new integrations. Do not send default; default is a response/catalog tier. |
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 /v1/platform/slides (per slide) | $0.03 / slide |
| AI image generation | POST /v1/platform/slides/edit/:editToken/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 for backwards compatibility but has no effect on price. Omit it in new integrations. If you send it, use one of the legacy request values:basic,standard, oradvanced.
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 | Requested target slide count. This is also the billable unit count for base slide generation. |
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 the slide editor. If the deck contains image
placeholders, the editor can resolve them through the image-generation endpoint.
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 string, 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 editor content string. Generated decks start as <slide>...</slide> HTML, while editor saves may store TipTap JSON. The server treats it as an opaque string, uploads it 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.