Skip to content

External embeds: workflow runtime and editor

This document is the canonical integration guide for embedding Madoo in external systems. It covers both browser surfaces:

  • Workflow runtime embed: an end-user widget that runs one published workflow.
  • Workflow editor embed: an embedded editor that can create, save, edit, and run workflows in a workspace.

Use this document whenever code changes touch embed token creation, embed authentication, iframe routes, or postMessage contracts.


External integrations must never expose a Madoo API key or OAuth client secret in the browser. The host application uses its backend to mint a narrow embed token, then passes only that embed token to the iframe.

External backend External browser Madoo
---------------- ---------------- -----
POST /api/v1/auth/token
client_id + client_secret
|
v
POST /api/v1/embed/.../tokens -> returns embed token
|
v
Render iframe with token -> /embed/... or /embed/editor
|
v
Communicate with iframe -> window.postMessage(...)

There are two token types:

Token type Endpoint JWT subject Scope Main use
Runtime embed token POST /api/v1/embed/tokens embed One workflow, optional interface, org, workspace, allowed origins End-user workflow execution
Editor embed token POST /api/v1/embed/editor/tokens embed_editor Org, workspace, roles, permissions, allowed origins Embedded workflow authoring

Both token types are JWTs signed by Madoo. Both can be revoked by JTI through the same revocation endpoint.

Declarative Apps: this contract still covers only workflow runtime and workflow editor embeds. Declarative Apps are not embeddable in V1. Their manifest uses minimumRendererVersion internally to reject unsupported layout capabilities safely; it does not change the URLs, tokens, or postMessage messages described here. A future OR-14 App embed must define its own explicit version handshake instead of implicitly reusing this workflow contract.

The Declarative App editor’s explained hidden-element placeholders are also an internal authoring adapter. They do not change iframe rendering, embed tokens, postMessage, or customer-runtime visibility: surfaces without that adapter continue to omit conditionally hidden elements completely.

The contextual Logic panel and authoring contract 2.21 are likewise internal App-authoring surfaces. Editing an operation’s declarative success outcomes changes the next hosted App manifest after save/publish, but adds no iframe command, token claim, external URL, or postMessage vocabulary to this V1 embed contract.

The editor’s copied /apps/{appId} URL is not an external share or embed URL. It opens the hosted Declarative App route for an authenticated user whose current workspace grants App use access. Anonymous and cross-workspace customer distribution remain outside the current contract.


First obtain a normal Public API bearer token with client credentials:

Terminal window
BASE_URL="https://testing-api.madoo.ai"
TOKEN=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials" \
"$BASE_URL/api/v1/auth/token" | jq -r .access_token)

The API key behind CLIENT_ID / CLIENT_SECRET determines the organization and workspace used by the embed token. The workspace is not accepted from the browser or from the embed-token request body.

Required permissions:

Operation Required permission
Create runtime workflow token ws:executions:create (WsExecutionsCreate)
Create editor token ws:workflows:manage (WsWorkflowsManage)
Run from embedded editor ws:executions:create must also be present in the embedded context
Save/create workflows from embedded editor ws:workflows:manage must be present in the embedded context

For runtime tokens, Madoo copies only the narrow permissions needed by the iframe, and only when the current API key / request context already has them: ws:workflows:read for workflow metadata, ws:assets:read for reading runtime assets, ws:assets:manage for runtime asset upload/management, ws:executions:create for starting workflow runs, and ws:executions:read for polling runtime execution status and outputs. This lets the runtime iframe access the required APIs without broadening privileges.

For editor tokens, Madoo copies the parent API-key context into the embed token: owner user id, email verification state, organization role, workspace role, and explicit permissions. This is intentional: the embedded editor calls normal workspace APIs, so normal authorization middleware must be able to resolve the same effective permissions.


Use a runtime embed when an external end user should fill workflow inputs and generate outputs, without seeing the Madoo editor.

POST /api/v1/embed/tokens
Authorization: Bearer <public_api_access_token>
Content-Type: application/json

Request body:

Field Type Required Notes
workflow string yes Published workflow id. Accepts wf_... or a raw GUID.
allowed_origins string[] yes External origins allowed to host the iframe, for example https://app.example.com.
interface string or null no Custom interface id. Omit or null for the default workflow interface.
expires_in_minutes integer or null no >= 1. Null means practical never-expiry (100-year JWT expiry).
max_executions integer or null no >= 1. Null means unlimited.
prefilled_inputs object or null no Reserved in the token contract; runtime UI initialization should currently use madoo:init / madoo:setValues.
end_user_id string or null no Max 128 chars. Used for audit/correlation and end-user execution isolation.

Example:

Terminal window
WF="wf_53fb2f6576fa4bc9a6d3c91a7e84de47"
curl -s -X POST "$BASE_URL/api/v1/embed/tokens" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workflow": "'"$WF"'",
"interface": "simple",
"allowed_origins": ["https://app.example.com"],
"expires_in_minutes": 60,
"max_executions": 25,
"end_user_id": "customer-10472"
}'

Response:

{
"token": "eyJhbGciOi...",
"jti": "emb_7f2e...",
"expires_at": "2026-06-18T15:30:00+00:00"
}

Store jti server-side if you may need to revoke the token before expiry.

Runtime embed route:

{APP_BASE_URL}/embed/{workflowGuid}/{interfaceId?}?token={embed_token}

Supported query parameters:

Parameter Values Notes
token JWT Required. Runtime embed token returned by POST /api/v1/embed/tokens.
interface string Optional fallback interface id when not present in the path.
theme light, dark Optional initial theme class.
primaryColor CSS color Optional initial primary/accent color, applied before iframe readiness.
locale locale code Optional, for example it or en. Unsupported locales fall back to English.
layout default, compact, inline, headless Optional UI layout.
compact true Shortcut for compact layout.
hideHeader true Hide the workflow/interface header.
hideExecuteButton true Reserved by init payload; not all interface renderers expose a separate button.
hidePoweredBy true Hide the powered-by label.
hideProgress true Hide default progress block when applicable.
hideOutput true Hide progress/output area.
showName false Hide the workflow/interface name while keeping the rest of the embed UI.
showBranding false Hide Madoo branding references, including the powered-by label.

Custom CSS should be sent with postMessage (madoo:init payload css or madoo:setCustomCss) after the iframe loads. Do not place full CSS in the URL.

Example:

<iframe
id="madoo-runtime"
src="https://app.madoo.ai/embed/53fb2f65-76fa-4bc9-a6d3-c91a7e84de47/simple?embed=true&token=EMBED_TOKEN&locale=it&layout=compact"
style="width:100%;border:0;"
allow="clipboard-read; clipboard-write"
></iframe>

The hosted SDK can create and manage the runtime iframe for you: It always appends embed=true to the iframe URL so Madoo runs in embed mode and does not trigger the normal refresh-token flow.

<div id="madoo-runtime"></div>
<script src="https://app.madoo.ai/embed/sdk/madoo-embed.js"></script>
<script>
const runtime = MadooEmbed.create({
container: "#madoo-runtime",
workflow: "53fb2f65-76fa-4bc9-a6d3-c91a7e84de47",
interface: "simple",
token: "EMBED_TOKEN",
locale: "it",
layout: "compact",
customCss: ".madoo-embed { font-family: Inter, sans-serif; }",
onExecutionCompleted: (data) => console.log(data.outputs),
});
</script>

If your backend already returns the clean runtime embed URL, pass it as iframeUrl and pass the token separately:

MadooEmbed.create({
container: "#madoo-runtime",
iframeUrl: "https://app.madoo.ai/embed/53fb2f65-76fa-4bc9-a6d3-c91a7e84de47",
token: "EMBED_TOKEN",
});

The runtime page bootstraps by calling:

Internal call Purpose
GET /api/embed/config Validates token, resolves workflow GUID to internal workflow id, returns metadata and token execution cap.
POST /api/embed/check-execution Increments/checks the per-token execution counter when max_executions is set.
Standard workflow/execution APIs Load definition/interface, create execution, poll status, load outputs. The embed token is used as bearer auth.

If a design/template_render node has the opt-in latest_compatible policy, a new runtime embed execution can select a compatible published template revision. Its effective pin is stored in that execution’s immutable definition snapshot. The runtime embed does not save or version the workflow. A permanent pin change belongs to authoring in the embedded editor or another authorized authoring surface; a released App binding remains frozen.

EmbedAuthMiddleware validates runtime tokens on /embed/* and /api/embed/*, sets a scoped principal, stores EmbedClaims in HttpContext.Items, and adds Content-Security-Policy: frame-ancestors 'self' ... for embed page requests.

Parent pages send commands to the iframe with:

iframe.contentWindow?.postMessage(
{
type: "madoo:init",
payload: { locale: "it", values: { prompt: "Summer campaign" } },
},
"https://app.madoo.ai",
);

Supported parent-to-iframe commands:

Message type Payload Effect
madoo:init EmbedInitPayload Applies initial theme, locale, CSS, values, visibility flags, and layout.
madoo:setValues object Sets input values by field key. Values are JSON-encoded internally by the iframe.
madoo:execute none Starts the workflow execution with current inputs.
madoo:reset none Clears execution state and input values.
madoo:setTheme EmbedTheme Applies CSS variables and light/dark mode.
madoo:setLocale { "locale": "it" } Changes iframe language.
madoo:setCustomCss { "css": "..." } Injects sanitized custom CSS. Blocks url(...), @import, javascript:, and expression(...).

Values preserve their JSON scalar type. In particular, an input/number field must receive a finite JavaScript number such as 60, not the string "60"; an input/boolean field must receive true or false, not text. This is the same strict contract used by editor runs, REST v1, MCP, AI Agent and AI Assistant. It lets reusable workflows safely expose controls such as highlight duration and optional subtitle generation without surface-specific coercion.

An input/json_value field receives one complete JavaScript object, array or scalar and preserves it as one structured value, including when the runtime stores a large payload outside the execution row. It does not create iterations. Use input/json only when the configured array members must fan out into separate executions. The distinction is identical in an embedded runtime and in a direct REST v1 call.

EmbedInitPayload shape:

type EmbedInitPayload = {
theme?: EmbedTheme;
locale?: string;
values?: Record<string, unknown>;
css?: string;
lockedFields?: string[];
hiddenFields?: string[];
hideHeader?: boolean;
hideExecuteButton?: boolean;
hidePoweredBy?: boolean;
hideProgress?: boolean;
hideOutput?: boolean;
showName?: boolean;
showBranding?: boolean;
compact?: boolean;
layout?: "default" | "compact" | "inline" | "headless";
};

EmbedTheme shape:

type EmbedTheme = {
mode?: "light" | "dark" | "auto";
primaryColor?: string;
backgroundColor?: string;
surfaceColor?: string;
textColor?: string;
mutedColor?: string;
borderColor?: string;
successColor?: string;
errorColor?: string;
borderRadius?: string;
fontFamily?: string;
};

The iframe posts events to the parent with { type, payload }.

Always validate event.origin in the parent page before trusting the payload.

window.addEventListener("message", (event) => {
if (event.origin !== "https://app.madoo.ai") return;
const { type, payload } = event.data ?? {};
if (type === "madoo:execution:completed") {
console.log(payload.outputs);
}
});

Runtime iframe-to-parent events:

Message type Payload When emitted
madoo:ready { version, fields } Config and workflow definition are loaded.
madoo:resize { width, height } Content size changes. Use it to adjust iframe height.
madoo:values:changed { fieldKey, value, allValues } A field value changes.
madoo:validation { valid, errors } Validation event. Reserved for validation-capable UI paths.
madoo:execution:started { executionId } Execution was created.
madoo:execution:progress { executionId, progress, status } Execution status/progress changed while non-terminal.
madoo:execution:completed { executionId, outputs } Execution completed or partially succeeded.
madoo:execution:failed { executionId, error, code } Execution failed or was cancelled.
madoo:error { code, message } Runtime command or execution startup error.

madoo:error codes. message is a human, end-user-facing string, safe to display as-is; where Madoo produces a localized message (e.g. missing_required_inputs, invalid_execution_inputs) it follows the embed locale. Known code values: missing_required_inputs (a required input was left empty; message names the field(s) to fill in), invalid_execution_inputs (a provided input is not accepted — an unrecognized key and/or a value that is not JSON-encoded; message names the offending input(s)), upload_pending (an asset upload is still in progress — wait, then retry), execution_cap_exceeded (the embed’s execution limit was reached), and execution_failed (any other startup error). Branch on code for custom handling; new codes may be added over time, so treat unknown codes as a generic error.

The hosted runtime and embedded editor present a concise error summary in their primary UI. Low-level provider bodies, FFmpeg diagnostics, memory addresses and local worker paths are never rendered as the headline; in the editor they remain available only inside an explicit Technical details disclosure for authorized troubleshooting. Integrators should apply the same summary-first pattern to any error payload they render themselves.

Video previews and seeking. Madoo-provided video URLs support browser byte-range requests, so an embedded native <video> can start from metadata and seek without downloading the whole asset first. If an integrator proxies an output URL through its own server or CDN, that layer must preserve the request Range header and the 206 Partial Content, Content-Range and Accept-Ranges: bytes response semantics. A custom player cannot compensate for a proxy that collapses range requests into full 200 OK responses.

Skipped outputs. Each entry in payload.outputs carries a presence of "present" or "absent". An "absent" output was skipped because an optional input was not provided (or an upstream node was skipped); it has no url/value. The run still completes successfully — check presence before rendering, and skip the absent ones. See Reading outputs › Optional inputs & skipping.

Iterative runs. A run whose output nodes iterate (fan-out over a list) delivers one entry per iteration in payload.outputs — potentially dozens or hundreds. Entries carry a logical_name (the author-facing output name) alongside the unique name ({logical}_{iteration}_{nodeId}): group by logical_name when rendering. Runs executed before this capability shipped return an empty list for iterative outputs.

Auto-resize example:

const iframe = document.querySelector<HTMLIFrameElement>("#madoo-runtime");
window.addEventListener("message", (event) => {
if (event.origin !== "https://app.madoo.ai") return;
if (event.data?.type !== "madoo:resize") return;
iframe!.style.height = `${event.data.payload.height}px`;
});

Use an editor embed when an external application should let a workspace user author workflows inside your own UI.

POST /api/v1/embed/editor/tokens
Authorization: Bearer <public_api_access_token>
Content-Type: application/json

Request body:

Field Type Required Notes
allowed_origins string[] yes Origins allowed to host the editor iframe.
expires_in_minutes integer or null no >= 1. Null means practical never-expiry.
end_user_id string or null no Max 128 chars. Audit/correlation id for the external user.

Example:

Terminal window
curl -s -X POST "$BASE_URL/api/v1/embed/editor/tokens" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"allowed_origins": ["https://admin.example.com"],
"expires_in_minutes": 60,
"end_user_id": "seller-user-42"
}'

Response:

{
"token": "eyJhbGciOi...",
"jti": "emb_ed_9c1a...",
"expires_at": "2026-06-18T15:30:00+00:00"
}

The editor token includes:

Claim group Purpose
uid, org_id, ws_id, aki Normal identity, workspace context, and parent API-key traceability.
email_verified Lets email verification middleware see the embedded owner as verified when the API-key owner is verified.
org_role, ws_role, permissions Lets normal authorization checks work inside the embedded editor.
allowed_origins Stored for embed policy/contract and future origin-aware filtering.
end_user_id External audit/correlation identifier.

Editor route:

{APP_BASE_URL}/embed/editor?token={editor_token}

Supported query parameters:

Parameter Aliases Notes
token editorToken Editor embed token.
workflowExternalId workflow_external_id, workflow External workflow id to open, for example wf_....
name workflowName, workflow_name Initial workflow name for create flows when workflowExternalId is omitted.
theme - Optional initial theme class.
primaryColor - Optional initial primary/accent color, applied before iframe readiness.
locale - Initial locale. Unsupported locales fall back to English.
showName - Pass false to hide the workflow name in the embedded editor toolbar.
showBranding - Pass false to hide Madoo branding references in the embedded editor shell.

Example:

<iframe
id="madoo-editor"
src="https://app.madoo.ai/embed/editor?embed=true&token=EDITOR_TOKEN&workflowExternalId=wf_abc123&locale=it"
style="width:100%;height:900px;border:0;"
allow="clipboard-read; clipboard-write"
></iframe>

The hosted SDK exposes an editor bridge with the same iframe + postMessage model as the runtime: It always appends embed=true to the iframe URL so Madoo runs in embed mode and does not trigger the normal refresh-token flow.

<div id="madoo-editor"></div>
<button id="save-workflow">Save</button>
<script src="https://app.madoo.ai/embed/sdk/madoo-embed.js"></script>
<script>
const editor = MadooEmbed.editor.create({
container: "#madoo-editor",
token: "EDITOR_TOKEN",
workflow: "wf_abc123", // for create flows, omit workflow and pass name instead
// name: "Campaign workflow",
locale: "it",
customCss: ".madoo-editor-embed { font-family: Inter, sans-serif; }",
onSaved: (workflow) => console.log(workflow.workflowExternalId),
onSaveFailed: (error) => console.error(error.message),
});
document.querySelector("#save-workflow").addEventListener("click", () => {
editor.save();
});
</script>

If your backend returns a clean editor URL and workflow identity separately, pass those to the bridge instead of adding query parameters yourself:

MadooEmbed.editor.create({
container: "#madoo-editor",
editorUrl: "https://app.madoo.ai/embed/editor",
token: "EDITOR_TOKEN",
workflowExternalId: "wf_abc123",
showName: false,
showBranding: false,
});

The bridge builds the iframe URL for token/workflow initialization, listens for madoo:editor:ready, then applies theme/CSS through postMessage. If constructing the iframe manually, token and workflow initialization are query-string based. Theme can be passed initially with theme or updated after load with madoo:editor:setTheme. Custom CSS should be sent after madoo:editor:ready with madoo:editor:setCustomCss. Do not place full CSS in the URL.

When workflowExternalId is omitted, the parent must provide name. The workflow is only created when the parent sends madoo:editor:save or the user runs the workflow. The embedded editor does not show a Save button; hosts that need an explicit save action should render it outside the iframe and send madoo:editor:save. Missing name is treated as an initialization error.

For a design/template_render node, an embedded editor save or run that selects the current template revision requires a Published DesignDocument. An already pinned immutable revision remains usable if its source template later returns to draft or is archived. A rejected current selection returns through the existing madoo:editor:saveFailed path; the iframe message contract is unchanged. When the author chooses another published revision in the embedded editor, the same review dialog as the full editor shows added, removed and changed placeholder fields, new required fields, connected-field risks, page count and whether visual content changed. The author confirms the manual pin change; opening the workflow never switches revisions implicitly. This review is informational. The editor saves against the definition ETag it read on load. If another authoring surface changes the workflow first, the save returns a 412 precondition failure and the iframe emits madoo:editor:saveFailed; the author must reload and review the newer definition before trying again. An execution request does not silently write the template pin. The publisher also rejects a bare numeric version ID on a template outside Published status unless the saved workflow pin carries the matching document/version identity and hashes.

After an editor run completes or partially succeeds, the embedded editor opens its final-results gallery when at least one workflow output exists (unless the user disabled automatic opening in the editor’s execution menu). Closing the gallery returns to the executed canvas without clearing its node states. Failed and cancelled runs do not open the gallery automatically. This is an iframe-local presentation behavior and does not add or change any postMessage event.

The editor iframe posts madoo:editor:ready after installing its message listener. Wait for this event before sending commands.

Accepted parent-to-editor commands:

Message type Payload Effect
madoo:editor:save { "requestId"?: string } Saves current workflow and publishes it before emitting madoo:editor:saved. If no workflow exists yet, uses the name provided during initialization. Echoes requestId in the response event.
madoo:editor:run none Saves when needed and runs the current workflow.
madoo:editor:setTheme EditorEmbedTheme Applies CSS variables and light/dark mode.
madoo:editor:setCustomCss { "css": "..." } Injects sanitized custom CSS.

Example:

const editor = document.querySelector<HTMLIFrameElement>("#madoo-editor");
window.addEventListener("message", (event) => {
if (event.origin !== "https://app.madoo.ai") return;
if (event.data?.type !== "madoo:editor:ready") return;
editor!.contentWindow?.postMessage(
{
type: "madoo:editor:setTheme",
payload: {
mode: "light",
primaryColor: "#0f766e",
borderRadius: "6px",
fontFamily: "Inter, sans-serif",
},
},
"https://app.madoo.ai",
);
});

Editor iframe-to-parent events:

Message type Payload When emitted
madoo:editor:ready { version, acceptedMessages } Message listener is ready.
madoo:editor:saved WorkflowEditorSavedPayload Workflow save succeeds.
madoo:editor:saveFailed { message, requestId? } Workflow save fails.

The saved payload is produced by WorkflowEditorSurface and includes the saved workflow identity and name. When save was triggered by madoo:editor:save, the payload also includes the request id supplied by the parent. Treat the exact shape as part of the frontend contract and update this document whenever WorkflowEditorSavedPayload changes.


Runtime and editor embed tokens use the same revocation endpoint:

DELETE /api/v1/embed/tokens/{jti}?expires_at={iso8601}
Authorization: Bearer <public_api_access_token>

Examples:

Terminal window
curl -s -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"$BASE_URL/api/v1/embed/tokens/emb_7f2e...?expires_at=2026-06-18T15:30:00Z"
curl -s -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"$BASE_URL/api/v1/embed/tokens/emb_ed_9c1a..."

Notes:

  • jti must start with emb_, so both emb_... and emb_ed_... are valid.
  • If expires_at is omitted, the revocation flag uses a 30-day TTL.
  • Runtime and editor validation both check the Redis revocation flag.
  • Revoking the parent API key also invalidates child embed tokens once the API-key active cache says the key is inactive.

Follow these rules in every external integration:

Rule Reason
Mint embed tokens only on your backend. API keys and OAuth client secrets must never reach the browser.
Use narrow allowed_origins. The runtime middleware uses these for iframe frame-ancestors; they are also part of the token contract.
Prefer short-lived per-session tokens. Limits impact if a browser token is copied.
Use max_executions for runtime tokens where possible. Bounds cost and abuse for end-user widgets.
Use end_user_id for customer-facing embeds. Gives isolation/correlation by external user.
Validate event.origin in every parent message listener. The iframe posts with targetOrigin: '*', so parent-side origin validation is mandatory.
Send postMessage with Madoo’s exact target origin. Avoid broadcasting commands to an arbitrary child window.
Revoke tokens when an external session, installation, or user is disabled. Revocation is immediate and checked on every embed request.

These are important because they affect how integrations should be built today:

  • Runtime embed tokens are validated by EmbedAuthMiddleware on /embed/* and /api/embed/*.
  • Editor embed tokens are normal bearer JWTs for the SPA/API surface. They are not processed by EmbedAuthMiddleware.
  • Runtime iframe initialization is split: token/workflow/interface come from URL and backend config; theme, locale, dynamic values, visibility, and custom CSS can come from postMessage.
  • Editor iframe initialization is query-string based for token/workflow/theme/locale. Theme and CSS can be updated with postMessage.
  • The runtime bridge supports origin filtering in startListening(allowedOrigins), but the runtime page currently starts it without a token-derived origin list. Parent applications must still validate event.origin, and Madoo should update this document if iframe-side origin filtering becomes enforced.
  • prefilled_inputs exists in the public token request and application request model. The current runtime page does not consume it directly from /api/embed/config; use madoo:init or madoo:setValues for browser-side prefilling until that changes.

Area File
Public token endpoints backend/src/Presentation/Madoo.Api/V1/EmbedTokensV1Controller.cs
Public token DTOs backend/src/Presentation/Madoo.Api.Contracts/V1/Embed/EmbedV1Contracts.cs
Token creation/validation backend/src/Core/Madoo.Application/Services/Identity/EmbedTokenService.cs
Token service contract/claims backend/src/Core/Madoo.Application/Services/Identity/IEmbedTokenService.cs
Runtime embed middleware backend/src/Presentation/Madoo.Api/Middleware/EmbedAuthMiddleware.cs
Runtime bootstrap API backend/src/Presentation/Madoo.Api/Controllers/EmbedController.cs
Runtime iframe page frontend/src/app/routes/embed/index.tsx
Editor iframe page frontend/src/app/routes/embed/editor.tsx
Hosted runtime/editor SDK frontend/src/embed/host/madoo-embed-host-sdk.ts
Shared embed message protocol frontend/src/embed/protocol/embed-message-protocol.ts
Runtime iframe bridge frontend/src/embed/internal/runtime-iframe-bridge.ts
API client embed methods/types frontend/src/shared/lib/api-client.ts

Any change to the files listed in the code map, or to embed token claims, iframe URLs, query parameters, supported postMessage message types, auth requirements, execution-cap behavior, or revocation behavior must update this document in the same change.

If the implementation and this document disagree, treat the implementation as the source of truth, then update this document immediately.


Next: 08-reference.md - reference appendix.