Skip to content

Technical Specifications for Custom Embeds (Power Up) in Composer

What is a Custom Embed?

Composer supports a method of integrating with foreign asset and content management systems called custom embeds. A custom embed is a way for developers to integrate portions of their own UI directly into Composer and return data for Composer to save in a story document.

To create a custom embed you need:

  • To be comfortable writing front-end UX code, including JavaScript
  • To host and serve the UX code reliably to iframes within Composer. This can be on your own server, or from a static environment, like Amazon S3.
  • To search and fetch content from your source system by id (for example, by making asynchronous HTTP calls)
  • (Recommended) To have an API endpoint that can serve rendering information about a piece of content on demand in an oEmbed-like way

Composer supports multiple types of custom embeds simultaneously, so you can build separate embeds with different types. For example, audio, videos, galleries, and images could each be a distinct type of embed.

Building the Admin UI

The admin UI of your custom embed consists of three panels:

  • Search Panel
  • View Panel
  • Edit Panel

Having all panels in place gives your authoring application the ability to use any third party media system including the ability to search for media, view it, and make any changes to it.

In Composer, you should first create a Custom Embed Type. Within that custom embed type, you can set a display name and a subtype value that distinguishes your custom embed type from the others.

You will also need to set a URL for each of the three panels listed above. For working examples of all three panels, see the reference implementation at washingtonpost/arc-custom-embed, which provides searchApi.html, viewApi.html, and editApi.html for an audio Custom Embed type.

About the Panels

Search Panel

When a user adds your Custom Embed element to their document in Composer, your Search Panel appears. In this panel, the user can search, select, or sort content items from your foreign system and select one for adding to the document. For example, you might populate this iframe with a text box that lets users search for audio by title and then click OK to use it. The exact mechanism for finding and selecting content is up to you. You control the entire UI within the iframe.

Depending on which Custom Embed subtype you’re adding, you should use a corresponding search panel URL to load the search user experience. Search integration may receive any arbitrary data through the URL.

View Panel

Composer may need to display your Custom Embed type from the editor body, related content, or featured media panel. This panel is used to show your embedded content when a user is not interacting with it directly.

In order to do that, Composer creates an iframe and sets the iframe src attribute to the link to the preconfigured view panel URL. Composer makes any necessary substitution to the view panel URL from ANS to make a proper URL.

The View panel should not have Edit controls; however, it might contain some action buttons, such as play or stop. Composer loads the Edit Panel when a user initiates editing.

Edit Panel

This panel is available to allow users to edit the presentation of the selected foreign content within the context of the document. For example, on an embedded audio, a user might turn on autoplay and set a start position of 30 seconds. On an image, a user might set a caption or title.

This works similarly as the View Panel, except Composer waits for the submit or cancel message from the iframe. The submit message must have well-formed Custom Embed ANS in it with all the updates.

The Edit Panel may have a cancel button along with Submit button for the better UI. However, Composer may cancel editing at any time just by removing that iframe. No message will be sent to the Edit integration.

Unlike the View Panel, data returned from an Edit Panel requires a config object additionally to the required fields of url and id.

Iframe Communication Protocol

The search, view and edit panels should each send a handshake postMessage to the parent window (window.parent.postMessage) as soon as they are loaded and ready to receive commands or interact with the user. If Composer does not receive the initial handshake message, Composer displays an error message with a Retry button.

The Search Panel is expected to return a configuration JSON on a successful search. Configuration JSON is subject to validation; see Validation and Constraints below.

The View Panel is expected to send only the handshake message. The View Panel receives configuration in the form of a query string (URL-encoded).

The Edit Panel receives configuration in the form of a query string (URL-encoded). The Edit Panel is expected to send the handshake message and the configuration JSON when a user submits changes. If the user discards changes, a cancel message should be sent. Configuration JSON is subject to validation; see Validation and Constraints below.

Query string parameters

Composer communicates with the panels through query string data. Custom embed data is passed in as URL-encoded data in a p parameter in the form of JSON. A random key should also be sent in a k parameter.

ParameterDescription
pCustom Embed data as URL-encoded JSON
kRandom key used to match postMessage responses to this integration

Example:

Terminal window
editApi.html?k=d5ac90ec-096a-4cce-986a-b842f9d8d4c7&p=%7B%22id%22%3A%2217b3224337d2d3%22%2C%22url%22%3A%22https%3A%2F%2Fexample.com%2Ftitle%2F17b3224337d2d3%22%2C%22config%22%3A%7B%22caption%22%3A%22Example%20caption%22%7D%7D

Message format

Plugins should respond back through the browser postMessage mechanism. Each message should be a JSON object and contain the following fields:

FieldDescription
sourceAlways set to custom_embed
actionOne of ready, data, or cancel
dataCustom embed data, or the content height of the iframe (depends on action)
keyMust echo the value from the k query string argument

Ready Message

The ready message should be sent by each panel as soon as it renders their content and content height is known.

{
"source":"custom_embed",
"action":"ready",
"data":{"height":908},
"key":"d5ac90ec-096a-4cce-986a-b842f9d8d4c7"
}

If ready message will not be sent within ~10 seconds, Composer renders a timeout error and plugin content will be discarded.

Data Message

The data message is sent by the Search Panel when a user selects an item, and by the Edit Panel when editing is done. The data payload corresponds to the embed property of the ANS custom_embed element — Composer wraps it in the rest of the ANS structure when persisting it.

The data field is what will be persisted as a content element within the ANS document and available for front-end code to access when rendering the element.

{
"source": "custom_embed",
"action": "data",
"data": {
"id": "b0bc95dc11919",
"url": "https://my.content.com/data/2",
"config": {
"id": 2,
"text": "Some Text",
"image_id": 1
}
},
"key": "d5ac90ec-096a-4cce-986a-b842f9d8d4c7"
}

The data field must contain id, url, and config:

  • id — Identifier for the embedded item in your source system. Composer stores this verbatim and passes it back to the View and Edit panels via the p query string parameter.
  • url — Canonical URL of the embedded item in its source system (for example, https://www.imdb.com/title/tt0107290). Composer stores this but does not dereference it.
  • config — Arbitrary JSON object holding presentation/configuration data for this embed (caption, autoplay flag, and so on). It must not contain referent, type, or version fields, and should hold as few fields as possible.

Please do not put large objects here, as the data is round-tripped through query strings and is subject to the size limitations described in Validation and Constraints. Use data.id to identify the underlying resource and data.config to store presentation properties only.

Cancel Message

Cancel message is used to notify Composer that user wants to cancel search or discard any editing changes.

{
"source": "custom_embed",
"action": "cancel",
"key":"d5ac90ec-096a-4cce-986a-b842f9d8d4c7"
}

The cancel message notifies Composer to close the UI. It does nothing for the view panel and should not be used there.

Composer can close search or edit the iframe by itself without notifying the iframe content. Please consider this behavior and do not persist any changes in the system. The only proper way to persist changes is to send data through data message back to Composer.

Validation and Constraints

The final Custom Embed configuration has the following limitations:

  • Configuration must be valid JSON.
  • JSON size must be no more than 2048 bytes.
  • JSON must not have type, version, or referent fields at any level. Composer strips those out.

This JSON will be saved as a content element in the ANS document and is available for downstream consumers (for example, feature and template developers) to access.

Message Communication Example

Composer Load a Search Integration

  1. Integration loads and sends back Ready message

    {
    "source": "custom_embed",
    "action": "ready",
    "data": {
    "height": 908
    },
    "key":"..."
    }
  2. User selects an item

    {
    "source": "custom_embed",
    "action": "data",
    "data": {
    "id": "b0bc95dc11919",
    "url": "https://my.content.com/data/2",
    "config": { "caption": "Example caption" }
    },
    "key": "..."
    }

Composer Load View Integration

viewApi.html?p=...

  1. View integration renders its content and sends back ready message with the content height.
{
"source": "custom_embed",
"action": "ready",
"data": {
"height": 480
},
"key": "..."
}

Composer Load Edit Integration

editApi.html?p=...

  1. Edit integration renders it’s content and sends back ready message with the content height.

    {
    "source": "custom_embed",
    "action": "ready",
    "data": {
    "height": 890
    },
    "key": "..."
    }
  2. User accepted changes

    {
    "source": "custom_embed",
    "action": "data",
    "data": {...},
    "key": "..."
    }
  3. OR User cancelled changes

    {
    "source": "custom_embed",
    "action": "cancel",
    "key": "..."
    }

Additional Resources