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.
| Call | Needed | When |
|---|---|---|
POST /start-iframe-session | Required | The user clicks your button |
POST /users/exchange-connect-code | Required | Your page receives flume:user_connected |
GET an image link | Required if you keep photos | Your save handler runs |
POST /deep-links | Optional | The user clicks "Open in Flume" on a saved item |
DELETE /users/{external_ref}/token | Optional | A user leaves, or a token may have leaked |
GET /keys, POST /keys, DELETE /keys/{key_id} | Key rotation | On your rotation schedule, or after a leak |
Rules for every call
Section titled “Rules for every call”- Base URL:
https://sandbox-api.tryflume.ai/partner/v1in 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_KEYandFLUME_USER_TOKENfrom the environment. Ids in samples are examples. - Calls from a browser are blocked. See Keys and tokens.
Start a session
Section titled “Start a session”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
- The user clicks your button for one item.
- Your page asks your backend to start a session.
- 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.
- Flume returns iframe_url and session_uuid.
- Your backend returns only those two values to your page.
- Your page starts a ready timer of 15 seconds and adds the message listener.
- Your page sets iframe_url as the iframe src, at once.
- The iframe spends the launch code with Flume. It works once, within 120 seconds.
- 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-Tokenwhenever 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_urleach time. - Use
iframe_urlat 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_urlsandproject.destination_zipsave 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"}'const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/start-iframe-session", { method: "POST", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, "X-Flume-User-Token": process.env.FLUME_USER_TOKEN, "Content-Type": "application/json", }, body: JSON.stringify({ "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" }),});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());import os
import requests
response = requests.post( "https://sandbox-api.tryflume.ai/partner/v1/start-iframe-session", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], "X-Flume-User-Token": os.environ["FLUME_USER_TOKEN"], }, json={ "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" }, timeout=30,)response.raise_for_status()print(response.json())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
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
X-Flume-User-Token | No | The user access token you hold for this user. Send it whenever you hold one: the iframe then opens without the email step. |
Content-Type | Yes | application/json. |
Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
user | object | Yes | Who is working. |
user.external_ref | string | Yes | Your permanent id for the user. Never reuse one. 1 to 128 characters. |
user.email | string | Yes | The user's email. Flume sends the verification code there. |
user.first_name | string | No | Up to 128 characters. |
user.last_name | string | No | Up to 128 characters. |
user.phone | string | No | A US number in any common format. |
user.org | object | No | The user's company. |
user.org.external_ref | string | No | Your id for the company. |
user.org.name | string | No | Up to 255 characters. |
user.org.website_domain | string | No | For example acme.example. |
user.user_type | string | No | The kind of user: DEVELOPER, GC, SUBCONTRACTOR, ARCHITECT_DESIGNER or OTHER. |
project | object | Yes | Where the result is filed in the user's Flume account. |
project.external_ref | string | Yes | Your permanent id for the project. 1 to 128 characters. |
project.name | string | No | Up to 255 characters. |
project.project_type | string | No | RESIDENTIAL, MULTIFAMILY, COMMERCIAL, HOSPITALITY, HEALTHCARE, EDUCATION, RETAIL or OTHER. |
project.city | string | No | Up to 128 characters. |
project.state | string | No | The state, for example TX. |
project.destination_zip | string | No | The delivery ZIP code. The iframe uses it to price. Without it, the user types one. |
project.estimated_area_sqft | integer | No | The project's total area in square feet. |
project.unit_count | integer | No | Homes, units or rooms. |
project.target_install_date | string | No | A date, YYYY-MM-DD. |
project.budget | object | No | The project's budget per unit. |
project.budget.amount | string | Yes | The budget, as a decimal string. |
project.budget.currency | string | Yes | A 3-letter currency code, for example USD. |
project.budget.unit | string | No | The unit the amount is per. Match the quantity's unit. |
project.start_date | string | No | A date, YYYY-MM-DD. |
project.attributes | object | No | Anything else you hold. Flume stores it and does not act on it. |
item | object | Yes | What the user wants a product for. |
item.external_id | string | Yes | Your id for the item. 1 to 128 characters. It comes back in the save payload. |
item.label | string | No | Your short label for the item, for example T-1. |
item.name | string | No | The item's name. The iframe shows it and searches with it. |
item.item_type | string | No | Your own word for the kind of item, for example Tile. |
item.description | string | No | Free text about the item. |
item.labels | array | No | Label and value pairs you hold for the item. Up to 100. |
item.labels[].label | string | Yes | The label, for example Finish. |
item.labels[].value | string | No | Its value, for example Matte. |
item.rooms | array | No | Rooms the item is used in. Up to 100. |
item.rooms[].name | string | Yes | The room's name. |
item.rooms[].quantity | number | No | The quantity for that room. |
item.image_urls | array of strings | No | https 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_urls | array of strings | No | https links to documents for the item. Up to 20. |
item.quantity | number | No | How much the user needs. The iframe uses it to price. |
item.uom | string | No | The unit of quantity, for example Sq. Ft.. |
item.unit_price | object | No | The unit price of the product the item names today. The iframe uses it only to show a saving on each result. |
item.unit_price.amount | string | Yes | The price, as a decimal string. |
item.unit_price.currency | string | Yes | A 3-letter currency code, for example USD. |
item.unit_price.unit | string | No | The unit the amount is per. Match the quantity's unit. |
item.budget | object | No | The item's budget per unit. |
item.budget.amount | string | Yes | The budget, as a decimal string. |
item.budget.currency | string | Yes | A 3-letter currency code, for example USD. |
item.budget.unit | string | No | The unit the amount is per. Match the quantity's unit. |
item.need_by_date | string | No | A date, YYYY-MM-DD. |
item.attributes | object | No | Anything else you hold. Flume stores it and does not act on it. |
host_origin | string | Yes | The 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
| Field | Type | Notes |
|---|---|---|
session_uuid | string (UUID) | The session's id. Every message from the iframe carries it. Log it. |
iframe_url | string | Set it as the iframe src at once. It holds a launch code that works once. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
| 401 | USER_TOKEN_INVALID | The 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. |
| 422 | VALIDATION_FAILED | A 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. |
Exchange a connect code
Section titled “Exchange a connect code”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_refis the user signed in to your product. If it is not, store nothing. - Store
access_tokenon 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-sessionfor 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"}'const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/users/exchange-connect-code", { method: "POST", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ "connect_code": "connect_example_code_0000" }),});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());import os
import requests
response = requests.post( "https://sandbox-api.tryflume.ai/partner/v1/users/exchange-connect-code", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], }, json={ "connect_code": "connect_example_code_0000" }, timeout=30,)response.raise_for_status()print(response.json())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
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
Content-Type | Yes | application/json. |
Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
connect_code | string | Yes | The connect_code from the flume:user_connected message. |
Response fields
| Field | Type | Notes |
|---|---|---|
external_ref | string | Your id for the user the token belongs to. Check that it is the user signed in to your product. |
email | string | The email the user verified. |
access_token | string | The user access token. Shown once, in this response. Store it on your server. |
token_type | string | Always user_token. |
verified_at | string | UTC time of the verification. |
account | string | CREATED: Flume opened a Flume account for the user. LINKED: the email already had one. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
| 401 | CONNECT_CODE_INVALID | The 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. |
| 422 | VALIDATION_FAILED | A field is missing, unknown or has an unusable value. field names it. | Fix the field and send again. |
Fetch an image
Section titled “Fetch an image”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
GETon the link. No header and no key: the link itself is the permission. urlsis keyed by the longest edge in pixels, as a string. The sizes differ per image, up to960. 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
| Status | Code | When | Do this |
|---|---|---|---|
| 404 | NOT_FOUND | The image link expired, or the image is gone. | Nothing to retry. Keep the record without that photo. |
Create a link that opens Flume
Section titled “Create a link that opens Flume”Open in Flume, for a saved item. Optional. A one-time link, created on the click and never stored.
Read this diagram as text
- The user clicks "Open in Flume" on a saved item.
- Your page opens a blank tab in the click handler, so the browser does not block it.
- Your page asks your backend for a link.
- 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.
- Flume returns a link that works once, within 7 days.
- Your backend returns the link. It stores it nowhere.
- 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_uuidfrom the save payload'smatch.search_session_uuid, andsku_uuidfromproduct.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_urlinstead. 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"}'const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/deep-links", { method: "POST", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, "X-Flume-User-Token": process.env.FLUME_USER_TOKEN, "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json", }, body: JSON.stringify({ "search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f", "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30", "target": "product" }),});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());import osimport uuid
import requests
response = requests.post( "https://sandbox-api.tryflume.ai/partner/v1/deep-links", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], "X-Flume-User-Token": os.environ["FLUME_USER_TOKEN"], "Idempotency-Key": str(uuid.uuid4()), }, json={ "search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f", "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30", "target": "product" }, timeout=30,)response.raise_for_status()print(response.json())Response 201:
{ "url": "https://<flume-app>/<one-time-path>", "expires_at": "2026-11-18T10:00:00Z"}Headers
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
X-Flume-User-Token | Yes | The user access token of the user who clicked. |
Idempotency-Key | No | One value per click, 1 to 255 characters. A retry with the same value does not return the link again: url is null. |
Content-Type | Yes | application/json. |
Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
search_session_uuid | string (UUID) | Yes | The search that found the product. Take it from the save payload: match.search_session_uuid. |
sku_uuid | string (UUID) | No | The product to open. Take it from the save payload: product.sku_uuid. |
quote_uuid | string (UUID) | No | An instant quote to open, with sku_uuid. |
target | string | No | What the link opens: product, search or quote. Send product for a saved item. |
utm_content | string | No | A name for the button that asked, for your own reporting. Up to 64 characters. |
redirect_uri | string | No | Where Flume sends the user back to. https, on a redirect host Flume has set for you. |
Response fields
| Field | Type | Notes |
|---|---|---|
url | string or null | The link. Open it at once. It works once. null on a retry with the same Idempotency-Key. |
expires_at | string | UTC time the link stops working. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
| 401 | USER_TOKEN_INVALID | The 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. |
| 403 | USER_NOT_CONNECTED | The call carried no user access token. | Send the stored token. If you hold none, the user opens the iframe and verifies first. |
| 404 | SESSION_NOT_FOUND | The search is not this user's. | Use the ids from this user's own save payload. |
| 409 | IDEMPOTENCY_IN_PROGRESS | A call with the same Idempotency-Key is still running. | Wait a moment, then retry. |
| 422 | SKU_NOT_IN_SEARCH | The product was not a result of that search. | Send the search_session_uuid and sku_uuid from the same save payload. |
| 422 | VALIDATION_FAILED | A field is missing, unknown or has an unusable value. field names it. | Fix the field and send again. |
| 429 | RATE_LIMITED | Your daily limit of links is used up. | Wait for the number of seconds in Retry-After. |
Revoke a user's access token
Section titled “Revoke a user's access token”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"const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/users/u_8812/token", { method: "DELETE", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, },});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(response.status);import os
import requests
response = requests.delete( "https://sandbox-api.tryflume.ai/partner/v1/users/u_8812/token", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], }, timeout=30,)response.raise_for_status()print(response.status_code)Response 204, with no body.
Headers
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
| 404 | NOT_FOUND | You have no user with that external_ref. | Nothing to revoke. Delete your stored token. |
Rotate your key
Section titled “Rotate your key”Flume issues your first key. After that you rotate it yourself, with three calls.
POST /keysissues a new key. The key you call with keeps working foroverlap_days.- Store the new
secret. It is shown once. Deploy it. GET /keyslists your keys. Find the old key'skey_id.DELETE /keys/{key_id}revokes the old key at once, when nothing uses it.
| Limit | Sandbox | Live |
|---|---|---|
| Active keys | 5 | 2 |
| Keys issued in 24 hours | 10 | 1 |
If a key leaks: rotate with overlap_days: 0, deploy the new key, and tell Flume.
Issue a new key
Section titled “Issue a new key”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}'const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/keys", { method: "POST", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json", }, body: JSON.stringify({ "label": "rotation 2026-11", "overlap_days": 7 }),});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());import osimport uuid
import requests
response = requests.post( "https://sandbox-api.tryflume.ai/partner/v1/keys", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], "Idempotency-Key": str(uuid.uuid4()), }, json={ "label": "rotation 2026-11", "overlap_days": 7 }, timeout=30,)response.raise_for_status()print(response.json())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
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
Idempotency-Key | Yes | A new value for each rotation, for example a UUID. |
Content-Type | Yes | application/json. |
Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
label | string | No | A name for the new key. Up to 128 characters. |
overlap_days | integer | No | How many days the key you call with keeps working. Default 7, at most 30. |
Response fields
| Field | Type | Notes |
|---|---|---|
key_id | string | The key's id. Use it to revoke the key. Not a secret. |
prefix | string | How the key starts. Safe to log. |
label | string or null | The label you gave it. |
status | string | ACTIVE, EXPIRING, REVOKED or EXPIRED. An EXPIRING key was replaced and works until expires_at. |
created_at | string | UTC time. |
last_used_at | string or null | UTC time of the last call made with it. |
expires_at | string or null | UTC time the key stops working, when it has one. |
secret | string or null | The new key. Shown once, in this response. null on a retry with the same Idempotency-Key. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
| 409 | KEY_LIMIT_REACHED | You already have the most active keys allowed. | Revoke a key you no longer use. |
| 429 | KEY_ROTATION_RATE_LIMITED | A key was issued too recently. The current key keeps working. | Wait for the number of seconds in Retry-After. |
| 422 | VALIDATION_FAILED | A field is missing, unknown or has an unusable value. field names it. | Fix the field and send again. |
List your keys
Section titled “List your keys”curl -X GET "https://sandbox-api.tryflume.ai/partner/v1/keys" \ -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/keys", { method: "GET", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, },});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());import os
import requests
response = requests.get( "https://sandbox-api.tryflume.ai/partner/v1/keys", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], }, timeout=30,)response.raise_for_status()print(response.json())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
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
Response fields
| Field | Type | Notes |
|---|---|---|
keys | array | Your keys, with their status. Never a secret. |
keys[].key_id | string | The key's id. Use it to revoke the key. Not a secret. |
keys[].prefix | string | How the key starts. Safe to log. |
keys[].label | string or null | The label you gave it. |
keys[].status | string | ACTIVE, EXPIRING, REVOKED or EXPIRED. An EXPIRING key was replaced and works until expires_at. |
keys[].created_at | string | UTC time. |
keys[].last_used_at | string or null | UTC time of the last call made with it. |
keys[].expires_at | string or null | UTC time the key stops working, when it has one. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
Revoke a key
Section titled “Revoke a key”curl -X DELETE "https://sandbox-api.tryflume.ai/partner/v1/keys/pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d" \ -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/keys/pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d", { method: "DELETE", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, },});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(response.status);import os
import requests
response = requests.delete( "https://sandbox-api.tryflume.ai/partner/v1/keys/pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d", headers={ "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], }, timeout=30,)response.raise_for_status()print(response.status_code)Response 204, with no body.
Headers
| Header | Required | Notes |
|---|---|---|
X-Flume-Partner-Key | Yes | Your partner key. |
Errors
| Status | Code | When | Do this |
|---|---|---|---|
| 401 | PARTNER_KEY_INVALID | The partner key is missing, unknown, revoked or expired. | Check the key and the environment it belongs to. |
| 404 | NOT_FOUND | You have no key with that key_id. | Read the id from the list of keys. |
| 409 | LAST_ACTIVE_KEY | It is your only working key. | Rotate first, then revoke the old key. |
What else can your backend call?
Section titled “What else can your backend call?”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.