Skip to content

Public API

The Deckyard Public API lets you programmatically create, manage, and export presentations. Use it to build integrations, automate workflows, or connect Deckyard with other tools.

Go to Settings > API Keys and create a new key. Choose the scopes you need:

ScopeAccess
readList and view presentations, themes, slide types
writeCreate, update, and delete presentations
exportExport presentations to various formats
aiUse AI-powered generation features, including translation
comments:readRead comments on presentations the key owner can access
comments:writeCreate comments and replies, and change a comment’s status

A new key without explicit scopes gets read and write. The comment scopes are separate from read and write: a key needs them explicitly to touch comments.

Important: Copy your API key immediately - it won’t be shown again.

Terminal window
curl https://your-instance.com/api/v1/presentations \
-H "Authorization: Bearer dk_live_your_api_key_here"

Interactive API documentation is available at /api/v1/docs on your Deckyard instance.

A few endpoints need no API key: GET /api/v1 (the API’s name, version and main entry points), /api/v1/docs, /api/v1/openapi.yaml and the JSON Schema endpoints.

All requests require an API key in the Authorization header:

Authorization: Bearer dk_live_your_api_key_here

API keys use the format dk_live_ followed by a random token. The dk_live_ prefix indicates a production key.

  • Keys are stored securely (only the hash is saved)
  • Revoke keys instantly from Settings if compromised
  • Keys inherit the permissions of the user who created them

Each API key has one rate-limit class, stored on the key. These classes are not plans: every new key gets free, and an admin who needs a higher limit changes the key’s tier column in the database.

ClassRequests/minAI calls/dayExports/day
free601050
pro300100500
enterprise1000UnlimitedUnlimited

Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. They describe daily usage and reset at midnight UTC, not the per-minute bucket:

X-RateLimit-Limit: 86400
X-RateLimit-Remaining: 86155
X-RateLimit-Reset: 1706140800

Exceeding the per-minute limit returns 429 with Retry-After: 60; exceeding a daily limit returns 429 with the X-RateLimit-* headers. Details in Rate Limiting.

GET /api/v1/presentations

Query parameters:

  • limit (1-100, default 50) - Maximum results
  • offset (default 0) - Skip this many results
Terminal window
curl "https://your-instance.com/api/v1/presentations?limit=10" \
-H "Authorization: Bearer $API_KEY"
GET /api/v1/presentations/{id}

Returns the full presentation including all slides.

POST /api/v1/presentations
Content-Type: application/json
{
"title": "My Presentation",
"description": "Optional description",
"theme": "deckyard",
"language": "en-GB"
}
PUT /api/v1/presentations/{id}
Content-Type: application/json
{
"title": "Updated Title",
"slides": [...]
}
DELETE /api/v1/presentations/{id}

Moves the presentation to trash (soft delete).

POST /api/v1/presentations/{id}/duplicate

Creates a copy of the presentation.

Work on single slides without sending the whole presentation back. Reads need read, changes need write. A slide type may be given in any accepted spelling (title-slide, core/title-slide, eu.deckyard.slide.title); the stored type is always the registry key.

GET /api/v1/presentations/{id}/slides/{slideId}

Returns the slide and its index in the presentation.

POST /api/v1/presentations/{id}/slides
Content-Type: application/json
{
"type": "title-slide",
"content": { },
"notes": "Optional speaker notes",
"atIndex": 0
}

type is required. content is applied on top of the slide type’s defaults and the presentation’s theme. Position the slide with atIndex or afterSlideId; without either it is appended at the end. Returns 201 with the new slide and its index.

PUT /api/v1/presentations/{id}/slides/{slideId}

Replaces the slide’s type, content, notes and visibility. Fields you leave out keep their current value; the slide’s id and parent are always kept. The slide is validated against its type before it is saved.

DELETE /api/v1/presentations/{id}/slides/{slideId}

The last slide of a presentation cannot be deleted (400).

POST /api/v1/presentations/{id}/slides/reorder
Content-Type: application/json
{
"slideIds": ["slide-3", "slide-1", "slide-2"]
}

Slides are placed in the order given. Slides you leave out keep their relative order after the listed ones; an unknown id is a 400.

Writing raw HTML or CSS into a slide requires that the key’s owner is allowed to edit custom HTML; otherwise the request is refused with 403.

Read your organization’s slide library and insert its items into a presentation.

GET /api/v1/slide-library?themeId=deckyard&limit=20

Requires read. Query parameters: themeId (optional filter), limit and offset. Items in the trash are left out. Each item has its id, name, slideType, themeId, content, tags, createdAt and createdById.

GET /api/v1/slide-library/{itemId}

Requires read.

POST /api/v1/presentations/{id}/slides/from-library
Content-Type: application/json
{
"libraryItemId": "abc123",
"afterSlideId": "slide-2"
}

Requires write. Creates a new slide from the library item, using the presentation’s theme and language. Position it with atIndex or afterSlideId, as when creating a slide. The response includes copiedFrom with the library item’s id and name.

Read reviewer feedback and respond to it. Requires the comments:read or comments:write scope, and a Deckyard instance running on the database storage backend: file storage has no comment store.

Comments identify people by id only (authorId, resolvedById), never by email address. Each comment carries an editUrl that opens the editor at the commented slide.

GET /api/v1/presentations/{id}/comments?status=open

Requires comments:read. Query parameters:

  • status - open, resolved, dismissed or all (default all)
  • slideId - only comments on this slide
  • since - an ISO 8601 date or datetime

Returns the comments with their replies and the context of the slide they are on.

POST /api/v1/presentations/{id}/comments
Content-Type: application/json
{
"body": "Can we shorten this title?",
"slideId": "slide-2"
}

Requires comments:write. The comment is written as the key’s owner, who needs permission to comment on the presentation. body is required, at most 5000 characters. Add parentId to reply to an existing comment. Returns 201.

POST /api/v1/comments/{commentId}/status
Content-Type: application/json
{
"status": "resolved"
}

Requires comments:write. status is resolved, open or dismissed. Only the presentation’s owner or original creator may change a comment’s status; a transition that is not allowed returns 409.

POST /api/v1/presentations/{id}/publish

Requires write. Publishes the presentation, the same way publishing from the editor does. Refused on an instance running in sandbox mode.

GET /api/v1/presentations/{id}/publish

Requires read. Returns isPublished, and for a published presentation also publishId, slug, path, ogImageUrl and publishedAt.

DELETE /api/v1/presentations/{id}/publish

Requires write. Takes the published version offline.

POST /api/v1/presentations/{id}/translate
Content-Type: application/json
{
"targetLang": "nl",
"sourceLang": "en-GB",
"vendor": "openai"
}

Requires ai, write access to the presentation, and AI enabled on the instance; counts toward the daily AI limit. Stores a translated language version inside the presentation.

  • targetLang (required) - one of the supported languages
  • sourceLang - defaults to the presentation’s active language
  • vendor - the AI provider to use
  • fillMissing (default true) - only translate what the existing target version lacks
  • overwrite (default false) - replace an existing target version
GET /api/v1/translate/languages

Requires read. Returns each language’s code and English label.

Export presentations in various formats. Requires the export scope.

GET /api/v1/presentations/{id}/export/json

Returns a portable JSON format that can be imported elsewhere.

GET /api/v1/presentations/{id}/export/html

Returns a standalone HTML file with all assets embedded.

GET /api/v1/presentations/{id}/export/pptx?scale=2

Query parameters:

  • scale (1-3, default 2) - Image quality multiplier
GET /api/v1/presentations/{id}/export/pdf

Returns print-ready HTML optimized for PDF conversion.

Generate content using AI. Requires the ai scope.

GET /api/v1/ai/vendors

Returns available AI providers and which are configured. This one needs only read.

POST /api/v1/ai/wizard
Content-Type: application/json
{
"content": "Your raw content text here...",
"vendor": "openai",
"theme": "deckyard",
"language": "en-GB"
}

Generates a complete presentation from text content.

POST /api/v1/ai/append-slides
Content-Type: application/json
{
"presentationId": "abc123",
"content": "Additional content...",
"vendor": "openai"
}

Generates slides and appends them to an existing presentation.

GET /api/v1/themes

Returns available system and custom themes.

GET /api/v1/slide-types

Returns slide type definitions with field schemas.

GET /api/v1/slide-types/{slideType}/schema

Returns one slide type’s fields with their metadata, its defaults, and an example slide.

GET /api/v1/image-library?q=nature&limit=20

Search the image library for assets.

The deck format’s JSON Schema, generated from this instance’s slide-type registry. These endpoints need no API key.

GET /api/v1/schema/deck.json
GET /api/v1/schema/slide-types/{name}.json

The first returns the whole-deck schema, the second the content schema of one slide type. See Schemas for how to use them.

The API uses standard HTTP status codes:

StatusMeaning
200Success
201Created
400Bad Request - Invalid input
401Unauthorized - Invalid or missing API key
403Forbidden - Valid key but insufficient scope
404Not Found
405Method Not Allowed - see the Allow header
429Rate Limited - Slow down
500Server Error

Error responses use one envelope: error is a stable machine code to branch on, message is readable text, and details is optional extra information.

{
"error": "bad_request",
"message": "Invalid slide data",
"details": ["..."]
}
Terminal window
# Create a presentation
RESPONSE=$(curl -s -X POST "https://your-instance.com/api/v1/presentations" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Q4 Report", "theme": "deckyard"}')
# Extract the ID
PRES_ID=$(echo $RESPONSE | jq -r '.presentation.id')
# Export as PowerPoint
curl "https://your-instance.com/api/v1/presentations/$PRES_ID/export/pptx" \
-H "Authorization: Bearer $API_KEY" \
-o report.pptx
Terminal window
curl -X POST "https://your-instance.com/api/v1/ai/wizard" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Our company had a great Q4. Revenue grew 25% year over year...",
"vendor": "openai",
"theme": "deckyard"
}'
import requests
API_KEY = "dk_live_your_key_here"
BASE_URL = "https://your-instance.com/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# List presentations
response = requests.get(f"{BASE_URL}/presentations", headers=headers)
presentations = response.json()["presentations"]
# Create a new presentation
new_pres = requests.post(
f"{BASE_URL}/presentations",
headers=headers,
json={"title": "API Created", "theme": "deckyard"}
)
print(new_pres.json())

The full OpenAPI 3.0 specification is available at:

GET /api/v1/openapi.yaml

Import this into tools like Postman or Insomnia for automatic client generation.

Go to Settings > API Keys to:

  • Create new keys with specific scopes
  • View key usage statistics
  • Revoke compromised keys

If you need programmatic key management:

Terminal window
# List your keys
curl "https://your-instance.com/api/api-keys" \
-H "Cookie: your-session-cookie"
# Create a key
curl -X POST "https://your-instance.com/api/api-keys" \
-H "Cookie: your-session-cookie" \
-H "Content-Type: application/json" \
-d '{"name": "CI/CD Key", "permissions": ["read", "export"]}'
# Revoke a key
curl -X DELETE "https://your-instance.com/api/api-keys/{key-id}" \
-H "Cookie: your-session-cookie"
# View usage
curl "https://your-instance.com/api/api-keys/{key-id}/usage?days=30" \
-H "Cookie: your-session-cookie"