Developers
Synclo API reference.
Generate presentations and documents from your own code. Send a topic or source text; Synclo writes and designs the presentation, saves it in your workspace and returns its links.
- Version
- 1
- Base URL
https://synclo.app/api/v1- OpenAPI 3.1
- openapi.json
- For AI tools
- developers.md
Overview
The API generates the same presentations and documents as the Synclo app, from the same credits. Each generated presentation is saved in the API key’s workspace, where it can be edited, shared and presented like any other.
Quick reference
- Base URL:
https://synclo.app/api/v1. Requests and responses are JSON over HTTPS. - Authenticate with an API key in the
Authorizationheader:Bearer sy_…. Workspace admins create keys in Settings › API. - Generation is asynchronous:
POST /generationsreturns202with a generationid; pollGET /generations/{id}every 5 seconds untilstatusiscompletedorfailed. - A generation costs 10 credits per slide, the same as in the app. Failed generations aren't charged.
- Send an
Idempotency-KeywithPOST /generationsso a retried request never creates or charges twice. - Every response carries a
Synclo-Request-Id; include it when you contact support.
The full API is also described in an OpenAPI 3.1 file, openapi.json, for Postman, Insomnia and client generators, and as plain text in developers.md for AI coding tools.
To make presentations from Claude or ChatGPT without code, use the connector. To build your own MCP client, see MCP server and OAuth.
https://synclo.app/api/v1Authentication
Authenticate every request with an API key, sent as a bearer token in the Authorization header. Keys start with sy_.
Workspace admins create and revoke keys in Settings › API. A key belongs to its workspace: generated presentations are saved there, and credits are drawn from its balance. API access is included with Pro, Max and Business; on other plans, requests return 402 plan_required.
A key is shown once, when it’s created. Keep it on your server and never in client-side code. A revoked key stops working immediately and returns 401.
Authorization: Bearer sy_…curl https://synclo.app/api/v1/me \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/me",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/me", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"workspace": {
"id": "5b0e2f71-9c3a-4d8e-b6a1-7e4f0c2d9a63",
"name": "Acme"
},
"plan": {
"id": "pro",
"name": "Pro"
},
"credits": 1920
}Quick start
From an API key to a finished presentation.
Create an API key in Settings › API and set it as an environment variable.
Shell export SYNCLO_API_KEY=sy_…Create a generation, then poll it every 5 seconds until
statusiscompletedorfailed. Most presentations complete in one to three minutes. The cURL version usesjq.Quick start ID=$(curl -s https://synclo.app/api/v1/generations \ -H "Authorization: Bearer $SYNCLO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input":"A pitch for a neighborhood bike repair shop","slides":8}' | jq -r .id) while true; do GEN=$(curl -s https://synclo.app/api/v1/generations/$ID \ -H "Authorization: Bearer $SYNCLO_API_KEY") STATUS=$(echo "$GEN" | jq -r .status) if [ "$STATUS" = completed ] || [ "$STATUS" = failed ]; then break; fi sleep 5 done echo "$GEN" | jq '.document // .error'import os, time, requests API = "https://synclo.app/api/v1" HEADERS = {"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"} res = requests.post(f"{API}/generations", headers=HEADERS, json={ "input": "A pitch for a neighborhood bike repair shop", "slides": 8, }) res.raise_for_status() generation = res.json() while generation["status"] in ("queued", "running"): time.sleep(5) res = requests.get(f"{API}/generations/{generation['id']}", headers=HEADERS) res.raise_for_status() generation = res.json() if generation["status"] == "completed": print(generation["document"]["edit_url"]) else: print(generation["error"]["message"])const API = "https://synclo.app/api/v1"; const headers = { Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`, "Content-Type": "application/json", }; let res = await fetch(`${API}/generations`, { method: "POST", headers, body: JSON.stringify({ input: "A pitch for a neighborhood bike repair shop", slides: 8 }), }); if (!res.ok) throw new Error((await res.json()).error.message); let generation = await res.json(); while (generation.status === "queued" || generation.status === "running") { await new Promise((resolve) => setTimeout(resolve, 5000)); res = await fetch(`${API}/generations/${generation.id}`, { headers }); if (!res.ok) throw new Error((await res.json()).error.message); generation = await res.json(); } console.log(generation.status === "completed" ? generation.document.edit_url : generation.error.message);Open
document.edit_urlto view the presentation in Synclo. Set"share": "link"for a public link, or"export": "pdf"for a file.
Credits
A generation costs the same as in the app: 10 credits per slide, drawn from the workspace’s balance. Images generated with auto are included.
When you create a generation, its estimate (credits.estimated, 10 credits per requested slide) is checked against the balance. If the balance is lower, the request returns 402 insufficient_credits and no generation is created. Credits are reserved when processing starts and charged for the slides generated; failed generations aren’t charged.
Image models other than auto may add credits per image: see Image models. The current balance is returned by GET /me, and a completed generation reports credits.used and credits.remaining. Plans and credit packs are on Pricing.
{
"error": {
"code": "insufficient_credits",
"message": "This generation needs 80 credits; the workspace has 40.",
"request_id": "req_Q3m8ZkT1vR6yWb2NcX5aLp9Jd4",
"credits": {
"balance": 40,
"needed": 80
}
}
}Create a generation
POST/api/v1/generations
Creates a presentation or document. Returns 202 Accepted with the generation, in the same shape as GET /generations/{id}, and a Location header; processing continues in the background.
Unknown parameters are rejected with 400, so a misspelled parameter is never silently ignored.
Headers
Idempotency-KeyheaderOptional, 1 to 255 characters. A retry with the same key and body within 24 hours returns the original generation with
Idempotent-Replayed: trueand isn't charged again. See Idempotency.
Body
inputstringRequiredThe content to generate from. With
generate, a topic or brief; withcondenseorpreserve, the source text, at least 20 words. Up to 400,000 characters; longer input returns413too_large.modestringHow
inputis used.generatewrites new content from a topic or brief.condensesummarizes the source text into slides and keeps its facts.preservekeeps the source text as written.Default:
generateformatstringslidesfor a presentation,documentfor a document of sections.Default:
slidesslidesintegerThe number of slides, the opening slide included (sections, for a document), from 1 to 20. With
condenseandpreserve, the maximum: the source text sets the number, up to this value. Every slide is charged 10 credits.Default:
8themestringThe
idof a theme fromGET /themes. If omitted, Synclo designs a look for the presentation.Default: A look designed for the presentation
templatestringThe
idof a template fromGET /templates. The presentation follows the template's structure, and its look unlessthemeorbrandis set.Default: None
aspectstringThe slide aspect ratio. Applies to
slidesonly.One of
16:9,4:3.Default:
16:9page_sizestringfluidpages size to their content;letteranda4are fixed print sizes. Applies todocumentonly.Default:
fluidtextobjectText options.
text.amountstringHow much text each slide carries.
One of
minimal,concise,detailed,extensive.Default: Chosen per slide
text.tonestringThe writing style, up to 100 characters, such as
warmorformal.text.audiencestringThe intended audience, up to 200 characters, such as
local investors.text.languagestringThe output language: a BCP 47 tag such as
frorpt-BR, or a language name such asJapanese.Default: The language of
input
imagesobjectImage options.
images.sourcestringaigenerates images.placeholdersadds an empty image slot to each slide, to fill in the editor.noneadds no images.Default:
aiimages.modelstringauto, or theidof a model from Image models. Withauto, Synclo chooses the model and images are included in the slide price; other models may add credits per image.Default:
autoimages.stylestringthemeuses the theme's image style. Otherwise, theidof a style from Image styles, or a description of up to 500 characters, such aspencil sketches on cream paper.Default:
theme
instructionsstringAdditional instructions for the content and design, up to 2,000 characters, such as
Close on the ask.brandbooleanIf
true, applies the workspace's brand from Settings › Brand: logo, fonts and colors. Returns400if the workspace has no brand.Default:
falsesharestringlinkturns on the public link, returned asdocument.share_url.One of
private,link.Default:
privateexportstringGenerates a file with the presentation:
pdf,pptx(PowerPoint), orpng(a ZIP with one PNG per slide; a single PNG for a one-slide presentation).Default: None
Responses
202Generation created
status queued and credits.estimated. A repeated Idempotency-Key returns the original generation with Idempotent-Replayed: true.400Invalid request
"brand": true in a workspace with no brand.402Insufficient credits
409Idempotency conflict
Idempotency-Key was already used with a different body.413Input too large
input exceeds 400,000 characters.429Too many generations
too_many_running), or the rate limit was reached (rate_limited).curl -X POST https://synclo.app/api/v1/generations \
-H "Authorization: Bearer $SYNCLO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c9e1a-2b7d-4c55-9d0e-1a2b3c4d5e6f" \
-d '{
"input": "A pitch for a neighborhood bike repair shop",
"mode": "generate",
"format": "slides",
"slides": 8,
"theme": "cobalt",
"aspect": "16:9",
"text": {
"amount": "concise",
"tone": "warm",
"audience": "local investors",
"language": "en"
},
"images": {
"source": "ai",
"model": "auto",
"style": "theme"
},
"instructions": "Close on the ask.",
"brand": false,
"share": "private",
"export": "pdf"
}'import os, requests
res = requests.post(
"https://synclo.app/api/v1/generations",
headers={
"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}",
"Idempotency-Key": "6f1c9e1a-2b7d-4c55-9d0e-1a2b3c4d5e6f",
},
json={
"input": "A pitch for a neighborhood bike repair shop",
"mode": "generate",
"format": "slides",
"slides": 8,
"theme": "cobalt",
"aspect": "16:9",
"text": {
"amount": "concise",
"tone": "warm",
"audience": "local investors",
"language": "en",
},
"images": {
"source": "ai",
"model": "auto",
"style": "theme",
},
"instructions": "Close on the ask.",
"brand": False,
"share": "private",
"export": "pdf",
},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "6f1c9e1a-2b7d-4c55-9d0e-1a2b3c4d5e6f",
},
body: JSON.stringify({
"input": "A pitch for a neighborhood bike repair shop",
"mode": "generate",
"format": "slides",
"slides": 8,
"theme": "cobalt",
"aspect": "16:9",
"text": {
"amount": "concise",
"tone": "warm",
"audience": "local investors",
"language": "en"
},
"images": {
"source": "ai",
"model": "auto",
"style": "theme"
},
"instructions": "Close on the ask.",
"brand": false,
"share": "private",
"export": "pdf"
}),
});
console.log(await res.json());HTTP/1.1 202 Accepted
Location: /api/v1/generations/gen_3f2a9c0e7b1d4e8a9c6f2b5d8e1a4c7b
{
"id": "gen_3f2a9c0e7b1d4e8a9c6f2b5d8e1a4c7b",
"object": "generation",
"status": "queued",
"created_at": "2026-10-12T14:03:11Z",
"started_at": null,
"completed_at": null,
"document": null,
"export": null,
"credits": {
"estimated": 80,
"used": null,
"remaining": null
},
"warnings": [],
"error": null
}Retrieve a generation
GET/api/v1/generations/{id}
Returns a generation. Poll every 5 seconds until status is completed or failed; most complete in one to three minutes.
Path parameters
idstringThe generation's
id, returned byPOST /generations.
Response
idstringUnique identifier:
gen_followed by 32 hexadecimal characters.objectstringAlways
generation.statusstringqueued,running,completedorfailed.created_atstringWhen the generation was created (ISO 8601, UTC).
started_atstring or nullWhen processing started;
nullwhile queued.completed_atstring or nullWhen the generation completed or failed;
nulluntil then.documentobject or nullThe generated presentation once
statusiscompleted; otherwisenull.document.idstringThe document ID, for
GET /documents/{id}.document.titlestringThe title.
document.slidesintegerThe number of slides (or sections), including the opening slide.
document.edit_urlstringOpens the presentation in the Synclo editor. Requires a workspace member's sign-in.
document.share_urlstring or nullThe public link, or
nullif the link is off.
exportobject or nullThe file requested with
export, ornull.urlrequires no authentication and expires after 7 days; treat it as a secret. If the file couldn't be generated, the presentation is still created,urlisnullanderrorexplains why.export.formatstringThe requested format.
One of
pdf,pptx,png.export.urlstring or nullThe download URL.
export.expires_atstringWhen
urlexpires.export.errorstringPresent only if the file couldn't be generated.
creditsobjectCredit usage.
credits.estimatedinteger10 credits per requested slide, checked against the balance when the generation was created.
credits.usedinteger or nullCredits charged:
nulluntil the generation finishes,0if it failed.credits.remaininginteger or nullThe workspace's balance after the generation, once
completed; otherwisenull.
warningsarray of stringsNon-fatal issues: the presentation was created, but a requested option couldn't be applied (for example, the public link couldn't be turned on). Usually empty.
errorobject or nullcodeandmessagewhenstatusisfailed; otherwisenull. Failed generations aren't charged. See Errors.
curl https://synclo.app/api/v1/generations/gen_3f2a9c0e7b1d4e8a9c6f2b5d8e1a4c7b \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/generations/gen_3f2a9c0e7b1d4e8a9c6f2b5d8e1a4c7b",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/generations/gen_3f2a9c0e7b1d4e8a9c6f2b5d8e1a4c7b", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"id": "gen_3f2a9c0e7b1d4e8a9c6f2b5d8e1a4c7b",
"object": "generation",
"status": "completed",
"created_at": "2026-10-12T14:03:11Z",
"started_at": "2026-10-12T14:03:12Z",
"completed_at": "2026-10-12T14:05:02Z",
"document": {
"id": "8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c",
"title": "Spoke & Sprocket",
"slides": 8,
"edit_url": "https://synclo.app/d/8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c",
"share_url": "https://acme.synclo.site/r/spoke-sprocket-x7Kq9TmA2pD"
},
"export": {
"format": "pdf",
"url": "https://synclo.app/files/kQ7mX2pLw9RaT3vB/spoke-and-sprocket.pdf",
"expires_at": "2026-10-19T14:05:02Z"
},
"credits": {
"estimated": 80,
"used": 80,
"remaining": 1920
},
"warnings": [],
"error": null
}List themes
GET/api/v1/themes
Returns the themes available for theme: Synclo’s, then the workspace’s own. Workspaces with brand lock return their own themes only.
Response
themes[].idstringThe value to send as
theme.themes[].namestringThe theme's name.
themes[].kindstringprebuiltfor Synclo's themes,workspacefor the workspace's own.themes[].descriptionstring or nullWhat the theme suits;
nullfor the workspace's own.
curl https://synclo.app/api/v1/themes \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/themes",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/themes", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"themes": [
{
"id": "cobalt",
"name": "Cobalt",
"kind": "prebuilt",
"description": "Confident business decks"
},
{
"id": "midnight",
"name": "Midnight",
"kind": "prebuilt",
"description": "Launches, tech and evening events"
},
…
]
}| id | Name | Description |
|---|---|---|
cobalt | Cobalt | Confident business decks |
midnight | Midnight | Launches, tech and evening events |
paprika | Paprika | Food, events and small businesses |
recess | Recess | Lessons and classrooms |
gridline | Gridline | Data reports, reviews and research |
charter | Charter | Boards, finance, legal and consulting |
billboard | Billboard | Campaigns, launches and announcements |
zurich | Zurich | Design, architecture and strategy |
fern | Fern | Health, sustainability and nonprofits |
bloom | Bloom | People, wellbeing and community |
gala | Gala | Celebrations and invitations |
folio | Folio | Reports, essays and pre-reads |
ledger | Ledger | Law, finance and academia |
mono | Mono | Product specs and engineering |
vow | Vow | Weddings, invitations and hospitality |
clay | Clay | Cafés, craft and interiors |
marigold | Marigold | Posters, youth and community events |
ink | Ink | Pitch decks and keynotes |
graphite | Graphite | Product reviews and strategy |
noir | Noir | Luxury, fashion and real estate |
forest | Forest | Nature, wellness and retreats |
ember | Ember | Restaurants, nights out and sports |
List templates
GET/api/v1/templates
Returns the templates available for template. A generation from a template follows its structure and look, with content written for your input.
Response
templates[].idstringThe value to send as
template.templates[].namestringThe template's name.
templates[].descriptionstringWhat the template makes.
templates[].good_forarray of stringsTypical uses.
curl https://synclo.app/api/v1/templates \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/templates",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/templates", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"templates": [
{
"id": "waypoint",
"name": "Lesson",
"description": "A lesson built as one adventure in one illustrated world: a hook, the skills one stop at a time, a quiz, a partner activity and an exit ticket.",
"good_for": [
"Lessons for any age",
"Classroom activities",
"Skills workshops for kids"
]
},
…
]
}| id | Name | Description |
|---|---|---|
waypoint | Lesson | A lesson built as one adventure in one illustrated world: a hook, the skills one stop at a time, a quiz, a partner activity and an exit ticket. |
threshold | Portfolio | Selected work told the same way each time: before and after at one seam, the details, what changed and the client's words. |
plumbline | Proposal | A client proposal that shows the work as it is and as it will be, the options side by side, the schedule and every line of the price. |
transit | Research report | Survey findings where every chart makes one point and says who was asked, how many and what the question was. |
block-party | Event invitation | An invitation that runs the whole day: when and where, the programme, a map, what to bring, how to help and how to reply. |
debut | Product launch | A launch told through the product itself: its screens built as real cards, three customers followed through their day. |
fanfare | Marketing campaign | A campaign plan at poster volume: the line, the creative, where it runs, the weeks and the numbers that prove it worked. |
monday | Weekly update | A team's week on one strip, Monday to Friday: what got done, what is at risk and the decisions needed. |
groundwork | Project status | Every measure read as planned against actual, with today marked wherever time appears and the site photographed month by month. |
caliper | Quarterly review | A quarter read like an instrument: every figure against its target and tolerance, red only where it is out. |
upswing | Seed pitch | A raise told along the company's own growth curve: flat on the problem, rising on the proof, dashed into the plan. |
proof | Case study | A customer story told as measured change: every claim a before and an after. |
chroma | Product catalogue | A range where every product owns a color: its page, its photograph and its place in the index. |
huddle | Workshop | A hands-on session in three modules, each with its own shape and color, drawn as warm flat illustration. |
show-and-tell | School project | A bright class project where the subject itself appears on every slide, big enough to read from the back row. |
runway | Investor update | The quarter told against the runway: every figure read against how long the money lasts. |
overcast | Lesson | A lesson that makes a class see it: a hook, a vote, the story in pictures, a check at the end. |
throughline | Strategy report | The answer on the cover, then the case: every title a claim, every slide placed in the argument. |
lineup | Wedding weekend | Three days told as a small festival: the line-up, the set times, the stages and the wristband. |
endpoint | Research talk | A study presented figure by figure, each with its takeaway in plain words. |
hush | Client proposal | From the problem you can measure to the change you promise, priced and scheduled. |
forecast | Product roadmap | What ships now, what comes next and what is still a chance, with the odds on each. |
playback | Research readout | What customers told you, each finding with how many said it. |
shade | Impact report | A year of work for donors and partners, measured in what changed. |
call-sheet | Trip plan | Who's coming, each day's plan and call time, the bookings and the budget. |
wayfinding | Onboarding | A new hire's first month: the people, the tools, the rituals and the checklist. |
pitch-tally | Seed pitch | The problem, the traction and the ask, for investors. |
report-review | Business review | A leadership team’s numbers, one point a slide. |
lesson-water-cycle | Lesson | A class from the hook to the exit question, with one activity. |
proposal-harbor-pine | Client proposal | The scope, the approach, the timeline and the price. |
themes-talus | Product launch | What it is, who it’s for and how it sells. |
editor-kickoff | Sales presentation | What customers get, with the numbers that prove it. |
research-heat | Conference talk | Research turned into a talk, every source kept. |
teams-weekly | Weekly update | What shipped, what’s next and where the team needs help. |
present-bikes | Year in review | A year’s story for a room: what happened, what it cost, what’s next. |
pdf-greenline | Board update | A quarter’s results and decisions, ready for the board. |
export-fitout | Project pitch | Options side by side, with the costs and a timeline. |
invite-sam | Party invitation | Who, when and where, and the one thing to keep quiet. |
doc-riverside | Grant application | The problem, the plan, the budget and who does what. |
Retrieve the workspace
GET/api/v1/me
Returns the API key’s workspace, its plan and its credit balance.
Response
workspace.idstringThe workspace ID.
workspace.namestring or nullThe workspace name.
plan.idstringpro,max,business.plan.namestringThe plan name.
creditsinteger or nullThe workspace's credit balance.
curl https://synclo.app/api/v1/me \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/me",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/me", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"workspace": {
"id": "5b0e2f71-9c3a-4d8e-b6a1-7e4f0c2d9a63",
"name": "Acme"
},
"plan": {
"id": "pro",
"name": "Pro"
},
"credits": 1920
}List documents
GET/api/v1/documents
Returns the workspace’s documents visible to the key’s owner, most recently updated first, up to 100. Documents in Trash are excluded.
Response
documents[].idstringThe document ID.
documents[].titlestringThe title.
documents[].updated_atstringWhen the document was last updated.
documents[].created_atstringWhen the document was created.
documents[].share_urlstring or nullThe public link, or
nullif the link is off.
curl https://synclo.app/api/v1/documents \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/documents",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/documents", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"documents": [
{
"id": "8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c",
"title": "Spoke & Sprocket",
"updated_at": "2026-10-12T14:05:02Z",
"created_at": "2026-10-12T14:03:12Z",
"share_url": "https://acme.synclo.site/r/spoke-sprocket-x7Kq9TmA2pD"
}
]
}Retrieve a document
GET/api/v1/documents/{id}
Returns a document with its full content: sections and blocks as Synclo stores them, its theme and its public link. An invalid ID returns 400; a document the key can’t access returns 404.
Path parameters
idstringThe document ID (a UUID), from
document.idorGET /documents.
Response
idstringThe document ID.
titlestringThe title.
themestringThe ID of the theme it uses.
updated_atstringWhen the document was last updated.
created_atstringWhen the document was created.
share_urlstring or nullThe public link, or
nullif the link is off.documentobjectThe content: sections and blocks.
curl https://synclo.app/api/v1/documents/8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c \
-H "Authorization: Bearer $SYNCLO_API_KEY"import os, requests
res = requests.get(
"https://synclo.app/api/v1/documents/8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c",
headers={"Authorization": f"Bearer {os.environ['SYNCLO_API_KEY']}"},
)
print(res.json())const res = await fetch("https://synclo.app/api/v1/documents/8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c", {
headers: {
Authorization: `Bearer ${process.env.SYNCLO_API_KEY}`,
},
});
console.log(await res.json());{
"id": "8c1e5b2a-4d3f-4a7e-9b1c-2d6e8f0a3b5c",
"title": "Spoke & Sprocket",
"theme": "cobalt",
"updated_at": "2026-10-12T14:05:02Z",
"created_at": "2026-10-12T14:03:12Z",
"share_url": "https://acme.synclo.site/r/spoke-sprocket-x7Kq9TmA2pD",
"document": {
"version": 1,
"title": "Spoke & Sprocket",
"sections": [
…
],
"citations": []
}
}MCP server
POSThttps://synclo.app/mcp
The MCP server lets Claude, ChatGPT and other MCP clients create and read presentations for a person who signs in with their Synclo account. It’s the server behind the Claude and ChatGPT connector; this section is for connecting other clients.
Connection
- Address
https://synclo.app/mcp- Transport
- Streamable HTTP. Send one JSON-RPC 2.0 message per
POST; responses are JSON. Notifications return202with no body, andGETreturns405. - Protocol versions
2025-11-25,2025-06-18and2025-03-26.- Authorization
- An OAuth access token for the person, sent as
Authorization: Bearer syo_…. See OAuth. - Plans
- Every plan, Free included. A connection acts in one workspace, which the person picks when they allow access.
Generations
Generation is asynchronous, as in the API. create_presentation returns a generation_id at once; get_generation waits up to 40 seconds for the generation before it returns, so call it again until the status is completed or failed. Most presentations complete in one to three minutes.
The same request within 10 minutes returns the first generation instead of starting another, so a retried call isn’t charged twice.
Credits and limits
A generation costs the same as in the API: 10 credits per slide from the connected workspace’s balance. Failed generations aren’t charged. The workspace’s plan sets the most slides per presentation: 10 on Free, 20 on Plus, Pro, Max and Business.
Calls are rate limited per connection, with the same limits as API requests.
Results and errors
A tool returns its result as text content and as structuredContent. A call that can’t complete returns a result with isError: true and a readable message. When the workspace has no credits left, the message is “This workspace is out of credits. Add credits at synclo.app.”
Without a valid access token, the server returns 401 with a WWW-Authenticate header that names the protected resource metadata and the scopes, plus error="invalid_token" when the token sent is expired or revoked, was issued for another resource, or belongs to a person who is no longer a member of the workspace. A tool that needs a scope the token lacks returns 403 insufficient_scope.
claude mcp add --transport http synclo https://synclo.app/mcp{
"mcpServers": {
"synclo": {
"type": "http",
"url": "https://synclo.app/mcp"
}
}
}POST /mcp HTTP/1.1
Host: synclo.app
Authorization: Bearer syo_…
Content-Type: application/json
Accept: application/json, text/event-stream
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "create_presentation",
"arguments": {
"input": "A pitch for a neighborhood bike repair shop",
"slides": 8
}
}
}HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://synclo.app/.well-known/oauth-protected-resource/mcp", scope="presentations:read presentations:write"| Tool | Title | Scope | Description |
|---|---|---|---|
create_presentation | Create a presentation | presentations:write | Creates a designed presentation in Synclo: a pitch deck, a sales or board deck, a lesson, a report, a proposal or any slides, written and designed with pictures and charts, or a document of sections. Works from a topic, a brief, an outline or source text, and saves it in the connected workspace. Returns a generation_id at once; a presentation takes three to five minutes, and in chat apps that show Synclo's card, the card shows it as it is written and when it is ready. Costs 10 credits per slide, the opening slide included. A repeat of the same request within 10 minutes returns the first generation. |
get_generation | Check on a presentation | presentations:read | Returns a generation's status. Waits up to 40 seconds for it to finish. When completed, returns the presentation's title, slide count, editor link, public link (if on) and the credits used and remaining; when failed, the reason. In chat apps that show Synclo's card, the card under create_presentation already shows the finished presentation. |
export_presentation | Export a presentation | presentations:read | Creates a PDF, PowerPoint (pptx) or PNG file of a presentation in the workspace and returns a download link that works for 7 days. A workspace can make up to 50 files a day through connected apps, one at a time. |
search_presentations | Find presentations | presentations:read | Lists the workspace's presentations and documents the person can see, most recently updated first, optionally filtered by words in the title. |
show_presentation | Show a presentation | presentations:read | Shows a presentation from the workspace in the chat, slide by slide, with links to open, present and download it. Returns its title, slide count and editor link. A presentation made with create_presentation in this chat is shown by its own card already. |
read_presentation | Read a presentation | presentations:read | Returns a presentation's text, slide by slide: each slide's title and words. |
list_themes | List themes | presentations:read | Lists the themes a presentation can use: Synclo's, then the workspace's own. |
list_templates | List templates | presentations:read | Lists the templates a presentation can follow. |
create_presentation arguments
inputstringRequiredWhat to make (a topic or brief), or the source text to turn into slides. Up to 400,000 characters.
modestringgenerate writes new content from a topic or a brief; condense makes slides from source text, tightening its words and keeping its facts; preserve keeps every word of the source text as written. Default generate.
One of
generate,condense,preserve.formatstringslides for a presentation, document for a document of sections. Default slides.
One of
slides,document.slidesintegerThe number of slides in all, the opening slide included: 5 makes the opening and four more. Default 8. From 1 to 20.
themestringA theme id from list_themes. If omitted, Synclo designs a look for the presentation.
templatestringA template id from list_templates.
languagestringThe output language, such as fr or Japanese. Default: the language of input. Up to 60 characters.
tonestringThe writing style, such as warm or formal. Up to 100 characters.
audiencestringThe intended audience. Up to 200 characters.
image_stylestringtheme (the default), a style id, or a description of the images. Up to 500 characters.
instructionsstringAdditional instructions for the content and design. Up to 2,000 characters.
| Tool | Arguments |
|---|---|
get_generation | generation_id (required) |
export_presentation | document_id (required), format (required) |
search_presentations | query, limit |
show_presentation | document_id (required) |
read_presentation | document_id (required) |
list_themes | None |
list_templates | None |
OAuth
Synclo is an OAuth 2.1 authorization server for the MCP server. Claude, ChatGPT and other MCP clients find it through discovery and need no setup; this section is for building your own client.
Use OAuth when your app acts for people in their own Synclo workspaces, on any plan. Access tokens work on the MCP server only. To automate your own workspace through the REST API, use an API key.
Discovery
GET /.well-known/oauth-protected-resource/mcp- Protected resource metadata (RFC 9728): the resource
https://synclo.app/mcp, its authorization server and its scopes. Also served at/.well-known/oauth-protected-resource. GET /.well-known/oauth-authorization-server- Authorization server metadata (RFC 8414): the endpoints below and what each supports.
Endpoints
GET /oauth/authorize- Shows the person the Allow screen; returns to
redirect_uriwith acode. POST /oauth/token- Exchanges a code or a refresh token for tokens. Form-encoded.
POST /oauth/register- Dynamic client registration (RFC 7591), for public clients.
POST /oauth/revoke- Revokes an access or refresh token (RFC 7009). Always returns
200.
Scopes
presentations:write- Create presentations and documents in the workspace. Spends its credits. Tools:
create_presentation. presentations:read- Find, read and show the presentations the person can open in the workspace, download them as files, list themes and templates, and check generations. Tools:
get_generation,export_presentation,search_presentations,show_presentation,read_presentation,list_themes,list_templates.
Client registration
Clients are public: there’s no client secret, and the token endpoint’s authentication method is none. Register in either of two ways.
- Client ID Metadata Document (preferred). Use an
httpsURL as yourclient_id. It serves JSON withclient_id(the same URL),client_nameandredirect_uris, and optionallylogo_uriandclient_uri. It must respond within 5 seconds and be at most 16 KB. Synclo keeps a copy for up to 24 hours, so a change can take a day to apply. - Dynamic client registration.
POST /oauth/register(RFC 7591) withclient_nameandredirect_uris; keep theclient_idit returns. Registration is rate limited.
Redirect URIs must match exactly. Loopback addresses (http://127.0.0.1 and http://localhost) match on any port, for desktop and command-line clients.
Authorization
Send the person to /oauth/authorize with response_type=code, client_id, redirect_uri, state, a PKCE code_challenge with code_challenge_method=S256 (plain isn’t supported), scope (space-separated; both scopes when omitted) and resource=https://synclo.app/mcp (RFC 8707; optional, and any other value returns invalid_target).
The person signs in if they need to, then sees your app’s name, the address it returns to and the permissions it asks for. They pick one workspace and choose Allow or Cancel. Synclo returns to redirect_uri with code and state, or with error. Every response carries iss=https://synclo.app (RFC 9207): check it before using the code.
A code is valid for 10 minutes and works once, with the same client, redirect URI, verifier and resource.
Tokens
Exchange the code at POST /oauth/token (form-encoded) with grant_type=authorization_code, code, redirect_uri, client_id, code_verifier and resource. The response holds an access token (syo_…) that expires after 1 hour and a refresh token (syr_…).
Refresh with grant_type=refresh_token. Each refresh returns a new refresh token, and the one sent stops working. Sending a refresh token that was already used revokes every token of that connection; the person then connects again. A connection can be refreshed for up to 90 days from the person’s approval, then needs a new authorization.
Errors use the codes of RFC 6749 and RFC 8707: invalid_request, invalid_client, invalid_grant, invalid_target and unsupported_grant_type. The token endpoint is rate limited. Token responses carry Cache-Control: no-store. Keep tokens on your server, never in URLs or logs.
Revocation
POST /oauth/revoke with token (RFC 7009) revokes it and always returns 200. A person can disconnect your app at any time in Settings › Connected apps, and its tokens stop working at once. Every call checks that the person is still a member of the workspace.
{
"issuer": "https://synclo.app",
"authorization_endpoint": "https://synclo.app/oauth/authorize",
"token_endpoint": "https://synclo.app/oauth/token",
"registration_endpoint": "https://synclo.app/oauth/register",
"revocation_endpoint": "https://synclo.app/oauth/revoke",
"response_types_supported": [
"code"
],
"grant_types_supported": [
"authorization_code",
"refresh_token"
],
"code_challenge_methods_supported": [
"S256"
],
"token_endpoint_auth_methods_supported": [
"none"
],
"revocation_endpoint_auth_methods_supported": [
"none"
],
"scopes_supported": [
"presentations:read",
"presentations:write"
],
"client_id_metadata_document_supported": true,
"authorization_response_iss_parameter_supported": true,
"service_documentation": "https://synclo.app/developers/oauth"
}https://synclo.app/oauth/authorize
?response_type=code
&client_id=https%3A%2F%2Fapp.example.com%2Foauth%2Fclient.json
&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
&scope=presentations%3Aread%20presentations%3Awrite
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&resource=https%3A%2F%2Fsynclo.app%2Fmcpcurl -X POST https://synclo.app/oauth/token \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=https://app.example.com/oauth/callback \
-d client_id=https://app.example.com/oauth/client.json \
-d code_verifier=$CODE_VERIFIER \
-d resource=https://synclo.app/mcp{
"access_token": "syo_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "syr_…",
"scope": "presentations:read presentations:write"
}curl -X POST https://synclo.app/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=$REFRESH_TOKEN \
-d client_id=https://app.example.com/oauth/client.json \
-d resource=https://synclo.app/mcp{
"client_id": "https://app.example.com/oauth/client.json",
"client_name": "Example App",
"redirect_uris": [
"https://app.example.com/oauth/callback"
],
"logo_uri": "https://app.example.com/logo.png",
"client_uri": "https://app.example.com"
}Image models
Set images.model to a model’s id, or to auto to let Synclo choose per image. Images generated with auto are included in the slide price.
Fast models add no credits. Quality and Specialist models add up to the credits below for each image they generate.
| id | Name | Kind | Added credits per image |
|---|---|---|---|
auto | Auto | Chosen by Synclo | None |
seedream-5-flash | Seedream 5.0 Flash | Fast | None |
flux-2-fast | Flux 2 Fast | Fast | None |
nano-banana-2-lite | Nano Banana 2 Lite | Fast | None |
nano-banana-2.1 | Nano Banana 2.1 | Quality | Up to 6 |
gpt-image-2.5-flare | GPT Image 2.5 Flare | Quality | Up to 8 |
flux-3 | Flux 3 | Quality | Up to 7 |
seedream-5-pro | Seedream 5.0 Pro | Quality | Up to 7 |
recraft-v4.1 | Recraft V4.1 | Specialist | Up to 5 |
nano-banana-2.1-hd | Nano Banana 2.1 HD | Specialist | Up to 8 |
Image styles
Set images.style to a style’s id, to theme for the theme’s own style, or to a description of up to 500 characters.
| id | Name | Kind |
|---|---|---|
theme | The theme’s style | Default |
photo | True to life | Photography |
lifestyle | Bright and candid | Photography |
cinematic | Golden hour | Photography |
black-white | Black and white | Photography |
still-life | Product close-up | Photography |
illustration | Flat vector | Illustration |
picture-book | Picture book | Illustration |
watercolor | Watercolor | Illustration |
isometric | Isometric | Illustration |
flat-line | Line art | Hand-drawn |
ink-sketch | Ink sketch | Hand-drawn |
3d | Soft 3D | Made by hand |
paper-cutout | Paper cut | Made by hand |
felt | Felt | Made by hand |
bold-poster | Screen-print poster | Graphic |
bauhaus | Bauhaus | Graphic |
magazine-cutout | Collage | Graphic |
neon-glow | Neon | Graphic |
waves | Silk waves | Abstract |
liquid-glass | Liquid glass | Abstract |
paint-swirl | Paint swirl | Abstract |
Errors
Errors share one shape: code is stable and meant for your code; message is readable text that may change; request_id matches the response’s Synclo-Request-Id header. Include the request ID when you contact support@synclo.app.
A generation that fails after it starts isn’t an error response: GET /generations/{id} returns 200 with status failed and an error whose code is listed in the second table. Failed generations aren’t charged.
{
"error": {
"code": "invalid_request",
"message": "…",
"request_id": "req_…"
}
}| Status | code | Description |
|---|---|---|
| 400 | invalid_request | The request body or a parameter is invalid, or a parameter is unknown. message names the parameter. |
| 401 | unauthorized | The API key is missing or invalid. The response includes WWW-Authenticate: Bearer realm="synclo". |
| 402 | insufficient_credits | The workspace doesn't have enough credits. error.credits includes balance and needed. |
| 402 | plan_required | The workspace's plan doesn't include API access. |
| 403 | forbidden | The API key's owner is no longer a member of the workspace. Create a new key in Settings › API. |
| 404 | not_found | The generation or document doesn't exist in this workspace. |
| 409 | idempotency_conflict | The Idempotency-Key was already used with a different request body. |
| 413 | too_large | input exceeds 400,000 characters. |
| 429 | rate_limited | Too many requests. Retry after the number of seconds in Retry-After. |
| 429 | too_many_running | The workspace already has 2 generations in progress. Retry when one completes. |
| 500 | internal | An unexpected error occurred. Retry; if it persists, contact support@synclo.app with the request ID. |
| 503 | unavailable | The generation couldn't be started. Retry shortly; no credits were charged. |
| error.code | Description |
|---|---|
insufficient_credits | Another generation spent the workspace's balance before this one started. |
unavailable | Synclo was temporarily unavailable. Retry the request. |
generation_failed | The presentation couldn't be generated. Retry the request. |
forbidden | The API key's owner is no longer a member of the workspace. |
timed_out | The generation ran longer than 15 minutes. |
internal | An unexpected error occurred. |
Rate limits
Requests are rate limited per workspace, and the limit depends on the plan. Every response reports the current window in these headers:
X-RateLimit-LimitheaderThe number of requests allowed in the current window.
X-RateLimit-RemainingheaderThe number of requests remaining in the current window.
X-RateLimit-ResetheaderWhen the current window resets, as a Unix timestamp.
Retry-AfterheaderOn
429responses: the number of seconds to wait before retrying.
Requests over the limit return 429 rate_limited; retry after Retry-After seconds. Polling GET /generations/{id} has a separate, higher limit, so polling every 5 seconds won’t exhaust it. A workspace can run 2 generations at a time; another returns 429 too_many_running.
HTTP/1.1 200 OK
Synclo-Request-Id: req_Q3m8ZkT1vR6yWb2NcX5aLp9Jd4
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1791817200Idempotency
A network error can hide a response after a generation was created. To retry safely, send an Idempotency-Key with POST /generations: a retry with the same key and body within 24 hours returns the original generation, with Idempotent-Replayed: true, and isn’t charged again.
Reusing a key with a different body returns 409 idempotency_conflict. Use a new key, such as a UUID, for each presentation.
Idempotency-Key: 6f1c9e1a-2b7d-4c55-9d0e-1a2b3c4d5e6fScope
What the API doesn’t do yet, and what to do instead.
- Editing a presentation
- The API creates presentations and documents but doesn't edit them. Generate a new one with revised
input, or opendocument.edit_urlto change it in the Synclo editor. - Files of existing presentations
- A file is generated with the presentation, through
export. Download other files from the editor. - Callbacks
- Synclo doesn't call your server when a generation completes. Poll
GET /generations/{id}. - Social posts and webpages
formatisslidesordocument.
Questions
Anything else: support@synclo.app.
How long does a generation take?
Most complete in one to three minutes. Poll every 5 seconds. A generation still running after 15 minutes fails with timed_out and isn't charged.
How much does a generation cost?
The same as in the app: 10 credits per slide from the workspace's balance, with images on auto included. Other image models may add credits per image. Failed generations aren't charged.
How do I know when a generation is complete?
Poll it. POST /generations returns the generation's id immediately; call GET /generations/{id} every 5 seconds until status is completed or failed.
Can I use the API from Zapier, Make or n8n?
Yes, with their HTTP steps: Webhooks by Zapier (Custom Request), the HTTP module in Make, or the HTTP Request node in n8n. Send the key in the Authorization header, create the generation with a POST, then repeat a GET until status is completed.
Can Claude or ChatGPT make presentations with Synclo?
Yes, through the connector, on every plan, with no API key: the person signs in with their Synclo account. Developers can connect any MCP client to the MCP server.
Which plans include the API?
Pro, Max and Business. Workspace admins create keys in Settings › API; a key uses its workspace's credits.
Who can open the file link?
Anyone with the URL, for 7 days: export.url requires no authentication. Store it as securely as the key, or download the file and serve it yourself.
Can I edit a generated presentation?
Yes, in the Synclo editor: every generated presentation is saved in the workspace, and document.edit_url opens it. The API itself doesn't edit presentations.