Skip to content

Quickstart

Open the Flume iframe for one item, let the user find and price an alternative, and receive the result as a message. Your backend makes two calls. Your page runs one snippet.

To run a working page first, copy the full example.

  • A sandbox partner key. Get a sandbox key.
  • Your page's origin, set as an allowed origin by Flume. For local work, ask for http://localhost:3000.
  • Only if you send item photos: your image host, set by Flume.

Flume sets your allowed origins and image hosts. You cannot change them yourself today. See Security and CSP.

Call this when the user clicks your "Find alternatives" button. Send the user, the project and the item.

curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/start-iframe-session" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{
"user": {
"external_ref": "u_8812",
"email": "sarah.chen@example.com",
"first_name": "Sarah",
"last_name": "Chen"
},
"project": {
"external_ref": "proj_7781",
"name": "Harbor Lofts - L2",
"city": "McLean",
"state": "VA",
"destination_zip": "22102"
},
"item": {
"external_id": "48213",
"label": "T-1",
"name": "Calacatta look porcelain 24 x 48",
"item_type": "Tile",
"quantity": 6000,
"uom": "Sq. Ft."
},
"host_origin": "https://app.acme.example"
}'

Response 201:

{
"session_uuid": "c1f0a6de-3b52-4d8e-9c47-2f8a1b6d0e93",
"iframe_url": "https://<iframe-origin>?partner=acme#launch_code=launch_example&session=c1f0a6de-3b52-4d8e-9c47-2f8a1b6d0e93"
}

Change host_origin to the exact origin of your own page before you run the sample. Every field is on Server calls.

Then:

  • Return iframe_url and session_uuid to your page.
  • Use iframe_url at once. Its launch code works once, within 120 seconds.
  • Never build or edit iframe_url yourself.

Create the iframe with this sandbox attribute and no src. The snippet sets src.

<iframe
id="flume"
title="Flume: find alternatives"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
></iframe>

Give it a size. Without CSS an iframe is 300 by 150 pixels.

#flume {
position: fixed;
inset: 0 0 0 auto;
width: min(470px, 100vw);
height: 100dvh;
border: 0;
}

Save the host snippet next to your page:

curl -sSO "https://docs.tryflume.ai/samples/mount-flume.js"

Then call it from a module script. mountFlume returns a function, stop, that removes its listener and timer.

import { mountFlume } from "./mount-flume.js";
const frame = document.querySelector("#flume");
const { iframe_url, session_uuid } = await startSessionOnYourBackend();
function removeFrame() {
stop();
frame.removeAttribute("src");
}
const stop = mountFlume({
frame,
iframeUrl: iframe_url,
sessionUuid: session_uuid,
flumeOrigin: new URL(iframe_url).origin,
handlers: {
hideSpinner() {},
showError() {
removeFrame();
showRetry("Flume did not load.", openFlumeAgain);
},
exchangeConnectCode(connectCode) {
sendToYourBackend("/flume/connect", { connectCode });
},
async saveItem(payload) {
const row = await sendToYourBackend("/flume/save", payload);
return { save_uuid: payload.save_uuid, item_ref: row.id, item_label: row.label };
},
close(reason) {
removeFrame();
if (reason === "expired") openFlumeAgain();
},
},
});

startSessionOnYourBackend, sendToYourBackend, showRetry and openFlumeAgain are your own functions. openFlumeAgain starts a new session and mounts again.

  • If saveItem throws, the snippet replies flume:item_save_failed with reason error. Throw { reason: "storage" } to state a reason.
  • Call stop() whenever your page removes the iframe itself.

The first time, the user verifies their email inside the iframe. Your page then receives flume:user_connected with a connect_code. Send it to your backend and exchange it within 120 seconds.

curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/users/exchange-connect-code" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{
"connect_code": "connect_example_code_0000"
}'

Response 200:

{
"external_ref": "u_8812",
"email": "sarah.chen@example.com",
"access_token": "ut_dev_acme_example_token",
"token_type": "user_token",
"verified_at": "2026-10-16T09:02:11Z",
"account": "LINKED"
}
  1. Check that external_ref is the user who is signed in to your product.
  2. Store access_token against that user. It replaces any token you held for them.
  3. Send it as X-Flume-User-Token on the next POST /start-iframe-session. The user then skips the email step.

When the user saves, your page receives flume:save_item. Its payload is the save payload.

  1. Write it into your own records. Use save_uuid as your idempotency key.
  2. Return { save_uuid, item_ref } from saveItem on success.
  3. Throw on failure. The reasons are permission, conflict, storage and error.

Reply within 60 seconds. After that the iframe treats the save as failed.

Your page opened the iframe, the user verified their email, and one saved item reached your records.