Browse documentation

Embed Deckary in your product

Let your own AI agent build presentations through Deckary MCP while your users edit the same deck in an embedded Deckary editor.

Last verified: 2026-10-02

Platforms that run their own AI agent can use Deckary as their presentation layer. Your agent writes slides through Deckary's MCP server with a server-side API key, and your users edit the same deck in a Deckary editor embedded in your page. Agent changes appear in the open editor within about a second, and the agent reads your users' edits back on its next read.

This integration is for platforms that call Deckary on behalf of their own users. If you are connecting your own AI client to your own Deckary account, use Getting started instead.

How it fits together#

The integration has three parts:

  1. Your agent loop connects to Deckary's partner MCP endpoint with your API key, then creates, reads and writes decks.
  2. Your backend exchanges the API key for short-lived editor grants. Each grant opens one deck for five minutes.
  3. Your page keeps the Deckary editor in an iframe and passes it grants through postMessage.

The API key never reaches a browser. The browser only receives deck-scoped grants, which the editor renews through your page while it stays open.

Get access#

Partner access is enabled per platform. Contact us to get started. Deckary provides:

ItemExampleUsed by
Partner IDyour-partner-idYour iframe URL
API keydk_partner_your-partner-id_…Your agent and backend only

Send us every origin that will host the editor, including staging and local HTTPS origins, for example https://app.example.com. Wildcards such as https://*.example.com are supported. Only HTTPS origins work.

Endpoints#

PurposeURLCalled by
MCP serverhttps://deckary.com/partner/mcpYour agent loop
Editor grantshttps://deckary.com/api/partner/editor-grantYour backend
Editor iframehttps://deckary.com/embed/editorYour page

Step 1: Connect your agent to MCP#

Use any MCP client with the Streamable HTTP transport and the header Authorization: Bearer YOUR_API_KEY. There is no OAuth flow, and the server is stateless.

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const deckary = new Client({ name: "your-agent", version: "1.0.0" });
await deckary.connect(
  new StreamableHTTPClientTransport(new URL("https://deckary.com/partner/mcp"), {
    requestInit: { headers: { Authorization: `Bearer ${process.env.DECKARY_API_KEY}` } },
  }),
);

const { tools } = await deckary.listTools(); // give these to your model
const created = await deckary.callTool({ name: "create_deck", arguments: { title: "Q3 review" } });
const deckId = created.structuredContent.deckId; // store this with your workspace

If your agent runs on the Claude Messages API, you can pass the server to its MCP connector instead: {"type": "url", "url": "https://deckary.com/partner/mcp", "name": "deckary", "authorization_token": "YOUR_API_KEY"}.

The tools your agent needs:

ToolPurpose
describe_authoring_vocabularyReturns the authoring language and rules. Call it before the first author_deck.
create_deckCreates an empty deck and returns its deckId. Not idempotent: after an unknown result, check list_decks before retrying.
read_deckReturns the current content and revision.
author_deckApplies one authoring program, guarded by expectedRevision, and returns images of changed slides.
render_slidesReturns exact slide images for visual checks.
render_consulting_visualPrepares built-in consulting visuals such as charts and frameworks.
list_decks, read_user_skillLists decks created with your key and reads saved skills.

Ignore open_deck_editor, open_editor_session and the url field in tool results. They serve Deckary's own apps; your users see decks through the embedded editor. The tool reference and Web Builder decks pages describe the authoring tools in more detail.

Step 2: Issue editor grants from your backend#

When the editor needs access, it asks your page for a grant. Your page forwards the request to your backend, which checks that the signed-in user may open that deck and then requests a grant from Deckary.

// Express example: POST /deckary/grant with body { deckId }
app.post("/deckary/grant", requireUser, async (req, res) => {
  const { deckId } = req.body;
  if (!(await userCanEditDeck(req.user, deckId))) return res.sendStatus(403); // your rule
  const response = await fetch("https://deckary.com/api/partner/editor-grant", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.DECKARY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ deckId }),
  });
  if (!response.ok) return res.sendStatus(response.status);
  res.set("Cache-Control", "no-store").json(await response.json());
});
Deckary responseMeaning
200 { token, expiresAt, sessionId }A grant for that deck, valid for five minutes. expiresAt is in epoch milliseconds.
400The body is not {"deckId": "DECK_ID"} with a valid UUID.
401The API key is missing or wrong.
404The deck does not exist or was not created with your key.

Issue a fresh grant for every request. Do not cache grants or share them between users or decks.

Step 3: Embed the editor#

Mount one iframe and keep it mounted while users work. Your page must be served over HTTPS from an origin registered with Deckary, and parentOrigin must equal your page's window.location.origin exactly. Deckary allows only that origin to frame the editor.

<iframe
  id="deckary-editor"
  title="Presentation editor"
  style="width:100%;height:100%;min-height:640px;border:0"
  src="https://deckary.com/embed/editor?partner=your-partner-id&parentOrigin=https%3A%2F%2Fapp.example.com&deckId=DECK_ID"
></iframe>
const DECKARY_ORIGIN = "https://deckary.com";
const frame = document.getElementById("deckary-editor") as HTMLIFrameElement;

window.addEventListener("message", async (event) => {
  if (event.origin !== DECKARY_ORIGIN || event.source !== frame.contentWindow) return;
  const message = event.data;
  if (message.type === "deckary:grant-request") {
    const response = await fetch("/deckary/grant", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ deckId: message.deckId }),
    });
    frame.contentWindow!.postMessage(
      response.ok
        ? { type: "deckary:grant", requestId: message.requestId, grant: await response.json() }
        : { type: "deckary:grant-error", requestId: message.requestId },
      DECKARY_ORIGIN,
    );
  }
});

// Show another deck without reloading the iframe. Pending edits are saved first.
export const openDeck = (deckId: string) =>
  frame.contentWindow!.postMessage({ type: "deckary:open", deckId }, DECKARY_ORIGIN);

You can leave deckId out of the iframe URL and send deckary:open once you know it. If your page sets a Content Security Policy frame-src, add https://deckary.com.

MessageDirectionPayloadWhen
deckary:readyEditor to pagenoneThe editor has started.
deckary:grant-requestEditor to pagerequestId, deckIdOn load, and when the current grant is within a minute of expiry. Answer within 15 seconds.
deckary:grantPage to editorrequestId, grantThe JSON from your grant endpoint, unchanged.
deckary:grant-errorPage to editorrequestIdThe user may not open this deck, or your backend failed.
deckary:openPage to editordeckIdSwitch the editor to another deck.
deckary:loadedEditor to pagedeckIdThe deck is open and editable.
deckary:errorEditor to pagedeckIdThe deck could not be opened. The editor shows a reload hint.

Work with decks#

Create each deck once, store its deckId with the workspace or conversation it belongs to, and keep the editor pointed at it.

  1. When a workspace needs a presentation, your backend or agent calls create_deck and stores the deckId.
  2. Your page renders the editor with that deckId. It stays visible while the agent works.
  3. The agent calls read_deck, then author_deck with expectedRevision set to the returned revision. Changes appear in the open editor without a reload.
  4. Users edit in the editor at the same time. Their edits save to the same deck, and the agent sees them on its next read_deck.
  5. If author_deck rejects a stale revision because someone edited in between, the agent reads the deck again and rebuilds its change from the current content. It should not resend the same program.

Users can download a native, editable PowerPoint file with Export PowerPoint in the editor.

Security checklist#

  • Keep the API key in a secrets manager. It must never reach a browser, a log line or a client bundle.
  • Your grant endpoint requires a signed-in user and checks that user's access to the requested deck. Deckary does not know your users, so this check is your access control.
  • Your message handler checks both event.origin and event.source, and always posts to the explicit Deckary origin, never "*".
  • Grants travel only through postMessage, never in URLs, query strings or logs.
  • To rotate a key, ask us for a second key, deploy it, then ask us to revoke the old one. Both keys work during the overlap.

See Security and data handling for how Deckary validates and stores presentation data.

Current limits#

  • All decks belong to your platform's Deckary account, and edits made in the embedded editor are attributed to that account rather than to individual users.
  • The embedded editor covers manual editing, images and logos, consulting visuals, theme import and PowerPoint export. Deckary's built-in AI assistant, sharing and version restore are not part of the embed, because your agent provides the AI.
  • The editor is built for desktop use. Give it as much width as your layout allows; narrow frames compact the toolbar and side panels.
  • Large author_deck programs can take up to 60 seconds. Set your MCP client timeout above that.

Troubleshooting#

SymptomLikely causeFix
MCP returns 401 "Invalid partner API key"Missing or wrong Authorization headerSend Bearer followed by the key exactly, and check that the key has not been revoked.
The grant endpoint returns 404The deck was created with another key, or the ID is wrongUse a deckId returned by create_deck or list_decks with your key.
The iframe refuses to connect or stays blankparentOrigin differs from your page origin, or it is not registeredMatch window.location.origin exactly, including the port, and register new origins with us.
The iframe shows "Not found"Unknown partner, unregistered parentOrigin, or malformed deckIdCheck the three query parameters.
The editor says it could not connectA grant request timed out or received deckary:grant-errorCheck your message handler and grant endpoint, and answer within 15 seconds.
Agent edits do not appearThe editor shows a different deck, or author_deck returned an errorCompare the deck IDs, then read the tool result's issues and repair the program.

For anything else, contact us with the deckId, the time in UTC and the failing request's status code.