The Flume iframe loads only inside pages on origins you agreed with Flume. Your partner key and user access tokens stay on your server. This page lists what Flume enforces, what your page must allow, and what to trust.
What do you agree with Flume?
Section titled “What do you agree with Flume?”Flume holds three lists per partner. Flume sets them. You cannot read or change them yourself today.
| List | Format | Used for |
|---|---|---|
| Allowed origins | Exact origins: scheme, host and port, no path, no trailing slash | Which pages may frame the iframe. Checked against host_origin on every session start |
| Image hosts | Exact host names | Which hosts Flume may fetch item.image_urls from |
| Redirect hosts | Exact host names | Which hosts a deep link's redirect_uri may name |
- To add or change an entry, ask Flume.
- A change to allowed origins can take up to 60 seconds to apply.
- Sandbox and live each have their own lists.
- A local origin such as
http://localhost:3000can be an allowed origin in the sandbox. See Develop on localhost.
What does Flume enforce on the iframe?
Section titled “What does Flume enforce on the iframe?”Flume sends these headers with the iframe document.
| Header | Value | Effect |
|---|---|---|
Content-Security-Policy | frame-ancestors followed by your allowed origins | Only your pages can frame it |
Content-Security-Policy | frame-ancestors 'none' for an unknown or inactive partner | The frame stays blank |
Cache-Control | no-store | The document is never cached |
Referrer-Policy | no-referrer | The iframe URL is not leaked in a referrer |
X-Content-Type-Options | nosniff | The browser does not guess content types |
Also:
- The iframe runs only when framed. Opened in its own tab, it does not start.
- The iframe's own policy allows scripts only from Flume's own hosts and its analytics host, and blocks nested frames.
- The launch code travels in the URL fragment, which browsers do not send to a server.
- The iframe removes the launch code from its URL as soon as it has read it.
What must your page's CSP allow?
Section titled “What must your page's CSP allow?”If your page sends a Content-Security-Policy, add these.
| Directive | Add | Why |
|---|---|---|
frame-src | The iframe origin | To frame the iframe |
img-src | The origin of the image links | Only if your page shows Flume image links directly |
connect-src | The origin of the image links | Only if your page fetches image bytes with fetch |
- The iframe origin is
new URL(iframe_url).origin. Read it once per environment from a real response, then add it to your policy. - The origin of the image links is the origin of a link in a real payload, for example
new URL(payload.product.gallery[0].urls[key]).origin. Read it once per environment. - Do not assume either origin equals the host your server calls. In the sandbox they can differ.
- Ask Flume before go-live whether those origins are about to change.
- If your page sends
Cross-Origin-Embedder-Policy: require-corp, the browser blocks the iframe.
What must always hold?
Section titled “What must always hold?”Twelve rules. An integration that breaks one is not finished.
| # | Rule | Detail |
|---|---|---|
| 1 | The partner key and every user access token stay on your server. Never in a browser, a client bundle, a log or a commit | Keys and tokens |
| 2 | Only your server calls Flume. A server call cannot be made from a browser | Server calls |
| 3 | Every server call sends X-Flume-Partner-Key. POST /start-iframe-session also sends X-Flume-User-Token whenever you hold a token for the user | Server calls |
| 4 | iframe_url, its launch code and the connect code are single use and short lived. Use each at once. Never store, log, rebuild or edit them | Timeouts and expiry |
| 5 | Read the iframe's origin from iframe_url. Never hardcode it | Set up your page |
| 6 | Drop a message unless its origin, source, version and session_uuid are right. Ignore types you do not know | Messages |
| 7 | Reply to the iframe with its origin as the target origin, never "*" | Messages |
| 8 | Answer every flume:save_item within 60 seconds, with flume:item_saved or flume:item_save_failed, carrying its save_uuid. A repeated save_uuid writes nothing twice | Messages |
| 9 | Exchange every connect code at once. Store the new token over the old one | Users and verification |
| 10 | Run a ready timer of 15 seconds. When it fires, remove the iframe and show an error with a retry | Timeouts and expiry |
| 11 | On 401 USER_TOKEN_INVALID, delete the stored token and start again without it. On 429, wait for Retry-After. Branch on error.code, never on error.message | Errors |
| 12 | The save payload came through the browser. Render its text as text, and do not treat a saved price as a commitment | Below |
The host snippet keeps rules 5 to 8 and 10 for you when you copy it unchanged.
What must your page check?
Section titled “What must your page check?”Your message listener receives every message sent to your window. Make the four checks on each one, and reply with the iframe's origin as the target, never "*". The host snippet does both.
Can you trust the save payload?
Section titled “Can you trust the save payload?”The save payload reaches your backend through the user's browser. Treat it like any other browser input.
- The four message checks prove the message came from the Flume iframe. They do not stop a user from changing a request to your own backend.
- Flume offers no server call to read the payload again. So do not treat a saved price as an order or a commitment. It is what the user saw, valid until
pricing.valid_until. - Check on your server that the item being written belongs to the signed-in user.
- Render every string as text. See Text rules.
Which user does a token belong to?
Section titled “Which user does a token belong to?”POST /users/exchange-connect-code returns external_ref: your own id for the user who verified.
- Compare it with the user who is signed in to your product.
- Store the token only when they match.
- If they differ, discard the token.
Where do credentials live?
Section titled “Where do credentials live?”Where each secret lives and travels. Long-lived secrets stay on servers. Only single-use, short-lived codes pass through the browser.
Read this diagram as text
- Your backend holds the partner key and each user access token. It sends them only to Flume, over HTTPS.
- Flume returns iframe_url, which carries a launch code. The launch code works once, within 120 seconds.
- Your backend hands iframe_url to your page. Your page sets it as the iframe src and stores it nowhere.
- The iframe spends the launch code with Flume and then uses its own short-lived credential.
- When a user verifies, the iframe posts a connect code to your page. It works once, within 120 seconds.
- Your page passes the connect code to your backend, which exchanges it for the user access token.
- Your page accepts a message only when its origin, source, version and session_uuid are right. The iframe posts only to the host_origin you sent.
| Credential | Where it lives | Reaches the browser |
|---|---|---|
| Partner key | Your server | Never |
| User access token | Your server | Never |
| Connect code | Passes through your page once | Yes. Single use, 120 seconds |
Launch code, in iframe_url | Passes through your page once | Yes. Single use, 120 seconds |
| The iframe's own credential | Inside the iframe | Yes, inside the iframe only. Your page cannot read it |
Flume stores every key, token and code as a hash only.
How does Flume fetch your item photos?
Section titled “How does Flume fetch your item photos?”When you send item.image_urls, Flume's server fetches the first photo.
httpsonly.- The host must be on your image host list.
- Redirects are not followed.
- 10 second timeout, 25 MB limit.
- The links must stay reachable for the session's life.
An entry that breaks a rule is a 422 VALIDATION_FAILED naming item.image_urls.<index>.
What should you treat as untrusted?
Section titled “What should you treat as untrusted?”- Every string in the save payload. Flume removes control characters and angle brackets only. Quotes and
&remain. - Render strings as text. Escape them on output. Never build HTML or an HTML attribute from them by joining strings.
- Every message, until it passes the four checks above.
How do you report a security problem?
Section titled “How do you report a security problem?”Contact Flume. Send the key prefix and X-Request-Id values. Never send a key or a token.