On this page
Studio in an iframe
Offer DokKraft document editing inside your own product: the studio loads in an iframe on your domains, authenticated by a short-lived token — your API key never leaves your server.
Integration flow
- Your backend calls
POST /api/v1/embed/sessionswith itsdfk_key. - The API returns a signed
url(default: 2 hours). - Your front loads the URL into an
<iframe>from an origin declared inallowed_domains. - The studio and your page talk through
postMessage.
Create the session
curl -X POST "https://dokkraft.blocsland.com/api/v1/embed/sessions" \
-H "Authorization: Bearer dfk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_id": "TEMPLATE_PUBLIC_ID",
"allowed_domains": ["https://app.your-product.com"],
"ttl_minutes": 120,
"permissions": { "variables": "frozen", "ai": false }
}'
201 response:
{
"url": "https://dokkraft.blocsland.com/embed/studio/dem_…",
"token": "dem_…",
"expires_at": "2026-09-14T12:00:00+00:00",
"permissions": { "variables": "frozen", "ai": false, "save": true, "export_pdf": true }
}
Permissions
| Permission | Default | Effect |
|---|---|---|
variables |
frozen |
Variables frozen (frozen) or editable (editable) in the iframe |
ai |
false |
AI assistant available |
save |
true |
Template saving allowed |
export_pdf |
true |
PDF export allowed |
postMessage bridge
Expected message sources: dokkraft-embed (iframe → parent) and dokkraft-host (parent → iframe). Always filter on event.origin.
| Direction | Type | Payload |
|---|---|---|
| parent → iframe | save |
{} — triggers saving |
| parent → iframe | generate |
{} — generates the PDF |
| parent → iframe | ping |
{} — liveness check |
| iframe → parent | ready |
{} — studio loaded |
| iframe → parent | dirty |
{ dirty: boolean } (debounced) |
| iframe → parent | saved |
{ templateId, name, created } |
| iframe → parent | generated |
{ dataUrl, filename } |
| iframe → parent | state |
{ dirty, templateId, name, isVariant } |
| iframe → parent | error |
{ code?, added?, removed?, message } |
| iframe → parent | session.revoked |
{ reason: "revoked" } — session revoked in real time (WebSocket) |
| iframe → parent | session.expired |
{ reason: "expired" } — TTL exceeded |
The session.revoked and session.expired messages arrive without any action on your side: the session was closed (revocation via DELETE /api/v1/embed/sessions/{token}, TTL or API key expiry). The iframe locks itself — hide your buttons and issue a new session. TTL expiry is tracked by a local timer inside the iframe; DokKraft additionally pushes session.expired over WebSocket when the tab was asleep at TTL time (server-side detection every minute). |
// Parent → iframe (origins filtered on both sides)
iframe.contentWindow.postMessage(
{ source: 'dokkraft-host', type: 'save' },
new URL(iframe.src).origin,
);
// iframe → parent
window.addEventListener('message', (event) => {
if (event.origin !== expected) return;
if (event.data?.source !== 'dokkraft-embed') return;
if (event.data.type === 'generated') download(event.data.dataUrl);
});
Security
- The
dem_…token is short-lived (5 to 1440 minutes) and carries no session. - Origins are verified on both sides: never put a wildcard in
allowed_domains. - A frozen template (
variables: frozen) only allows editing the content, not the structure.