Understanding the UCP Embedded Protocol: Secure iframe and WebView Communication

The Embedded Protocol (EP) is a JSON-RPC 2.0 transport binding that enables secure bidirectional messaging between a host application and an embedded business UI inside an iframe or WebView, handling everything from initial handshake to optional MessageChannel upgrades.

The Embedded Protocol defines how a super-app, browser, or native container can embed merchant interfaces and exchange structured messages with them. According to the Universal-Commerce-Protocol/ucp repository, this specification lives in docs/specification/embedded-protocol.md and serves as the foundation for capability-specific implementations like Embedded Checkout and Embedded Cart.

What Is the Embedded Protocol?

The Embedded Protocol (EP) standardizes communication between a host (the embedding application) and an embedded business UI running inside an iframe or native WebView. It specifies the message format, transport mechanisms, and security constraints required for cross-context interoperability.

At its core, EP mandates that all messages conform to the JSON-RPC 2.0 specification, using a strict envelope structure with jsonrpc, method, params, and optional id fields. This guarantees a uniform, language-agnostic format that any host or embedded page can parse, whether implemented in JavaScript, Swift, Java, or Kotlin.

Core Message Architecture

Request and Notification Types

EP distinguishes between two message patterns to control response expectations:

  • Requests – JSON-RPC objects containing an id field require a synchronous response from the host. These block until the host returns a result or error.
  • Notifications – Messages without an id field operate as fire-and-forget events. The embedded UI uses these to push state changes like ec.line_items.change or ep.cart.start without waiting for acknowledgment.

Error Handling Strategy

The protocol separates failure modes into distinct layers. Transport-level failures (network issues, malformed JSON) use the JSON-RPC error field, while application-level outcomes always return inside result.ucp.status. This field contains either success or error, allowing hosts to distinguish between protocol failures and business-logic failures without parsing capability-specific payloads.

Channel Setup and Upgrade Flow

Initial Handshake via postMessage

Communication begins with standard window.postMessage calls between the host window and the iframe content. As implemented in source/schemas/transports/embedded_config.json, the transport configuration schema validates allowed delegation patterns and Content Security Policy requirements before the connection activates.

The embedded context initiates the session by sending an ec.ready request (or equivalent capability-specific ready method). This request includes a params.delegate array listing capabilities—such as payment or address—that the embedded UI wants to delegate to the host.

Optional MessageChannel Upgrade

Upon receiving ec.ready, the host may respond with an upgrade object containing a transferred MessagePort. This optimization, documented in the Embedded Protocol specification, establishes a dedicated MessageChannel for lower-latency communication while maintaining same-origin restrictions.

If both parties agree to the upgrade, they switch from postMessage to the transferred port and resend the ready handshake over the new channel. If the host declines the upgrade, communication continues over the standard postMessage bus.

Native WebView Bridges

For native iOS or Android hosts, the protocol exposes globals on the embedded page's window object. Native code listens on window.Embedded{Capability}ProtocolConsumer or falls back to window.webkit.messageHandlers.Embedded{Capability}ProtocolConsumer to receive JSON-RPC strings directly from the WebView context.

Security Constraints

The Embedded Protocol enforces strict security at multiple layers to prevent click-jacking and cross-origin data leakage:

  • Origin validation – Hosts must verify event.origin against the iframe's continue_url before processing any message.
  • CSP frame-ancestors – The specification mandates Content Security Policy rules restricting which domains may embed the interface.
  • Sandboxed iframes – Embedded contexts should run in sandboxed environments with limited privileges.
  • Credentialless attribute – Optional support for the credentialless iframe attribute prevents credential leakage during cross-origin loads.

Implementation Examples

Host Side: Listening and Upgrading

This example from the UCP reference implementation shows a host receiving the initial ready request and optionally upgrading to a MessageChannel:

// Host page (super-app implementation)
const iframe = document.getElementById('businessIframe');

window.addEventListener('message', async (event) => {
  // Strict origin validation against incoming event
  if (event.origin !== new URL(iframe.src).origin) return;

  const msg = JSON.parse(event.data);
  
  if (msg.method === 'ec.ready') {
    // Create dedicated channel for improved performance
    const { port1, port2 } = new MessageChannel();

    const response = {
      jsonrpc: '2.0',
      id: msg.id,
      result: {
        ucp: { version: '1.0', status: 'success' },
        upgrade: { port: port2 },
        delegate: ['payment', 'address']
      }
    };
    
    // Transfer port2 to the embedded context
    iframe.contentWindow.postMessage(
      JSON.stringify(response), 
      event.origin, 
      [port2]
    );

    // Use port1 for subsequent communication
    port1.onmessage = (e) => handleEmbeddedMessage(e.data);
  }
});

Embedded Context: Sending Ready and Handling Upgrade

The embedded business page initiates the session and handles the optional channel transfer:

function sendReady() {
  const ready = {
    jsonrpc: '2.0',
    id: 'ready_1',
    method: 'ec.ready',
    params: {
      delegate: ['payment', 'address']
    }
  };
  window.parent.postMessage(JSON.stringify(ready), '*');
}

window.addEventListener('message', (event) => {
  const resp = JSON.parse(event.data);
  
  if (resp.result?.upgrade?.port) {
    const port = resp.result.upgrade.port;
    port.onmessage = (e) => handleHostMessage(e.data);
    
    // Re-send ready over the dedicated channel
    port.postMessage(JSON.stringify({
      jsonrpc: '2.0',
      id: 'ready_2',
      method: 'ec.ready',
      params: { delegate: ['payment', 'address'] }
    }));
  } else {
    handleHostMessage(resp);
  }
});

sendReady();

State Change Notifications

Cart state updates use fire-and-forget notifications without response requirements:

function notifyCartChange(cart) {
  const notif = {
    jsonrpc: '2.0',
    method: 'ep.cart.change',
    params: { cart }
  };
  
  // Prefer protocol-specific global if available
  const target = window.EmbeddedCartProtocol || window.parent;
  target.postMessage(JSON.stringify(notif), '*');
}

Session Error Escalation

When encountering unrecoverable errors, the embedded context sends a notification containing a continue_url so the host can redirect the user safely:

function sendSessionError() {
  const err = {
    jsonrpc: '2.0',
    method: 'ec.error',
    params: {
      ucp: { version: '1.0', status: 'error' },
      messages: [{
        type: 'error',
        code: 'not_supported_error',
        content: 'Requested auth credential type is not supported.',
        severity: 'unrecoverable'
      }],
      continue_url: 'https://merchant.example.com/checkout'
    }
  };
  window.parent.postMessage(JSON.stringify(err), '*');
}

Summary

  • The Embedded Protocol standardizes JSON-RPC 2.0 messaging between host applications and embedded iframes/WebViews in the UCP ecosystem.
  • Message types split into blocking Requests (with id) and non-blocking Notifications (without id) to optimize performance.
  • Channel flexibility allows starting with postMessage and optionally upgrading to a transferred MessageChannel for dedicated communication.
  • Security layers include strict origin validation, CSP frame-ancestors, and sandboxed iframe policies to prevent cross-origin attacks.
  • Error separation distinguishes transport failures (JSON-RPC error field) from business outcomes ( result.ucp.status field).
  • Capability-specific bindings like ec.* for checkout and ep.cart.* for cart functionality extend the base protocol defined in docs/specification/embedded-protocol.md.

Frequently Asked Questions

How does the Embedded Protocol handle security for cross-origin iframe communication?

The protocol mandates strict origin validation on every postMessage event, requiring hosts to verify event.origin against the embedded page's continue_url. Additionally, the specification recommends CSP frame-ancestors restrictions, sandboxed iframe attributes, and optional credentialless flags to prevent credential leakage and click-jacking attacks.

What is the difference between Requests and Notifications in EP?

Requests contain an id field and require the host to return a JSON-RPC response, creating a synchronous blocking pattern suitable for authentication or payment authorization. Notifications omit the id field and operate as fire-and-forget messages, allowing the embedded UI to broadcast state changes like cart updates without waiting for host acknowledgment.

When should applications use the MessageChannel upgrade instead of postMessage?

Applications should upgrade to MessageChannel when both the host and embedded context require lower-latency communication or want to isolate traffic from the standard window message bus. The upgrade occurs during the initial handshake when the host responds to ec.ready with a transferred MessagePort, though the protocol gracefully falls back to postMessage if either side doesn't support the upgrade.

Where are the authoritative specifications for the Embedded Protocol located?

The core specification resides at docs/specification/embedded-protocol.md in the Universal-Commerce-Protocol/ucp repository. Capability-specific extensions are documented separately: checkout methods use docs/specification/embedded-checkout.md, while cart functionality is defined in docs/specification/embedded-cart.md. The transport configuration schema at source/schemas/transports/embedded_config.json validates runtime EP implementations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →