← Back to Builder launch

Builder in iframe — Frontend guidelines

Use this page when your app embeds the builder in an iframe and you need Save and Save as template buttons in your own UI (outside the iframe). All communication uses the browser postMessage API with the BUILDER_BRIDGE protocol.

1. Message envelope

Every message must use this shape:

FieldRequiredDescription
channelYesAlways "BUILDER_BRIDGE". Ignore messages with any other channel.
typeYese.g. "READY", "SAVE_STATE", "SAVE_REQUEST", "SAVE_RESULT", "SAVE_TEMPLATE_REQUEST", "SAVE_TEMPLATE_RESULT".
payloadYesObject (can be {}). Type-specific data.
requestIdNoOptional. If you send it with a request, the response will echo it so you can match requests to responses.

2. Security

In your window.addEventListener('message', ...) handler, always check event.origin against the builder’s origin (e.g. the origin of your iframe’s src) before using event.data. Only process messages where event.data?.channel === "BUILDER_BRIDGE".

3. Builder tabs and SAVE_STATE

The builder has tabs: Builder, Headers, Footers, and Social. Saving a campaign or template is only valid on the Builder tab (the in-builder Save buttons are disabled on the other tabs).

Your parent app must mirror this: disable your external Save and Save as template buttons when the user is not on the Builder tab.

After READY, the iframe sends SAVE_STATE whenever the active tab changes:

{
  channel: 'BUILDER_BRIDGE',
  type: 'SAVE_STATE',
  payload: {
    saveEnabled: true,           // enable your Save button
    saveTemplateEnabled: true    // enable your Save as template button
  }
}

On Headers, Footers, or Social, both flags are false. Re-enable them when the user returns to Builder.

Do not enable Save buttons on READY alone — wait for SAVE_STATE (sent immediately after READY). Optionally show a hint such as “Switch to Builder tab to save” when saveEnabled is false.

4. Save campaign (temporary save)

Flow

  1. After the builder loads, it sends READY once, then SAVE_STATE with the current tab.
  2. Enable or disable your Save button from payload.saveEnabled on each SAVE_STATE.
  3. When the user clicks Save (only if enabled): show “Saving...”, then send SAVE_REQUEST to the iframe.
  4. Wait for exactly one SAVE_RESULT (same requestId if you sent one). Then hide “Saving...” and show success or payload.error.

Send SAVE_REQUEST

const builderOrigin = new URL(iframeEl.src).origin;
iframeEl.contentWindow.postMessage({
  channel: 'BUILDER_BRIDGE',
  type: 'SAVE_REQUEST',
  payload: {},
  requestId: 'save-' + Date.now()  // optional, for correlation
}, builderOrigin);

Handle READY, SAVE_STATE, and SAVE_RESULT

let ready = false;
let saveEnabled = false;
let saveTemplateEnabled = false;
let saving = false;
let savingTemplate = false;

function updateSaveButtons() {
  saveBtn.disabled = !ready || saving || !saveEnabled;
  saveTemplateBtn.disabled = !ready || savingTemplate || !saveTemplateEnabled;
}

window.addEventListener('message', function (event) {
  if (event.origin !== builderOrigin || event.data?.channel !== 'BUILDER_BRIDGE') return;
  const { type, payload, requestId } = event.data;

  if (type === 'READY') {
    ready = true;
    updateSaveButtons();
  }
  if (type === 'SAVE_STATE') {
    saveEnabled = !!payload.saveEnabled;
    saveTemplateEnabled = !!payload.saveTemplateEnabled;
    updateSaveButtons();
  }
  if (type === 'SAVE_RESULT') {
    saving = false;
    updateSaveButtons();
    if (payload.ok) {
      // Success: payload.newsletterId
    } else {
      // Failure: show payload.error (e.g. "Switch to the Builder tab to save")
    }
  }
});

5. Save as template

Same idea as Save: you send SAVE_TEMPLATE_REQUEST, the iframe runs “Save as template without content” (user may be prompted for template name inside the iframe), then you receive one SAVE_TEMPLATE_RESULT.

Send SAVE_TEMPLATE_REQUEST

iframeEl.contentWindow.postMessage({
  channel: 'BUILDER_BRIDGE',
  type: 'SAVE_TEMPLATE_REQUEST',
  payload: {},
  requestId: 'template-' + Date.now()
}, builderOrigin);

Handle SAVE_TEMPLATE_RESULT

if (type === 'SAVE_TEMPLATE_RESULT') {
  if (payload.ok) {
    // Success: payload.templateName
  } else {
    // Failure: show payload.error (e.g. "Template name missing or cancelled", "Duplicate template name")
  }
}

The template name is entered by the user in a modal inside the iframe. Your app only triggers the action and then handles success/error.

6. Checklist for your app

If you need help when setting this app, you can contact the MarketingPlatform backend team for support.

← Back to Builder launch