API Reference
The Impresent API provides programmatic access to all platform features. The API is hosted on Cloudflare Workers at https://api.impresent.dev.
Authentication
All API requests (except /health) require authentication using an API key.
Getting an API Key
- Log in to the Impresent Dashboard
- Navigate to Settings > API Keys
- Click “Create New Key”
- Copy and store your key securely - it won’t be shown again
Using Your API Key
Include the API key in the Authorization header:
curl https://api.impresent.dev/api/decks \
-H "Authorization: Bearer imp_your_api_key_here"
Base URL
https://api.impresent.dev
Response Format
All responses are JSON. Successful responses have this structure:
{
"data": { ... },
"meta": {
"requestId": "req_abc123"
}
}
Error responses:
{
"error": {
"code": "NOT_FOUND",
"message": "Deck not found"
},
"meta": {
"requestId": "req_abc123"
}
}
Endpoints
Health Check
Check if the API is running.
GET /health
Response:
{
"status": "ok",
"version": "1.0.0"
}
Decks
List Decks
Get all decks for the authenticated user.
GET /api/decks
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
limit | number | Max results (default: 50, max: 100) |
offset | number | Pagination offset |
sort | string | Sort field: updated, created, title |
order | string | Sort order: asc, desc |
Response:
{
"data": [
{
"id": "deck_abc123",
"slug": "quarterly-review",
"title": "Q3 2024 Review",
"templateId": "corporate",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:00Z"
}
],
"meta": {
"total": 42,
"limit": 50,
"offset": 0
}
}
Create Deck
Create a new deck.
POST /api/decks
Request Body:
{
"slug": "my-presentation",
"title": "My Presentation",
"markdown": "---\nmarp: true\n---\n\n# Slide 1\n\nContent here",
"templateId": "minimal"
}
| Field | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | URL-friendly identifier (unique per user) |
markdown | string | Yes | Full markdown content |
title | string | No | Display title (extracted from markdown if not provided) |
templateId | string | No | Template ID (default: minimal) |
Response:
{
"data": {
"id": "deck_xyz789",
"slug": "my-presentation",
"title": "My Presentation",
"templateId": "minimal",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
}
Get Deck
Get a deck by ID.
GET /api/decks/:id
Response:
{
"data": {
"id": "deck_abc123",
"slug": "quarterly-review",
"title": "Q3 2024 Review",
"markdown": "---\nmarp: true\n...",
"templateId": "corporate",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:00Z"
}
}
Get Deck by Slug
Get a deck by its slug.
GET /api/decks/by-slug/:slug
Update Deck
Update an existing deck.
PUT /api/decks/:id
Request Body:
{
"markdown": "---\nmarp: true\n---\n\n# Updated Content",
"title": "Updated Title",
"templateId": "dark"
}
All fields are optional. Only provided fields are updated.
Update Single Slide
Update a single slide by index (0-based).
PATCH /api/decks/:id/slides/:index
Request Body:
{
"content": "# Updated Slide\n\nNew content for this slide"
}
Delete Deck
Delete a deck permanently.
DELETE /api/decks/:id
Response:
{
"data": {
"deleted": true
}
}
Rendering
Render Deck
Render a stored deck to HTML, PDF, or PPTX.
POST /api/decks/:id/render
Request Body:
{
"format": "pdf"
}
| Field | Type | Required | Description |
|---|---|---|---|
format | string | Yes | Output format: html, pdf, pptx |
Response:
{
"data": {
"url": "https://storage.impresent.dev/renders/abc123.pdf",
"expiresAt": "2024-01-15T11:30:00Z",
"format": "pdf",
"sizeBytes": 524288
}
}
The URL is a presigned R2 URL valid for 1 hour.
Render Raw Markdown
Render markdown content directly (without storing a deck).
POST /api/render
Request Body:
{
"markdown": "---\nmarp: true\n---\n\n# Slide 1",
"format": "html",
"templateId": "minimal"
}
| Field | Type | Required | Description |
|---|---|---|---|
markdown | string | Yes | Markdown content to render |
format | string | Yes | Output format: html, pdf, pptx |
templateId | string | No | Template to apply |
Templates
List Templates
Get available templates (built-in and user’s custom templates).
GET /api/templates
Response:
{
"data": [
{
"id": "minimal",
"name": "minimal",
"description": "Clean, lots of whitespace, sans-serif",
"type": "built-in"
},
{
"id": "tmpl_user123",
"name": "my-brand",
"description": "Custom company branding",
"type": "custom"
}
]
}
Get Template
Get template details including CSS.
GET /api/templates/:id
Response:
{
"data": {
"id": "corporate",
"name": "corporate",
"description": "Professional, muted blues/grays, structured layouts",
"css": "/* @theme corporate */\n...",
"scaffold": "---\nmarp: true\n...",
"manifest": {
"variables": {
"--primary-color": "#1e40af"
},
"layouts": [
{ "name": "lead", "description": "Title slide" }
]
}
}
}
Upload Custom Template
Upload a new custom template.
POST /api/templates
Content-Type: multipart/form-data
Form Fields:
| Field | Type | Required | Description |
|---|---|---|---|
manifest | file | Yes | template.yaml file |
css | file | Yes | theme.css file |
scaffold | file | No | scaffold.md file |
assets | file[] | No | Asset files (logos, backgrounds) |
Delete Custom Template
Delete a custom template.
DELETE /api/templates/:id
Assets
Upload Asset
Upload an image or other binary asset.
POST /api/assets
Content-Type: multipart/form-data
Form Fields:
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload |
filename | string | No | Custom filename |
Response:
{
"data": {
"id": "asset_abc123",
"filename": "logo.png",
"mimeType": "image/png",
"sizeBytes": 15360,
"uri": "asset://asset_abc123"
}
}
Use the uri in your markdown:

Get Asset Metadata
GET /api/assets/:id
Get Asset URL
Get a presigned URL to download the asset.
GET /api/assets/:id/url
Response:
{
"data": {
"url": "https://storage.impresent.dev/assets/abc123.png",
"expiresAt": "2024-01-15T11:30:00Z"
}
}
Delete Asset
DELETE /api/assets/:id
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid API key |
FORBIDDEN | 403 | Access denied to resource |
NOT_FOUND | 404 | Resource not found |
VALIDATION_ERROR | 400 | Invalid request body |
CONFLICT | 409 | Resource already exists (e.g., duplicate slug) |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Server error |
Rate Limits
| Plan | Requests/minute | Renders/hour |
|---|---|---|
| Free | 60 | 10 |
| Pro | 300 | 100 |
| Team | 1000 | 500 |
Rate limit headers are included in all responses:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705315800