Skip to content

Server calls

Your backend makes two calls for every iframe integration, and a few more if you want what they offer. Your page never calls Flume: only your server does.

CallNeededWhen
POST /start-iframe-sessionRequiredThe user clicks your button
POST /users/exchange-connect-codeRequiredYour page receives flume:user_connected
GET an image linkRequired if you keep photosYour save handler runs
POST /deep-linksOptionalThe user clicks "Open in Flume" on a saved item
DELETE /users/{external_ref}/tokenOptionalA user leaves, or a token may have leaked
GET /keys, POST /keys, DELETE /keys/{key_id}Key rotationOn your rotation schedule, or after a leak
  • Base URL: https://sandbox-api.tryflume.ai/partner/v1 in the sandbox. See Environments.
  • Send your partner key in X-Flume-Partner-Key. Read it from configuration, never from source code.
  • Bodies and responses are JSON. Times are UTC. An unknown field in a request returns 422.
  • Every response carries X-Request-Id. Log it.
  • An error has one shape. See Errors.
  • Samples read FLUME_PARTNER_KEY and FLUME_USER_TOKEN from the environment. Ids in samples are examples.
  • Calls from a browser are blocked. See Keys and tokens.
Start a session and mount the iframeFrom the click to a loaded iframe. A user access token, when you hold one, skips the email step. The same content is listed as text under the diagram.UserFlume iframeYour pageYour backendFlume servers1clicks your button2your route3POST /start-iframe-session4iframe_url, session_uuid5iframe_url, session_uuid6ready timer, 15 s7iframe.src8launch code, once9flume:iframe_readyServer call (HTTPS)postMessageThe iframe's own callA person actsAny other step

Start a session and mount the iframe. From the click to a loaded iframe. A user access token, when you hold one, skips the email step.

Read this diagram as text
  1. The user clicks your button for one item.
  2. Your page asks your backend to start a session.
  3. Your backend calls POST /start-iframe-session with the user, project, item and host_origin. It sends the stored user access token when it holds one.
  4. Flume returns iframe_url and session_uuid.
  5. Your backend returns only those two values to your page.
  6. Your page starts a ready timer of 15 seconds and adds the message listener.
  7. Your page sets iframe_url as the iframe src, at once.
  8. The iframe spends the launch code with Flume. It works once, within 120 seconds.
  9. The iframe posts flume:iframe_ready. Your page clears the timer and hides its spinner. The iframe also posts it when it could not start: it then shows its own error, and its Try again posts flume:close_iframe with reason expired.

POST /start-iframe-session. Call it each time a user opens Flume for an item. It returns the URL you set as the iframe src.

  • Send the user's stored access token in X-Flume-User-Token whenever you hold one. The iframe then opens without the email step.
  • Opening the same user, project and item again continues the same work. You still get a new iframe_url each time.
  • Use iframe_url at once. Its launch code works once, within 120 seconds. Never store, log or edit it.
  • Send what you hold about the item. name, quantity, uom, image_urls and project.destination_zip save the user typing.
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/start-iframe-session" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
-H "X-Flume-User-Token: $FLUME_USER_TOKEN" \
-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"
}'

Leave the X-Flume-User-Token header out when you hold no token for the user.

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"
}

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.
X-Flume-User-TokenNoThe user access token you hold for this user. Send it whenever you hold one: the iframe then opens without the email step.
Content-TypeYesapplication/json.

Request fields

FieldTypeRequiredNotes
userobjectYesWho is working.
user.external_refstringYesYour permanent id for the user. Never reuse one. 1 to 128 characters.
user.emailstringYesThe user's email. Flume sends the verification code there.
user.first_namestringNoUp to 128 characters.
user.last_namestringNoUp to 128 characters.
user.phonestringNoA US number in any common format.
user.orgobjectNoThe user's company.
user.org.external_refstringNoYour id for the company.
user.org.namestringNoUp to 255 characters.
user.org.website_domainstringNoFor example acme.example.
user.user_typestringNoThe kind of user: DEVELOPER, GC, SUBCONTRACTOR, ARCHITECT_DESIGNER or OTHER.
projectobjectYesWhere the result is filed in the user's Flume account.
project.external_refstringYesYour permanent id for the project. 1 to 128 characters.
project.namestringNoUp to 255 characters.
project.project_typestringNoRESIDENTIAL, MULTIFAMILY, COMMERCIAL, HOSPITALITY, HEALTHCARE, EDUCATION, RETAIL or OTHER.
project.citystringNoUp to 128 characters.
project.statestringNoThe state, for example TX.
project.destination_zipstringNoThe delivery ZIP code. The iframe uses it to price. Without it, the user types one.
project.estimated_area_sqftintegerNoThe project's total area in square feet.
project.unit_countintegerNoHomes, units or rooms.
project.target_install_datestringNoA date, YYYY-MM-DD.
project.budgetobjectNoThe project's budget per unit.
project.budget.amountstringYesThe budget, as a decimal string.
project.budget.currencystringYesA 3-letter currency code, for example USD.
project.budget.unitstringNoThe unit the amount is per. Match the quantity's unit.
project.start_datestringNoA date, YYYY-MM-DD.
project.attributesobjectNoAnything else you hold. Flume stores it and does not act on it.
itemobjectYesWhat the user wants a product for.
item.external_idstringYesYour id for the item. 1 to 128 characters. It comes back in the save payload.
item.labelstringNoYour short label for the item, for example T-1.
item.namestringNoThe item's name. The iframe shows it and searches with it.
item.item_typestringNoYour own word for the kind of item, for example Tile.
item.descriptionstringNoFree text about the item.
item.labelsarrayNoLabel and value pairs you hold for the item. Up to 100.
item.labels[].labelstringYesThe label, for example Finish.
item.labels[].valuestringNoIts value, for example Matte.
item.roomsarrayNoRooms the item is used in. Up to 100.
item.rooms[].namestringYesThe room's name.
item.rooms[].quantitynumberNoThe quantity for that room.
item.image_urlsarray of stringsNohttps links to photos of the item, on an image host Flume has set for you. Up to 20. Leave it out until Flume has set your image host.
item.document_urlsarray of stringsNohttps links to documents for the item. Up to 20.
item.quantitynumberNoHow much the user needs. The iframe uses it to price.
item.uomstringNoThe unit of quantity, for example Sq. Ft..
item.unit_priceobjectNoThe unit price of the product the item names today. The iframe uses it only to show a saving on each result.
item.unit_price.amountstringYesThe price, as a decimal string.
item.unit_price.currencystringYesA 3-letter currency code, for example USD.
item.unit_price.unitstringNoThe unit the amount is per. Match the quantity's unit.
item.budgetobjectNoThe item's budget per unit.
item.budget.amountstringYesThe budget, as a decimal string.
item.budget.currencystringYesA 3-letter currency code, for example USD.
item.budget.unitstringNoThe unit the amount is per. Match the quantity's unit.
item.need_by_datestringNoA date, YYYY-MM-DD.
item.attributesobjectNoAnything else you hold. Flume stores it and does not act on it.
host_originstringYesThe exact origin of the page that frames the iframe, for example https://app.acme.example. It must be one of your allowed origins.

Response fields

FieldTypeNotes
session_uuidstring (UUID)The session's id. Every message from the iframe carries it. Log it.
iframe_urlstringSet it as the iframe src at once. It holds a launch code that works once.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
401USER_TOKEN_INVALIDThe user access token you sent is unknown, replaced or revoked.Delete the stored token. Start the session again without it. The user verifies again in the iframe.
422VALIDATION_FAILEDA field is missing or unusable. field names it: host_origin when the origin is not an allowed origin, item.image_urls.<i> when a photo link is not https on an image host Flume has set, user.external_ref when the token you sent belongs to another user.Fix the field. Ask Flume to add an origin or an image host. Send each user their own token.

POST /users/exchange-connect-code. Call it when your page receives flume:user_connected. It turns the single-use connect_code into the user's access token.

  • Call it at once. The code works once, within 120 seconds. Never log it.
  • Check that external_ref is the user signed in to your product. If it is not, store nothing.
  • Store access_token on your server, over any token you held for that user. The old one no longer works.
  • Send the stored token on every later POST /start-iframe-session for that user.
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"
}

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.
Content-TypeYesapplication/json.

Request fields

FieldTypeRequiredNotes
connect_codestringYesThe connect_code from the flume:user_connected message.

Response fields

FieldTypeNotes
external_refstringYour id for the user the token belongs to. Check that it is the user signed in to your product.
emailstringThe email the user verified.
access_tokenstringThe user access token. Shown once, in this response. Store it on your server.
token_typestringAlways user_token.
verified_atstringUTC time of the verification.
accountstringCREATED: Flume opened a Flume account for the user. LINKED: the email already had one.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
401CONNECT_CODE_INVALIDThe connect code is used, expired, unknown or another partner's, or the user's verification no longer holds.Nothing to store. The user verifies again on the next open.
422VALIDATION_FAILEDA field is missing, unknown or has an unusable value. field names it.Fix the field and send again.

The save payload holds image links under product.gallery[].urls. They expire after about 24 hours. To keep a photo, your server fetches the bytes when it handles the save.

  • A plain GET on the link. No header and no key: the link itself is the permission.
  • urls is keyed by the longest edge in pixels, as a string. The sizes differ per image, up to 960. Take the largest key at or under the size you show, and never assume a key exists.
  • The response is the image bytes, with its Content-Type.
  • Fetch from your server, store the bytes, and serve your own copy.
  • In the sandbox a link can stop working earlier. Treat a failed fetch as "no photo", not as a failed save.
curl -sS -o photo.jpg "$IMAGE_LINK"

Errors

StatusCodeWhenDo this
404NOT_FOUNDThe image link expired, or the image is gone.Nothing to retry. Keep the record without that photo.
Open in Flume, for a saved itemOptional. A one-time link, created on the click and never stored. The same content is listed as text under the diagram.UserYour pageYour backendFlume servers1clicks Open in Flume2open a blank tab3your route4POST /deep-links5url6url7tab goes to the urlServer call (HTTPS)A person actsAny other step

Open in Flume, for a saved item. Optional. A one-time link, created on the click and never stored.

Read this diagram as text
  1. The user clicks "Open in Flume" on a saved item.
  2. Your page opens a blank tab in the click handler, so the browser does not block it.
  3. Your page asks your backend for a link.
  4. Your backend calls POST /deep-links with the user access token, and the search_session_uuid and sku_uuid it kept from the save payload.
  5. Flume returns a link that works once, within 7 days.
  6. Your backend returns the link. It stores it nowhere.
  7. Your page points the tab at the link. Flume opens with the user signed in. With no token, link to product_url instead.

POST /deep-links. Optional. It returns a one-time link that opens the saved product in the Flume app, with the user already signed in. Use it for an "Open in Flume" button on a saved item.

  • Create the link when the user clicks, and open it at once. It works once, within 7 days. Never store it.
  • Take search_session_uuid from the save payload's match.search_session_uuid, and sku_uuid from product.sku_uuid. Store both with your record at save time.
  • Send target: "product" for a saved item.
  • It needs the user's access token. If you hold none, link to the save payload's product_url instead. That link does not expire, and the user signs in to Flume themselves.
  • Open a blank tab in the click handler, then set its location when your server answers. Otherwise the browser blocks the popup.
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/deep-links" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
-H "X-Flume-User-Token: $FLUME_USER_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f",
"sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30",
"target": "product"
}'

Response 201:

{
"url": "https://<flume-app>/<one-time-path>",
"expires_at": "2026-11-18T10:00:00Z"
}

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.
X-Flume-User-TokenYesThe user access token of the user who clicked.
Idempotency-KeyNoOne value per click, 1 to 255 characters. A retry with the same value does not return the link again: url is null.
Content-TypeYesapplication/json.

Request fields

FieldTypeRequiredNotes
search_session_uuidstring (UUID)YesThe search that found the product. Take it from the save payload: match.search_session_uuid.
sku_uuidstring (UUID)NoThe product to open. Take it from the save payload: product.sku_uuid.
quote_uuidstring (UUID)NoAn instant quote to open, with sku_uuid.
targetstringNoWhat the link opens: product, search or quote. Send product for a saved item.
utm_contentstringNoA name for the button that asked, for your own reporting. Up to 64 characters.
redirect_uristringNoWhere Flume sends the user back to. https, on a redirect host Flume has set for you.

Response fields

FieldTypeNotes
urlstring or nullThe link. Open it at once. It works once. null on a retry with the same Idempotency-Key.
expires_atstringUTC time the link stops working.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
401USER_TOKEN_INVALIDThe user access token you sent is unknown, replaced or revoked.Delete the stored token. Start the session again without it. The user verifies again in the iframe.
403USER_NOT_CONNECTEDThe call carried no user access token.Send the stored token. If you hold none, the user opens the iframe and verifies first.
404SESSION_NOT_FOUNDThe search is not this user's.Use the ids from this user's own save payload.
409IDEMPOTENCY_IN_PROGRESSA call with the same Idempotency-Key is still running.Wait a moment, then retry.
422SKU_NOT_IN_SEARCHThe product was not a result of that search.Send the search_session_uuid and sku_uuid from the same save payload.
422VALIDATION_FAILEDA field is missing, unknown or has an unusable value. field names it.Fix the field and send again.
429RATE_LIMITEDYour daily limit of links is used up.Wait for the number of seconds in Retry-After.

DELETE /users/{external_ref}/token. Optional. It stops the user's access token at once, and ends every iframe session opened with it. Use it when a user leaves your product or a token may have leaked. Then delete your stored token. The user verifies again on the next open.

curl -X DELETE "https://sandbox-api.tryflume.ai/partner/v1/users/u_8812/token" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"

Response 204, with no body.

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
404NOT_FOUNDYou have no user with that external_ref.Nothing to revoke. Delete your stored token.

Flume issues your first key. After that you rotate it yourself, with three calls.

  1. POST /keys issues a new key. The key you call with keeps working for overlap_days.
  2. Store the new secret. It is shown once. Deploy it.
  3. GET /keys lists your keys. Find the old key's key_id.
  4. DELETE /keys/{key_id} revokes the old key at once, when nothing uses it.
LimitSandboxLive
Active keys52
Keys issued in 24 hours101

If a key leaks: rotate with overlap_days: 0, deploy the new key, and tell Flume.

curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/keys" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"label": "rotation 2026-11",
"overlap_days": 7
}'

Response 201:

{
"key_id": "pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d",
"prefix": "sk_dev_acme_9d2",
"label": "rotation 2026-11",
"status": "ACTIVE",
"created_at": "2026-11-11T10:00:00Z",
"last_used_at": null,
"expires_at": null,
"secret": "sk_dev_acme_example_key"
}

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.
Idempotency-KeyYesA new value for each rotation, for example a UUID.
Content-TypeYesapplication/json.

Request fields

FieldTypeRequiredNotes
labelstringNoA name for the new key. Up to 128 characters.
overlap_daysintegerNoHow many days the key you call with keeps working. Default 7, at most 30.

Response fields

FieldTypeNotes
key_idstringThe key's id. Use it to revoke the key. Not a secret.
prefixstringHow the key starts. Safe to log.
labelstring or nullThe label you gave it.
statusstringACTIVE, EXPIRING, REVOKED or EXPIRED. An EXPIRING key was replaced and works until expires_at.
created_atstringUTC time.
last_used_atstring or nullUTC time of the last call made with it.
expires_atstring or nullUTC time the key stops working, when it has one.
secretstring or nullThe new key. Shown once, in this response. null on a retry with the same Idempotency-Key.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
409KEY_LIMIT_REACHEDYou already have the most active keys allowed.Revoke a key you no longer use.
429KEY_ROTATION_RATE_LIMITEDA key was issued too recently. The current key keeps working.Wait for the number of seconds in Retry-After.
422VALIDATION_FAILEDA field is missing, unknown or has an unusable value. field names it.Fix the field and send again.
curl -X GET "https://sandbox-api.tryflume.ai/partner/v1/keys" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"

Response 200:

{
"keys": [
{
"key_id": "pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d",
"prefix": "sk_dev_acme_9d2",
"label": "rotation 2026-11",
"status": "ACTIVE",
"created_at": "2026-11-11T10:00:00Z",
"last_used_at": null,
"expires_at": null
}
]
}

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.

Response fields

FieldTypeNotes
keysarrayYour keys, with their status. Never a secret.
keys[].key_idstringThe key's id. Use it to revoke the key. Not a secret.
keys[].prefixstringHow the key starts. Safe to log.
keys[].labelstring or nullThe label you gave it.
keys[].statusstringACTIVE, EXPIRING, REVOKED or EXPIRED. An EXPIRING key was replaced and works until expires_at.
keys[].created_atstringUTC time.
keys[].last_used_atstring or nullUTC time of the last call made with it.
keys[].expires_atstring or nullUTC time the key stops working, when it has one.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
curl -X DELETE "https://sandbox-api.tryflume.ai/partner/v1/keys/pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d" \
-H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"

Response 204, with no body.

Headers

HeaderRequiredNotes
X-Flume-Partner-KeyYesYour partner key.

Errors

StatusCodeWhenDo this
401PARTNER_KEY_INVALIDThe partner key is missing, unknown, revoked or expired.Check the key and the environment it belongs to.
404NOT_FOUNDYou have no key with that key_id.Read the id from the list of keys.
409LAST_ACTIVE_KEYIt is your only working key.Rotate first, then revoke the old key.

These are the calls the iframe needs. The same key also opens the rest of the Partner API: search, products, instant quotes and saves from your own backend. You can combine the two, for example the iframe for search and the API to read a saved quote again.