Reference
Wire messages
Every message in both directions. You need this page only if you are bridging the protocol yourself; the loader speaks it for you.
The envelope
Every message, both directions, carries a namespace and a version. Anything without them is ignored, so your handler can share a page with any other postMessage traffic.
{ "ns": "vp-embed", "v": 1, "type": "…", … }The version is 1. It exists because a snippet pasted today outlives a server we redeploy a hundred times, a newer frame can recognise an older host and behave rather than going silent. If it ever changes, the old value keeps working.
Frame → your page
| type | Fields | Meaning |
|---|---|---|
ready | height | The chart is mounted and will accept commands. Sent once. |
resize | height | The chart's natural height changed. Apply it, or keep a fixed height. |
error | reason | The chart could not start, a refused symbol, or a missing datafeed. |
datafeedCall | id, method, args | The chart is asking your feed for something. Answer with datafeedResult or datafeedError, quoting the same id. |
commandResult | id, value | A command you sent succeeded. value is whatever that method returns, often a handle. |
commandError | id, reason | A command you sent was refused, and why. An unknown property is named here rather than dropped. |
event | name, payload | Something happened on the chart you asked to hear about, see Events. |
Your page → frame
| type | Fields | Meaning |
|---|---|---|
datafeedResult | id, result | The answer to one datafeedCall. id must match the call. |
datafeedError | id, reason | That call cannot be answered. reason reaches the console, not the reader. |
datafeedTick | id, bar | A realtime bar. id is the subscriberUID from subscribeBars, NOT a call id. |
datafeedTrade | id, trade | One executed print, { price, size, side, timestamp }, for a footprint chart. id is the subscriberUID from subscribeTrades, the same rule as a tick. |
setSymbol | symbol | Change the instrument. The host already chose one in the URL, so this decides nothing new. |
setTheme | theme | light or dark, for a page with its own toggle. |
command | id, method, args | Drive the chart. One type for the whole control API; the methods are on Methods you call. |
datafeedTick’s id is a subscription, not a call. Every other message pairs a reply to the id of the call that asked for it. Ticks are unsolicited, so they are addressed to the subscriberUID you were handed in subscribeBars. Send a call id there and the chart has nothing listening, the bar is dropped, silently.Pin the origin and the source
Check both. event.origin proves the message came from our document; event.source proves it came from THIS frame. Origin alone is not enough, a page embedding two of our charts would let either answer for the other, and the bug only appears on the page that has two.
if (ev.origin !== EMBED_ORIGIN) return;
if (ev.source !== frame.contentWindow) return;
if (!ev.data || ev.data.ns !== 'vp-embed' || ev.data.v !== 1) return;The frame cannot check your origin, because anybody may embed us, so it validates shape instead and rejects unknown fields. It does check the source: a message is accepted only when event.source is the window that embeds the frame. Send from the page that holds the iframe; a message posted from any other frame or a popup is ignored, so another frame on your page cannot drive your chart. That asymmetry is deliberate and is explained on the security model page.
A complete host
The whole bridge, with a working datafeed behind it, is on Without our script copy-pasteable, and run against a real chart by our browser suite on every change.