Deckly · Developers
Deckly from your code and your agents
A REST API and an MCP server to create, edit, share and export decks, landing pages and ads. The same agent as the app, with the same credits.
Authentication
Create a key in My account → API and send it with every request in the Authorization header. The full key is shown only once; keep it like a password.
The API and the MCP are for Plus and Pro accounts. Each account can have up to 10 keys; apps you connect with OAuth don't count.
- Base URL
https://deckly-backend.onrender.com/v1- Header
Authorization: Bearer dk_live_...
How it works
Generating takes from a few seconds to a couple of minutes, so all AI work runs in the background:
- Request the document with
POST /v1/decks,/v1/landingsor/v1/ads. It answers 202 with the document ingenerating. - Poll
GET /v1/documents/{id}every few seconds untilstatusisready. - If it stops at
needs_input, the agent has questions: answer them withPOST /v1/documents/{id}/answers, or send{}to take the suggested answers. - Once ready, share it with a link, export it, or ask for changes with
/edit.
Examples
Create a deck, wait until it's done and share it. Keep your key in the DECKLY_API_KEY environment variable.
# Create a deck
curl -X POST https://deckly-backend.onrender.com/v1/decks \
-H "Authorization: Bearer $DECKLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Quarterly results for a coffee chain", "slides": 10}'
# Check on it (use the id from the answer above)
curl https://deckly-backend.onrender.com/v1/documents/a1b2c3 \
-H "Authorization: Bearer $DECKLY_API_KEY"import os, time, requests
API = "https://deckly-backend.onrender.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['DECKLY_API_KEY']}"}
doc = requests.post(f"{API}/decks", headers=HEADERS, json={
"prompt": "Quarterly results for a coffee chain",
"slides": 10,
}).json()
while doc["status"] in ("generating", "needs_input"):
if doc["status"] == "needs_input":
# take the agent's suggested answers
doc = requests.post(f"{API}/documents/{doc['id']}/answers",
headers=HEADERS, json={"answers": {}}).json()
time.sleep(5)
doc = requests.get(f"{API}/documents/{doc['id']}", headers=HEADERS).json()
if doc["status"] == "ready":
share = requests.post(f"{API}/documents/{doc['id']}/share", headers=HEADERS).json()
print(share["share_url"])
pdf = requests.get(f"{API}/documents/{doc['id']}/export",
headers=HEADERS, params={"format": "pdf"})
open("deck.pdf", "wb").write(pdf.content)const API = "https://deckly-backend.onrender.com/v1";
const headers = {
Authorization: `Bearer ${process.env.DECKLY_API_KEY}`,
"Content-Type": "application/json",
};
async function call(path, init = {}) {
const res = await fetch(`${API}${path}`, { ...init, headers });
if (!res.ok) throw new Error((await res.json()).detail);
return res.json();
}
let doc = await call("/decks", {
method: "POST",
body: JSON.stringify({ prompt: "Quarterly results for a coffee chain", slides: 10 }),
});
while (doc.status === "generating" || doc.status === "needs_input") {
if (doc.status === "needs_input") {
// take the agent's suggested answers
doc = await call(`/documents/${doc.id}/answers`, {
method: "POST",
body: JSON.stringify({ answers: {} }),
});
}
await new Promise((resolve) => setTimeout(resolve, 5000));
doc = await call(`/documents/${doc.id}`);
}
if (doc.status === "ready") {
const { share_url } = await call(`/documents/${doc.id}/share`, { method: "POST" });
console.log(share_url);
}Endpoints
Every path hangs from the base URL and answers JSON, except export, which returns a file.
GET
/v1/meYour account: plan and available credits.
POST
/v1/decksCreate a deck.
slidesfrom 2 to 25, optionallanguage.POST
/v1/landingsCreate a landing page.
POST
/v1/adsCreate social media ads.
GET
/v1/documentsYour documents, newest first. Filter by
typeand page withlimitandoffset.GET
/v1/documents/{id}One document and its status.
POST
/v1/documents/{id}/answersAnswer the agent's questions:
{question_id: option index or text}.{}takes the suggested ones.POST
/v1/documents/{id}/editAsk for a change in plain language.
slide(0-based) limits it to one slide.POST
/v1/documents/{id}/stopStop the work in progress.
POST
/v1/documents/{id}/shareCreate the public read-only link.
DELETE
/v1/documents/{id}/shareRemove the public link.
GET
/v1/documents/{id}/exportDownload the file:
pdforimages(a zip of PNGs) for decks and ads;htmlfor landing pages.DELETE
/v1/documents/{id}Move it to the trash; it can be restored from the app for a few days.
The document
Almost every path returns the same shape, whether it's a deck, a landing page or an ad:
{
"id": "a1b2c3",
"type": "deck",
"title": "Quarterly results",
"status": "needs_input",
"questions": [
{
"id": "q1",
"text": "Who is the audience?",
"options": ["Investors", "The team"],
"default_index": 0
}
],
"slides": 10,
"share_url": null,
"editor_url": "https://www.decklyai.com/deck/a1b2c3",
"updated_at": "2026-10-06T18:20:00Z"
}status:generatingwhile it works,needs_inputwhen it's waiting for answers,readywhen it's done,failedorstopped.questionsonly has items whenstatusisneeds_input.share_urlisnulluntil you share it;editor_urlopens it in the app.
Errors and limits
An error returns its HTTP status and {detail, code}. detail comes in the Accept-Language language and can be shown as is; to branch in your code, read code.
- 120 requests per minute per key. Above that, 429
api_rate_limitedwithRetry-After. - Up to 2 AI jobs at once per account, same as in the app.
- 403
api_plan_requiredif the account is no longer Plus or Pro.
Credits
The API charges exactly like the app: the same price, held when the work starts and settled when it ends. Credits come from your plan and your top-ups.
max_credits caps what one request can spend. If you don't send it, the cap is the price quoted for that request.
MCP
The MCP server gives claude.ai, ChatGPT, Claude Code, Cursor and other AI clients the same actions as the API. It speaks streamable HTTP and authenticates two ways: by signing in with your Deckly account (OAuth, nothing to copy) or with a key.
- Endpoint
https://deckly-backend.onrender.com/mcp- Header
Authorization: Bearer dk_live_...
claude.ai and the Claude app
It connects as a connector, with no keys: you sign in with your Deckly account and allow it.
- In claude.ai, open
Settings → Connectorsand chooseAdd custom connector. - Name it Deckly and paste the URL
https://deckly-backend.onrender.com/mcp. - Claude opens Deckly: sign in with your account and click
Allow. That's it, you can use it in your chats.
ChatGPT
- In ChatGPT, open
Settings → Apps & Connectorsand turn on developer mode in the advanced settings. - Create a connector with the URL
https://deckly-backend.onrender.com/mcpand pickOAuthas the authentication. - ChatGPT opens Deckly: sign in with your account and click
Allow.
Every app you connect shows up in My account → API as a connector; disconnect it from there anytime. It spends your credits the same way the app does, and it's for Plus and Pro accounts.
Claude Code
No key needed: add the server and, when Claude Code asks, sign in with your Deckly account and click Allow.
claude mcp add --transport http deckly https://deckly-backend.onrender.com/mcpOr with a key, in one command:
claude mcp add --transport http deckly https://deckly-backend.onrender.com/mcp --header "Authorization: Bearer dk_live_..."Cursor
In ~/.cursor/mcp.json:
{
"mcpServers": {
"deckly": {
"url": "https://deckly-backend.onrender.com/mcp",
"headers": {
"Authorization": "Bearer dk_live_..."
}
}
}
}Other clients
Any client that takes an HTTP MCP server with headers:
{
"mcpServers": {
"deckly": {
"type": "http",
"url": "https://deckly-backend.onrender.com/mcp",
"headers": {
"Authorization": "Bearer dk_live_..."
}
}
}
}Tools
create_deckcreate_landingcreate_adsget_documentwait_for_documentlist_documentsedit_documentanswer_questionsshare_documentexport_documentget_account
wait_for_document waits up to about 50 seconds for the status to change, so the agent doesn't have to keep asking.
Studio mode: your agent designs
With the MCP connected, your own agent (Claude Code, Cursor, Codex...) becomes Deckly's designer. It works with the same design tools Deckly's agent uses for decks, landing pages and ads: it writes each slide, places photos, sees how it really looks and fixes it.
It's different from create_deck, create_landing and create_ads: there you hand a request to Deckly's agent and it designs. In studio mode your agent designs, and Deckly gives it the tools.
How a session goes
- Your agent starts an empty document with
start_deck,start_landingorstart_ads, or picks up one you already have by its id. - It reads
design_guide: the same design brief Deckly's agent follows. - It builds with the
deck_*,landing_*orads_*tools.look_atshows it how it really renders, so it can fix what came out wrong. - It wraps up with
finish_workand a summary. Everything it did is saved as one change in the editor's chat, and you can undo it in one step.
- While your agent works, the document is its alone: the app's chat and editor wait until it's done. After 15 minutes with no activity, it's released on its own.
- Your agent's own thinking doesn't spend Deckly credits; only what Deckly does along the way is charged, like generating images or picking photos.
Tools
- To start and finish
start_deckstart_landingstart_adsdesign_guidefinish_work
- Decks
deck_list_slidesdeck_read_slidedeck_write_slidedeck_edit_slidedeck_insert_slidedeck_delete_slidedeck_move_slidedeck_read_stylesdeck_write_stylesdeck_edit_stylesdeck_set_titledeck_piece_guidedeck_look_atdeck_find_imagedeck_generate_imagedeck_researchdeck_read_sourcedeck_search_projectdeck_read_project_filedeck_undo_turn
- Landing pages
landing_read_pagelanding_write_pagelanding_edit_pagelanding_look_atlanding_researchlanding_set_title
- Ads
ads_list_piecesads_read_pieceads_write_pieceads_edit_pieceads_delete_pieceads_set_fontsads_look_atads_researchads_set_title
Ask your agent
With the MCP set up, something like this is enough:
Using Deckly, design a 6-slide deck about the history of coffee in Mexico yourself. Check how every slide looks before you finish, and send me the share link.Coming soon: PPTX export.