The iframe and your page talk with window.postMessage. There are six message types: four from the iframe to your page, two from your page to the iframe.
The envelope
Section titled “The envelope”Every message, in both directions, has this shape.
{ "version": 1, "type": "flume:iframe_ready", "session_uuid": "c1f0a6de-3b52-4d8e-9c47-2f8a1b6d0e93", "payload": {}}| Field | Type | Notes |
|---|---|---|
version | number | Always 1 |
type | string | One of the six types below |
session_uuid | string | The session from POST /start-iframe-session |
payload | object | Depends on type |
Checks your page must make
Section titled “Checks your page must make”Drop a message unless all four hold.
| Check | Why |
|---|---|
event.origin equals the iframe's origin | Another frame cannot pose as Flume |
event.source is your iframe's contentWindow | Another window on that origin cannot pose as this iframe |
data.version is 1 | A future version is ignored safely |
data.session_uuid is your session's | A stale iframe cannot write to a new item |
Ignore any type you do not know. The host snippet does all of this.
All messages
Section titled “All messages”Every message, and which way it goes. All six postMessage types between the iframe and your page, in the order one open usually sends them. Four come to your page, two go back.
Read this diagram as text
- The iframe posts flume:iframe_ready once it has painted, with an empty payload. Clear the ready timer and show the iframe.
- First open only: the iframe posts flume:user_connected with a connect_code. Send it to your backend at once. The iframe expects no reply.
- The iframe posts flume:save_item with the save payload, which carries save_uuid. It waits 60 seconds for your reply.
- Your page replies flume:item_saved with the same save_uuid, and item_ref and item_label when you have them.
- Or your page replies flume:item_save_failed with the same save_uuid and a reason: permission, conflict, storage or error.
- The iframe posts flume:close_iframe with a reason of done, closed or expired. Remove the iframe. On expired, start a new session.
| Type | Direction | Payload | When |
|---|---|---|---|
flume:iframe_ready | Iframe to page | Empty | The iframe has loaded |
flume:user_connected | Iframe to page | connect_code | The user verified their email |
flume:save_item | Iframe to page | The save payload | The user saved a product |
flume:close_iframe | Iframe to page | reason | The iframe should be removed |
flume:item_saved | Page to iframe | save_uuid, your ids | Your write worked |
flume:item_save_failed | Page to iframe | save_uuid, reason | Your write failed |
From the iframe to your page
Section titled “From the iframe to your page”flume:iframe_ready
Section titled “flume:iframe_ready”The iframe has loaded and drawn its first screen.
- Payload:
{}. - Do this: clear your ready timer, remove your spinner, move focus into the iframe.
- It is also sent when the iframe loaded but could not start, for example with a used launch code. The iframe then shows its own error, with a retry and a close button. Your page cannot tell the two cases apart, and does not need to: clear the timer in both.
- After a failed start, the iframe's close button sends
flume:close_iframewithclosed, and its retry sendsexpired.
flume:user_connected
Section titled “flume:user_connected”The user verified their email inside the iframe.
| Payload field | Type | Notes |
|---|---|---|
connect_code | string | Single use. Valid for 120 seconds |
{ "version": 1, "type": "flume:user_connected", "session_uuid": "c1f0a6de-3b52-4d8e-9c47-2f8a1b6d0e93", "payload": { "connect_code": "connect_example_code_0000" }}- Do this: send
connect_codeto your backend. CallPOST /users/exchange-connect-code. Store theaccess_tokenover any token you held for the user. - Any token you held for this user stopped working when they verified. See Users and verification.
- If you skip the exchange, you hold no working token. The user verifies again on the next open.
flume:save_item
Section titled “flume:save_item”The user confirmed a save. The payload is the full save payload, one JSON object.
| Payload field | Type | Notes |
|---|---|---|
save_uuid | string (UUID) | Identifies this save. A retry carries the same value |
schema_version | string | "1.0" |
| Other fields | See Save payload |
- Do this: write the item into your records, then reply with
flume:item_savedorflume:item_save_failed. - Reply within 60 seconds. Silence counts as a failure.
- If the user presses "Try again", you get the same
save_uuidagain. Do not write a second record.
flume:close_iframe
Section titled “flume:close_iframe”The iframe is done. Remove it from your page.
| Payload field | Type | Values |
|---|---|---|
reason | string | done, expired or closed |
reason | Meaning | Do this |
|---|---|---|
done | The user finished after a save | Remove the iframe. Refresh your data |
expired | The session ended, or the user pressed Try again after a failed start | Start a session again and remount |
closed | The user pressed X or Escape | Remove the iframe |
{ "version": 1, "type": "flume:close_iframe", "session_uuid": "c1f0a6de-3b52-4d8e-9c47-2f8a1b6d0e93", "payload": { "reason": "done" }}From your page to the iframe
Section titled “From your page to the iframe”Post replies to the iframe's window, with the iframe's origin as the target origin. Never use "*".
frame.contentWindow.postMessage( { version: 1, type: "flume:item_saved", session_uuid: sessionUuid, payload }, new URL(iframeUrl).origin,);The iframe accepts a reply only when all of these hold:
- it comes from the page that framed it,
- that page's origin is the
host_originyou sent, - it carries the session's
session_uuid, - its
save_uuidis the save the iframe is waiting on.
A reply that fails one of these is ignored, and the save waits out its 60 seconds.
flume:item_saved
Section titled “flume:item_saved”Your write worked.
| Payload field | Type | Required | Notes |
|---|---|---|---|
save_uuid | string (UUID) | Yes | From the flume:save_item you are answering |
item_ref | string | No | Your id for the record. Max 128 characters |
item_label | string | No | Your label for it. Max 200 characters |
{ "version": 1, "type": "flume:item_saved", "session_uuid": "c1f0a6de-3b52-4d8e-9c47-2f8a1b6d0e93", "payload": { "save_uuid": "2b9d6f1c-7a34-4e58-b0c2-9d1e3f5a7b6c", "item_ref": "row_9931", "item_label": "T-1.A" }}flume:item_save_failed
Section titled “flume:item_save_failed”Your write failed. The iframe shows the user a retry.
| Payload field | Type | Required | Notes |
|---|---|---|---|
save_uuid | string (UUID) | Yes | From the flume:save_item you are answering |
reason | string | Yes | permission, conflict, storage or error |
message | string | No | Detail for Flume's logs. Cut at 500 characters. Never shown to the user |
reason | Use it when |
|---|---|
permission | The user may not edit that record |
conflict | The record changed or is locked |
storage | Your write failed |
error | Anything else |
The payload must be a plain object. A thrown Error cannot be sent as it is. The host snippet turns one into a proper payload.
What does the user see around a save?
Section titled “What does the user see around a save?”Every way a save can end. Three outcomes of one flume:save_item. Two of them show the user the same message, and a retry repeats the same save_uuid.
Read this diagram as text
- The iframe posts flume:save_item with a save_uuid.
- Your page replies flume:item_saved: the user sees the saved screen, with your item_label when you send one.
- Your page replies flume:item_save_failed with a reason of permission, conflict, storage or error: the user sees one message with Close and Try again. The reason is not shown.
- Your page does not reply within 60 seconds: the user sees the same message.
- Try again posts flume:save_item again with the same save_uuid. Your write must be safe to repeat.
| Case | The user sees |
|---|---|
You reply flume:item_saved | A saved screen. It shows item_label when you send one, as "Saved as ...". Otherwise your item's own label |
You reply flume:item_save_failed, with any reason | One message: the save to your product failed, with Close and Try again. reason and message are never shown |
| You do not reply within 60 seconds | The same message |
| Your reply arrives after that | A late reply for the same save_uuid can still be applied while that save is the current one. Do not rely on it: reply within the time limit |
| Flume could not build the payload | An error from Flume. Your page receives no flume:save_item |
- Try again sends
flume:save_itemagain with the samesave_uuid. A slow write of yours may have finished meanwhile, so the write must be safe to repeat. - After a save, the user can press Done, which sends
flume:close_iframewithdone, or open the product in Flume, which keeps the iframe open.
Rules the iframe follows
Section titled “Rules the iframe follows”- It posts only to the
host_originyou sent when you started the session. If your page is on another origin, no message reaches it. - It reports each save's result to Flume itself. Your backend makes no call for it.
- One exception: when the iframe cannot start, it has no session. It then posts only
flume:iframe_readyandflume:close_iframe, to the page that framed it.
What is not sent?
Section titled “What is not sent?”- No resize message. You size the iframe.
- No error message. The iframe shows its own error screen when it cannot start. Your ready timer covers a frame that never loads.
- No message per search or per quote. Only a save reaches your page.
Versioning
Section titled “Versioning”version is 1. A message with another version, or a type you do not know, must be ignored.