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:
- Your agent loop connects to Deckary's partner MCP endpoint with your API key, then creates, reads and writes decks.
- Your backend exchanges the API key for short-lived editor grants. Each grant opens one deck for five minutes.
- 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:
| Item | Example | Used by |
|---|---|---|
| Partner ID | your-partner-id | Your iframe URL |
| API key | dk_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#
| Purpose | URL | Called by |
|---|---|---|
| MCP server | https://deckary.com/partner/mcp | Your agent loop |
| Editor grants | https://deckary.com/api/partner/editor-grant | Your backend |
| Editor iframe | https://deckary.com/embed/editor | Your 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:
| Tool | Purpose |
|---|---|
describe_authoring_vocabulary | Returns the authoring language and rules. Call it before the first author_deck. |
create_deck | Creates an empty deck and returns its deckId. Not idempotent: after an unknown result, check list_decks before retrying. |
read_deck | Returns the current content and revision. |
author_deck | Applies one authoring program, guarded by expectedRevision, and returns images of changed slides. |
render_slides | Returns exact slide images for visual checks. |
render_consulting_visual | Prepares built-in consulting visuals such as charts and frameworks. |
list_decks, read_user_skill | Lists 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 response | Meaning |
|---|---|
200 { token, expiresAt, sessionId } | A grant for that deck, valid for five minutes. expiresAt is in epoch milliseconds. |
| 400 | The body is not {"deckId": "DECK_ID"} with a valid UUID. |
| 401 | The API key is missing or wrong. |
| 404 | The 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.
| Message | Direction | Payload | When |
|---|---|---|---|
deckary:ready | Editor to page | none | The editor has started. |
deckary:grant-request | Editor to page | requestId, deckId | On load, and when the current grant is within a minute of expiry. Answer within 15 seconds. |
deckary:grant | Page to editor | requestId, grant | The JSON from your grant endpoint, unchanged. |
deckary:grant-error | Page to editor | requestId | The user may not open this deck, or your backend failed. |
deckary:open | Page to editor | deckId | Switch the editor to another deck. |
deckary:loaded | Editor to page | deckId | The deck is open and editable. |
deckary:error | Editor to page | deckId | The 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.
- When a workspace needs a presentation, your backend or agent calls
create_deckand stores thedeckId. - Your page renders the editor with that
deckId. It stays visible while the agent works. - The agent calls
read_deck, thenauthor_deckwithexpectedRevisionset to the returnedrevision. Changes appear in the open editor without a reload. - 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. - If
author_deckrejects 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.originandevent.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_deckprograms can take up to 60 seconds. Set your MCP client timeout above that.
Troubleshooting#
| Symptom | Likely cause | Fix |
|---|---|---|
| MCP returns 401 "Invalid partner API key" | Missing or wrong Authorization header | Send Bearer followed by the key exactly, and check that the key has not been revoked. |
| The grant endpoint returns 404 | The deck was created with another key, or the ID is wrong | Use a deckId returned by create_deck or list_decks with your key. |
| The iframe refuses to connect or stays blank | parentOrigin differs from your page origin, or it is not registered | Match window.location.origin exactly, including the port, and register new origins with us. |
| The iframe shows "Not found" | Unknown partner, unregistered parentOrigin, or malformed deckId | Check the three query parameters. |
| The editor says it could not connect | A grant request timed out or received deckary:grant-error | Check your message handler and grant endpoint, and answer within 15 seconds. |
| Agent edits do not appear | The editor shows a different deck, or author_deck returned an error | Compare 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.