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:

  1. Request the document with POST /v1/decks, /v1/landings or /v1/ads. It answers 202 with the document in generating.
  2. Poll GET /v1/documents/{id} every few seconds until status is ready.
  3. If it stops at needs_input, the agent has questions: answer them with POST /v1/documents/{id}/answers, or send {} to take the suggested answers.
  4. 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.

curl
# 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"
Python
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)
JavaScript
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/me

    Your account: plan and available credits.

  • POST/v1/decks

    Create a deck. slides from 2 to 25, optional language.

  • POST/v1/landings

    Create a landing page.

  • POST/v1/ads

    Create social media ads.

  • GET/v1/documents

    Your documents, newest first. Filter by type and page with limit and offset.

  • GET/v1/documents/{id}

    One document and its status.

  • POST/v1/documents/{id}/answers

    Answer the agent's questions: {question_id: option index or text}. {} takes the suggested ones.

  • POST/v1/documents/{id}/edit

    Ask for a change in plain language. slide (0-based) limits it to one slide.

  • POST/v1/documents/{id}/stop

    Stop the work in progress.

  • POST/v1/documents/{id}/share

    Create the public read-only link.

  • DELETE/v1/documents/{id}/share

    Remove the public link.

  • GET/v1/documents/{id}/export

    Download the file: pdf or images (a zip of PNGs) for decks and ads; html for 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:

Document
{
  "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: generating while it works, needs_input when it's waiting for answers, ready when it's done, failed or stopped.
  • questions only has items when status is needs_input.
  • share_url is null until you share it; editor_url opens 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_limited with Retry-After.
  • Up to 2 AI jobs at once per account, same as in the app.
  • 403 api_plan_required if 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.

See plans and credits →

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.

  1. In claude.ai, open Settings → Connectors and choose Add custom connector.
  2. Name it Deckly and paste the URL https://deckly-backend.onrender.com/mcp.
  3. Claude opens Deckly: sign in with your account and click Allow. That's it, you can use it in your chats.

ChatGPT

  1. In ChatGPT, open Settings → Apps & Connectors and turn on developer mode in the advanced settings.
  2. Create a connector with the URL https://deckly-backend.onrender.com/mcp and pick OAuth as the authentication.
  3. 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.

Terminal
claude mcp add --transport http deckly https://deckly-backend.onrender.com/mcp

Or with a key, in one command:

Terminal
claude mcp add --transport http deckly https://deckly-backend.onrender.com/mcp --header "Authorization: Bearer dk_live_..."

Cursor

In ~/.cursor/mcp.json:

~/.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:

JSON
{
  "mcpServers": {
    "deckly": {
      "type": "http",
      "url": "https://deckly-backend.onrender.com/mcp",
      "headers": {
        "Authorization": "Bearer dk_live_..."
      }
    }
  }
}

Tools

  • create_deck
  • create_landing
  • create_ads
  • get_document
  • wait_for_document
  • list_documents
  • edit_document
  • answer_questions
  • share_document
  • export_document
  • get_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

  1. Your agent starts an empty document with start_deck, start_landing or start_ads, or picks up one you already have by its id.
  2. It reads design_guide: the same design brief Deckly's agent follows.
  3. It builds with the deck_*, landing_* or ads_* tools. look_at shows it how it really renders, so it can fix what came out wrong.
  4. It wraps up with finish_work and 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_deck
  • start_landing
  • start_ads
  • design_guide
  • finish_work
Decks
  • deck_list_slides
  • deck_read_slide
  • deck_write_slide
  • deck_edit_slide
  • deck_insert_slide
  • deck_delete_slide
  • deck_move_slide
  • deck_read_styles
  • deck_write_styles
  • deck_edit_styles
  • deck_set_title
  • deck_piece_guide
  • deck_look_at
  • deck_find_image
  • deck_generate_image
  • deck_research
  • deck_read_source
  • deck_search_project
  • deck_read_project_file
  • deck_undo_turn
Landing pages
  • landing_read_page
  • landing_write_page
  • landing_edit_page
  • landing_look_at
  • landing_research
  • landing_set_title
Ads
  • ads_list_pieces
  • ads_read_piece
  • ads_write_piece
  • ads_edit_piece
  • ads_delete_piece
  • ads_set_fonts
  • ads_look_at
  • ads_research
  • ads_set_title

Ask your agent

With the MCP set up, something like this is enough:

Prompt
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.