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.
1. Integration model
Section titled “1. Integration model”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 | vPOST /api/v1/embed/.../tokens -> returns embed token | vRender iframe with token -> /embed/... or /embed/editor | vCommunicate 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
minimumRendererVersioninternally to reject unsupported layout capabilities safely; it does not change the URLs, tokens, orpostMessagemessages 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.
2. Server-side authentication
Section titled “2. Server-side authentication”First obtain a normal Public API bearer token with client credentials:
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.
3. Runtime workflow embed
Section titled “3. Runtime workflow embed”Use a runtime embed when an external end user should fill workflow inputs and generate outputs, without seeing the Madoo editor.
3.1 Create a runtime token
Section titled “3.1 Create a runtime token”POST /api/v1/embed/tokensAuthorization: Bearer <public_api_access_token>Content-Type: application/jsonRequest 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:
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.
3.2 Render the runtime iframe
Section titled “3.2 Render the runtime iframe”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.
3.3 Runtime postMessage commands
Section titled “3.3 Runtime postMessage commands”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;};3.4 Runtime postMessage events
Section titled “3.4 Runtime postMessage events”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:errorcodes.messageis 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 embedlocale. Knowncodevalues:missing_required_inputs(a required input was left empty;messagenames 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;messagenames 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), andexecution_failed(any other startup error). Branch oncodefor 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 requestRangeheader and the206 Partial Content,Content-RangeandAccept-Ranges: bytesresponse semantics. A custom player cannot compensate for a proxy that collapses range requests into full200 OKresponses.
Skipped outputs. Each entry in
payload.outputscarries apresenceof"present"or"absent". An"absent"output was skipped because an optional input was not provided (or an upstream node was skipped); it has nourl/value. The run still completes successfully — checkpresencebefore 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 alogical_name(the author-facing output name) alongside the uniquename({logical}_{iteration}_{nodeId}): group bylogical_namewhen 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`;});4. Editor embed
Section titled “4. Editor embed”Use an editor embed when an external application should let a workspace user author workflows inside your own UI.
4.1 Create an editor token
Section titled “4.1 Create an editor token”POST /api/v1/embed/editor/tokensAuthorization: Bearer <public_api_access_token>Content-Type: application/jsonRequest 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:
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. |
4.2 Render the editor iframe
Section titled “4.2 Render the editor iframe”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.
4.3 Editor postMessage commands
Section titled “4.3 Editor postMessage commands”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", );});4.4 Editor postMessage events
Section titled “4.4 Editor postMessage events”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.
5. Revocation
Section titled “5. Revocation”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:
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:
jtimust start withemb_, so bothemb_...andemb_ed_...are valid.- If
expires_atis 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.
6. Security rules for host applications
Section titled “6. Security rules for host applications”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. |
7. Current implementation notes
Section titled “7. Current implementation notes”These are important because they affect how integrations should be built today:
- Runtime embed tokens are validated by
EmbedAuthMiddlewareon/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 validateevent.origin, and Madoo should update this document if iframe-side origin filtering becomes enforced. prefilled_inputsexists in the public token request and application request model. The current runtime page does not consume it directly from/api/embed/config; usemadoo:initormadoo:setValuesfor browser-side prefilling until that changes.
8. Code map
Section titled “8. Code map”| 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 |
9. Maintenance rule
Section titled “9. Maintenance rule”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.