Slideform REST API
Programmatic access to projects, jobs, files, and usage data.
Authentication
All endpoints (except /health) require an API key in the x-api-key header.
API keys are scoped to an organization. Generate one from the Slideform app under Admin → API.
// Every request must include this header
x-api-key: ta_<your_api_key>
Typical Workflow
- Check quota —
GET /usageto verify remaining slides - List projects —
GET /projectsto find the project ID - Get project details —
GET /project?project=<id>to discover pragmas - Submit a job —
POST /jobwith pragma values - Poll for completion —
GET /status/<job_id>until status is"ok" - Download the file —
GET /job/<job_id>/file - Clean up —
DELETE /job/<job_id>/fileto remove files you no longer need - Review history —
GET /project/<id>/downloadsto see past jobs
Code Examples
List Projects
Fetch all projects in your organization.
curl -s -H "x-api-key: ta_your_key" \
https://rest.slideform.co/projects
import requests API_KEY = "ta_your_key" BASE = "https://rest.slideform.co" HEADERS = {"x-api-key": API_KEY} resp = requests.get(f"{BASE}/projects", headers=HEADERS) for p in resp.json()["projects"]: print(f"{p['id']} {p['name']}")
const API_KEY = "ta_your_key"; const BASE = "https://rest.slideform.co"; const headers = { "x-api-key": API_KEY }; const resp = await fetch(`${BASE}/projects`, { headers }); const data = await resp.json(); data.projects.forEach(p => console.log(p.id, p.name));
require "net/http" require "json" API_KEY = "ta_your_key" BASE = "https://rest.slideform.co" uri = URI("#{BASE}/projects") req = Net::HTTP::Get.new(uri) req["x-api-key"] = API_KEY resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } data = JSON.parse(resp.body) data["projects"].each { |p| puts "#{p['id']} #{p['name']}" }
Full Workflow: Submit, Poll, Download
End-to-end example that submits a job, polls until completion, and downloads the result.
#!/bin/bash API_KEY="ta_your_key" BASE="https://rest.slideform.co" # 1. Submit a job JOB=$(curl -s -X POST "$BASE/job" \ -H "x-api-key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project": "abc123", "pragmas": { "{{report_date}}": "2026-03-01", "{{region}}": "North America" } }') JOB_ID=$(echo "$JOB" | jq -r '.id') echo "Submitted job: $JOB_ID" # 2. Poll for completion while true; do STATUS=$(curl -s -H "x-api-key: $API_KEY" "$BASE/status/$JOB_ID") JOB_STATUS=$(echo "$STATUS" | jq -r '.status') if [ "$JOB_STATUS" = "ok" ]; then echo "Job complete!" break elif [ "$JOB_STATUS" = "failed" ]; then echo "Job failed." exit 1 fi echo "Status: $JOB_STATUS — waiting 5s..." sleep 5 done # 3. Download the file FILE_NAME=$(echo "$STATUS" | jq -r '.file_name') curl -s -H "x-api-key: $API_KEY" \ "$BASE/job/$JOB_ID/file" \ -o "$FILE_NAME" echo "Downloaded: $FILE_NAME"
import time import requests API_KEY = "ta_your_key" BASE = "https://rest.slideform.co" HEADERS = {"x-api-key": API_KEY} # 1. Submit a job job = requests.post( f"{BASE}/job", headers=HEADERS, json={ "project": "abc123", "pragmas": { "{{report_date}}": "2026-03-01", "{{region}}": "North America", }, }, ).json() job_id = job["id"] print(f"Submitted job: {job_id}") # 2. Poll for completion while True: status = requests.get( f"{BASE}/status/{job_id}", headers=HEADERS ).json() if status["status"] == "ok": print("Job complete!") break elif status["status"] == "failed": raise RuntimeError("Job failed") print(f"Status: {status['status']} — waiting 5s...") time.sleep(5) # 3. Download the file file_name = status["file_name"] resp = requests.get( f"{BASE}/job/{job_id}/file", headers=HEADERS ) with open(file_name, "wb") as f: f.write(resp.content) print(f"Downloaded: {file_name}")
import { writeFile } from "node:fs/promises"; const API_KEY = "ta_your_key"; const BASE = "https://rest.slideform.co"; const headers = { "x-api-key": API_KEY, "Content-Type": "application/json" }; // 1. Submit a job const jobResp = await fetch(`${BASE}/job`, { method: "POST", headers, body: JSON.stringify({ project: "abc123", pragmas: { "{{report_date}}": "2026-03-01", "{{region}}": "North America", }, }), }); const job = await jobResp.json(); const jobId = job.id; console.log(`Submitted job: ${jobId}`); // 2. Poll for completion let status; while (true) { const r = await fetch(`${BASE}/status/${jobId}`, { headers }); status = await r.json(); if (status.status === "ok") { console.log("Job complete!"); break; } else if (status.status === "failed") { throw new Error("Job failed"); } console.log(`Status: ${status.status} — waiting 5s...`); await new Promise(r => setTimeout(r, 5000)); } // 3. Download the file const fileResp = await fetch(`${BASE}/job/${jobId}/file`, { headers }); const buffer = Buffer.from(await fileResp.arrayBuffer()); await writeFile(status.file_name, buffer); console.log(`Downloaded: ${status.file_name}`);
require "net/http" require "json" API_KEY = "ta_your_key" BASE = "https://rest.slideform.co" def api_get(path) uri = URI("#{BASE}#{path}") req = Net::HTTP::Get.new(uri) req["x-api-key"] = API_KEY Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } end def api_post(path, body) uri = URI("#{BASE}#{path}") req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json", "x-api-key" => API_KEY) req.body = body.to_json Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } end # 1. Submit a job resp = api_post("/job", { project: "abc123", pragmas: { "{{report_date}}" => "2026-03-01", "{{region}}" => "North America", } }) job = JSON.parse(resp.body) job_id = job["id"] puts "Submitted job: #{job_id}" # 2. Poll for completion status = nil loop do status = JSON.parse(api_get("/status/#{job_id}").body) case status["status"] when "ok" puts "Job complete!" break when "failed" abort "Job failed" end puts "Status: #{status['status']} — waiting 5s..." sleep 5 end # 3. Download the file file_resp = api_get("/job/#{job_id}/file") file_name = status["file_name"] File.binwrite(file_name, file_resp.body) puts "Downloaded: #{file_name}"
Upload a File and Use in a Job
Upload an image, then reference it in a job submission.
# 1. Upload the image curl -s -X POST "$BASE/files" \ -H "x-api-key: $API_KEY" \ -F "file=@/path/to/logo.png" # 2. Submit a job referencing the uploaded image curl -s -X POST "$BASE/job" \ -H "x-api-key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project": "abc123", "pragmas": { "{{report_date}}": "2026-03-01", "{{logo}}": "logo.png" }, "options": { "{{logo}}": { "type": "image", "crop": true } } }'
import requests API_KEY = "ta_your_key" BASE = "https://rest.slideform.co" HEADERS = {"x-api-key": API_KEY} # 1. Upload the image with open("logo.png", "rb") as f: upload = requests.post( f"{BASE}/files", headers={"x-api-key": API_KEY}, files={"file": f}, ).json() print(f"Uploaded: {upload['id']}") # 2. Submit a job referencing the uploaded image job = requests.post( f"{BASE}/job", headers=HEADERS, json={ "project": "abc123", "pragmas": { "{{report_date}}": "2026-03-01", "{{logo}}": "logo.png", }, "options": { "{{logo}}": {"type": "image", "crop": True}, }, }, ).json() print(f"Job submitted: {job['id']}")
import { readFile } from "node:fs/promises"; const API_KEY = "ta_your_key"; const BASE = "https://rest.slideform.co"; // 1. Upload the image const form = new FormData(); form.append("file", new Blob([await readFile("logo.png")]), "logo.png"); const upload = await fetch(`${BASE}/files`, { method: "POST", headers: { "x-api-key": API_KEY }, body: form, }); const uploadData = await upload.json(); console.log(`Uploaded: ${uploadData.id}`); // 2. Submit a job referencing the uploaded image const job = await fetch(`${BASE}/job`, { method: "POST", headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ project: "abc123", pragmas: { "{{report_date}}": "2026-03-01", "{{logo}}": "logo.png", }, options: { "{{logo}}": { type: "image", crop: true }, }, }), }); const jobData = await job.json(); console.log(`Job submitted: ${jobData.id}`);
require "net/http" require "json" API_KEY = "ta_your_key" BASE = "https://rest.slideform.co" # 1. Upload the image uri = URI("#{BASE}/files") file = File.open("logo.png", "rb") form = [["file", file, { filename: "logo.png" }]] req = Net::HTTP::Post.new(uri) req["x-api-key"] = API_KEY req.set_form(form, "multipart/form-data") resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } upload = JSON.parse(resp.body) puts "Uploaded: #{upload['id']}" # 2. Submit a job referencing the uploaded image uri = URI("#{BASE}/job") req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json", "x-api-key" => API_KEY) req.body = { project: "abc123", pragmas: { "{{report_date}}" => "2026-03-01", "{{logo}}" => "logo.png", }, options: { "{{logo}}" => { type: "image", crop: true }, } }.to_json resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } job = JSON.parse(resp.body) puts "Job submitted: #{job['id']}"
Send an Email
Send an email with HTML content to a list of recipients.
curl -s -X POST "$BASE/email/send" \ -H "x-api-key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "html": "<h1>Monthly Report</h1><p>Attached.</p>", "subject": "March 2026 Report", "recipients": ["team@example.com"] }'
resp = requests.post(
f"{BASE}/email/send",
headers=HEADERS,
json={
"html": "<h1>Monthly Report</h1><p>Attached.</p>",
"subject": "March 2026 Report",
"recipients": ["team@example.com"],
},
).json()
print(resp["message"]) # "Email sent to 1 recipient(s)"
const resp = await fetch(`${BASE}/email/send`, { method: "POST", headers, body: JSON.stringify({ html: "<h1>Monthly Report</h1><p>Attached.</p>", subject: "March 2026 Report", recipients: ["team@example.com"], }), }); const result = await resp.json(); console.log(result.message); // "Email sent to 1 recipient(s)"
resp = api_post("/email/send", { html: "<h1>Monthly Report</h1><p>Attached.</p>", subject: "March 2026 Report", recipients: ["team@example.com"], }) data = JSON.parse(resp.body) puts data["message"] # "Email sent to 1 recipient(s)"
Error Responses
All errors return a consistent JSON body:
{
"status": "failed",
"message": "Optional error description"
}
| HTTP Code | Meaning |
|---|---|
| 200 | Success (or job still processing — check the status field) |
| 400 | Bad request — missing or invalid parameters |
| 401 | Unauthorized — invalid or missing API key |
| 404 | Resource not found |
| 500 | Internal server error |
Projects
List Projects
Returns all projects accessible to the authenticated organization.
Parameters
None.
Response
{
"projects": [
{ "id": "abc123", "name": "Monthly Report" },
{ "id": "def456", "name": "Quarterly Review" }
]
}
| Field | Type | Description |
|---|---|---|
projects | array | List of project objects |
projects[].id | string | Unique project identifier |
projects[].name | string | Display name |
Get Project Details
Returns project metadata and the list of pragmas (template placeholders). Use this to discover what values to pass when submitting a job.
Query Parameters
| Parameter | Required | Description |
|---|---|---|
project | Required | The project ID |
Response
{
"id": "abc123",
"name": "Monthly Report",
"description": "Auto-generated monthly sales report",
"owner": "user@example.com",
"pragmas": [
{ "pragma": "{{report_date}}", "type": "text" },
{ "pragma": "{{region}}", "type": "dropdown" },
{ "pragma": "{{logo}}", "type": "image" }
]
}
| Field | Type | Description |
|---|---|---|
id | string | Project ID |
name | string | Project name |
description | string | Project description |
owner | string | Email of the project owner |
pragmas | array | Template placeholders |
pragmas[].pragma | string | Pragma key including {{ delimiters |
pragmas[].type | string | text, dropdown, image, chart, table, etc. |
Jobs
Submit a Job
Submits a form to generate an output file from a project template. The job runs asynchronously — use the returned id to poll for completion.
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
project |
Required | string | The project ID |
pragmas |
Required | object | Key-value mapping of pragma keys to values. Keys must include {{ and }} delimiters. |
options |
Optional | object | Per-pragma options, keyed by pragma name. Only needed for image pragmas. |
Example Request
{
"project": "abc123",
"pragmas": {
"{{report_date}}": "2026-03-01",
"{{region}}": "North America"
}
}
Example with Image Options
{
"project": "abc123",
"pragmas": {
"{{report_date}}": "2026-03-01",
"{{logo}}": "https://example.com/logo.png"
},
"options": {
"{{logo}}": {
"type": "image",
"crop": true,
"anchor": "center",
"transparent": false
}
}
}
Image Option Fields
| Field | Type | Default | Description |
|---|---|---|---|
type | string | — | Set to "image" |
crop | boolean | false | Crop image to fit placeholder |
anchor | string | "center" | Anchor position within placeholder |
transparent | boolean | false | Preserve transparency |
Response
{
"status": "ok",
"id": "a1b2c3d4e5f6",
"file_name": "Monthly_Report_03-15-2026_01-30-00_f7e8d9.pptx",
"project": "abc123",
"ts": 1710504600.0,
"duplicate": false
}
| Field | Type | Description |
|---|---|---|
status | string | "ok" on successful submission |
id | string | Unique job ID — use with /status/<job_id> and /job/<job_id>/file |
file_name | string | Generated output file name |
project | string | Project ID |
ts | number | Unix timestamp of submission |
duplicate | boolean | true if an identical pending job was reused |
Submit a Bulk Job
Submits multiple jobs from a single project. Can generate separate output files per pragma set, or combine all into a single document.
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
project |
Required | string | The project ID |
pragmas |
Required | array | Array of pragma value objects, one per output section |
settings |
Optional | object | Bulk operation settings |
mode |
Optional | string | "single" to combine all into one document. Omit for separate files. |
Example (separate files)
{
"project": "abc123",
"pragmas": [
{ "{{region}}": "North America", "{{quarter}}": "Q1" },
{ "{{region}}": "Europe", "{{quarter}}": "Q1" }
]
}
Example (single combined document)
{
"project": "abc123",
"pragmas": [
{ "{{region}}": "North America", "{{quarter}}": "Q1" },
{ "{{region}}": "Europe", "{{quarter}}": "Q1" }
],
"mode": "single"
}
Response
{
"status": "ok",
"results": {
"id": "d4e5f6a1b2c3",
"project": "abc123",
"status": "submitted",
"file_name": "Monthly_Report_03-15-2026_a1b2c3.pptx",
"ts": 1710504600.0
},
"message": "Check for results in the Slideform app"
}
"single" mode, all pragma sets are merged into one document with a single job ID. Without "single", separate jobs are submitted for each pragma set.
Check Job Status
Polls the status of a submitted job. Call repeatedly until status is "ok" (completed) or "failed".
Path Parameters
| Parameter | Required | Description |
|---|---|---|
job_id | Required | The job ID from POST /job or POST /bulk |
Response (in progress)
{
"status": "submitted"
}
Response (completed)
{
"status": "ok",
"id": "a1b2c3d4e5f6",
"file_name": "Monthly_Report_03-15-2026_01-30-00_f7e8d9.pptx",
"file_format": "pptx",
"url": "https://storage.example.com/...",
"project": "abc123",
"ts": 1710504600.0
}
| Field | Type | Description |
|---|---|---|
status | string | "submitted", "processing", "ok", or "failed" |
id | string | Job ID (completed only) |
file_name | string | Output file name (completed only) |
file_format | string | Format: pptx, docx, xlsx, pdf, html (completed only) |
url | string | Download URL if cloud storage is configured (may be null) |
project | string | Project ID (completed only) |
ts | number | Unix timestamp of completion (completed only) |
Get Job History
Returns past jobs for a specific project, sorted by most recent first.
Path Parameters
| Parameter | Required | Description |
|---|---|---|
project_id | Required | The project ID |
Query Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
limit | Optional | 10 | Max results to return |
Response
{
"downloads": [
{
"id": "a1b2c3d4e5f6",
"project": "abc123",
"status": "completed",
"url": "https://storage.example.com/...",
"file_name": "Monthly_Report_03-15-2026_f7e8d9.pptx",
"ts": 1710504600.0
}
],
"project_id": "abc123"
}
Files
Download File by Name
Downloads a generated output file by its file name. If the job is still processing, returns the current status instead.
Path Parameters
| Parameter | Required | Description |
|---|---|---|
file_name | Required | The output file name |
Response (completed)
Binary file contents with appropriate Content-Type and Content-Disposition headers.
Response (still processing)
{
"status": "submitted"
}
Download File by Job ID
Downloads the output file for a specific job. This is the recommended download method since it uses the stable job ID.
Path Parameters
| Parameter | Required | Description |
|---|---|---|
job_id | Required | The job ID |
Response (completed)
Binary file contents with appropriate Content-Type and Content-Disposition headers.
Response (still processing)
{
"status": "submitted"
}
Delete File by Job ID
Deletes the output file and storage record for a completed job. The file is permanently removed and cannot be recovered.
Path Parameters
| Parameter | Required | Description |
|---|---|---|
job_id | Required | The job ID |
Response (200)
{
"status": "ok"
}
Response (still processing)
{
"status": "submitted"
}
Upload a File
Uploads a file to the organization's file storage. Uploaded files can be referenced in project pragmas (e.g., as image sources).
Request
Multipart form data with a file field.
# Example with curl
curl -X POST \
-H "x-api-key: ta_your_key" \
-F "file=@/path/to/image.png" \
https://rest.slideform.co/files
Response
{
"status": "ok",
"id": "image.png",
"file_name": "/files/uploads/org/image.png",
"image_store": "local",
"ts": 1710504600.0
}
| Field | Type | Description |
|---|---|---|
status | string | "ok" |
id | string | File identifier (original filename) |
file_name | string | Full storage path |
image_store | string | Storage type ("local") |
ts | number | Unix timestamp of upload |
Other
Send Email
Sends an email with pre-generated HTML content. Recipients are validated against the organization's allowed domain list (if configured).
Request Body
| Field | Required | Type | Description |
|---|---|---|---|
html |
Required | string | HTML content of the email body |
recipients |
Required | array or string | Email address(es). A single string is accepted. |
subject |
Optional | string | Subject line. Defaults to "Email from Slideform". |
project_id |
Optional | string | Project ID for logging |
Example Request
{
"html": "<html><body><h1>Report</h1></body></html>",
"subject": "Monthly Report - March 2026",
"recipients": ["user@example.com", "manager@example.com"],
"project_id": "abc123"
}
Response (success)
{
"status": "ok",
"message": "Email sent to 2 recipient(s)"
}
Response (domain validation failure)
{
"status": "failed",
"error": "Recipients not in allowed domains: external@other.com"
}
*.example.com) are supported.
Get Usage & Quota
Returns the organization's current plan, remaining slide quota, and AI add-on usage.
Parameters
None.
Response
{
"status": "ok",
"remaining_slides": 450,
"plan": "Professional",
"addon": {
"max_monthly_insights": 100,
"remaining_insights": 72,
"num_monthly_insights": 28
}
}
| Field | Type | Description |
|---|---|---|
remaining_slides | number | Slides remaining in current billing period |
plan | string | Active plan name |
addon | object or null | AI add-on usage (only if organization has AI add-on) |
Health Check
No authentication required. Returns "healthy" (plain text) if the service is running.