Skip to main content
The UUID-based browser examples require the MEMBER_STOREFRONT company flag, the matching member-query API deployment, and Member Storefront SDK 0.12.0 or later. Confirm availability with Fluid before enabling it. The global client below also requires the member-page loader deployment and the published SDK release.
With the browser helper, a section or widget can query an installed Droplet’s backend on a signed-in member’s account-host page. Fluid authenticates the member, checks the Droplet installation, and signs the backend request. The section receives the returned JSON and renders it in the browser. The browser helper covers member storefront sections and app blocks. It does not enable private queries on public storefront pages, Portal, or mobile apps. It does not give the section a member bearer token, the Droplet webhook secret, or backend credentials. For Portal widget workers, follow Portal Droplet data. That workflow uses the built-in Portal queryDropletData capability once its SDK and backend are deployed; the member-storefront browser helper is not the Portal API. This connection reuses the existing Droplet backend signing scheme and webhook secret. The member-storefront flow adds member-session and Droplet-installation checks before forwarding the authenticated member and company context. Your backend still verifies the request and authorizes access to that member’s data.

Member API alternative for authenticated applications

The common Droplet UUID selector requires its backend deployment. Member-page examples additionally require the UUID-aware Liquid helper and Member Storefront SDK 0.12.0; canonical data_url also requires its backend deployment. Confirm deployment and published reference availability before using these examples. The browser helper keeps its cookie-based authentication, and existing extension-based requests remain a compatibility path.
An authenticated application or trusted server acting for a member can use the Member API alternative after release. Use a supported writable member credential; an email-link credential is not a replacement. The member credential determines the company and member; a storefront cookie, CSRF token, or browser-helper session context does not replace that credential. The Member API path does not require a member site or the browser helper’s company flag. The Droplet backend must still authorize the signed member’s requested read operation. Keep member credentials in the application’s authenticated request flow. Never put a bearer token in Liquid output, section settings, an extension ZIP, or the browser example below. The existing window.Fluid.droplets.createQueryClient helper continues to use the same-origin member session and its invalidation checks; it does not obtain or expose a member bearer token. The backend’s signed-request verification, tenant mapping, operation allowlist, and private-data permissions remain necessary for either path. Operation names and returned data belong to your Droplet; adding this API does not connect a native widget or supply a built-in commissions provider. Clear private results when the application’s member changes or signs out, discard late results from the previous member, and do not put them in shared caches or persistent browser storage.

Use one Droplet selector for applications and widgets

Use the Droplet UUID selector for both authenticated applications and member-page widgets. Copy the actual public uuid returned when you create or list the Droplet; it may use a drp_ prefix. Do not substitute a database UUID or an installation identifier. The request target is droplet_uuid, and the browser helper receives it through dropletUuid. Legacy extension selectors remain supported for compatibility; do not provide both selectors. A Droplet UUID query requires an active Droplet installation in the authenticated member’s company and a Droplet that is available and permitted to operate. The UUID target does not require a theme extension or publication. Member API callers do not need a placed widget or member site. Browser callers still need a permitted member page, live cookie session, CSRF and the existing member-storefront company flag. Legacy extension-targeted requests retain their extension checks.
  1. Register the receiver. Configure the Droplet’s data_url and securely provision its existing webhook secret on your backend. The backend setup applies to either query target.
  2. Install the Droplet. Verify an active installation in the member’s company. A Member API integration does not need a theme-extension ZIP or a UI placement.
  3. Define the reads. Implement application-owned operation names and input/result shapes on the receiver. Verify the signature, resolve the signed company and member together, and authorize each read. This connection does not permit backend writes.
  4. Query as the member. Use the supported Member API authentication flow and the Droplet’s actual public identifier. Keep member credentials in the authenticated application, validate the returned fields, and clear private results when the member changes or signs out.
Droplet UUID queries omit widget_type from the signed backend request; legacy extension-targeted requests retain it. Your receiver must support both shapes when you offer both paths. Neither a supplied operation nor an extension identifier is proof of permission: verify the signed body and apply the member’s permissions before returning data. Use Member APIs for the supported authentication model and the generated API reference for the exact operation contract once published. The Liquid and JavaScript examples below describe the member-page browser adapter; they do not expose member credentials.

What the browser helper needs

Your section or widget supplies the markup and JavaScript; your backend supplies the private data. Keep credentials for Exigo or any other data source on your backend. The browser renders only the result authorized for the current member. See package and publish an extension and place it on a member page for the section setup.

How the data reaches the section

  1. The section calls client.query("commissions.summary", {}).
  2. The helper obtains same-origin session and CSRF information, then submits the operation and Droplet UUID to Fluid.
  3. Fluid checks the live member/site access, member-storefront flag, active Droplet installation and Droplet availability. Extension publication checks apply only to the legacy extension target.
  4. Fluid signs the server-derived member and company context, then posts to the Droplet’s registered data_url.
  5. Your backend verifies the signature and timestamp, checks the member’s permissions, and returns a JSON object.
  6. The helper checks that the login has not changed. Your section renders the object or an empty/error state.
The section chooses an operation name, not a backend URL or route. You can add commissions.history alongside commissions.summary in your backend and section code without adding a Fluid route.

Prepare the backend

Configure your Droplet’s data_url with one publicly reachable HTTPS endpoint. Keep its existing webhook secret on your backend. Fluid refuses private-network destinations and redirects.

Register the destination and save the secret

On releases supporting the new name, use data_url in new integrations. The deprecated widget_data_url alias remains accepted and returned for compatibility, and both names identify the same receiver. If you send both names, their values must match; conflicting values are refused. These examples use data_url; confirm support before applying them to an older deployment. Use the v2026-10 company droplets API in your integration to configure data_url, for example https://commissions.northwind.example/member-data. Droplet creation and updates accept this field. See Create a droplet for your company and Update a droplet. Changing the endpoint in your section’s settings does not register a backend destination. To obtain the Droplet’s webhook_secret on v2026-10, rotate it with POST /api/company/v2026-10/droplets/{uuid}/webhook-secret-rotations (Rotate a droplet’s webhook secret). Only the company that publishes the Droplet can rotate it. The response returns the new secret once, and no other v2026-10 response returns it. For external hosting, save it securely at that point. Rotation replaces the secret immediately: every webhook and data request Fluid signs afterward uses the new one. A lifecycle webhook already queued when you rotate is still signed with the old secret. Update every backend that verifies with the old secret straight away. Confirm that your backend has the current secret before testing an existing Droplet. The data request uses this Droplet-level secret for signature verification. Installation access tokens have a separate purpose when your backend calls Fluid APIs. Keep both credentials on the server. embed_url is the Droplet’s embedded application UI address. Install and uninstall webhook URLs receive lifecycle events. The member-data request goes to data_url. Deploy a handler at the exact registered URL and preserve its raw request body until signature verification finishes. For local development, register your HTTPS tunnel’s endpoint and keep the secret in the local backend environment. Mist-hosted backend. Attach the Droplet to your provisioned Mist app using Mist’s Droplet connection workflow. The attachment syncs the Droplet’s webhook_secret into both FLUID_DROPLET_SECRET and FLUID_WEBHOOK_AUTH_TOKEN, alongside FLUID_DROPLET_UUID. Rotating the secret writes the new value to both variables and redeploys the app. If the app has no linked GitHub repository, it gets the new values but isn’t redeployed, so deploy it yourself after rotating. Use process.env.FLUID_DROPLET_SECRET as the secret argument to the raw-body verifier. Both secret variables contain the same Droplet-level signing key already used for lifecycle webhooks; member-data requests need no new secret. Attaching the Droplet queues a redeployment when the hosting project has a linked GitHub repository. If no repository is linked, the variables are synced but a redeployment is not queued. Wait for a successful deployment with the synced variables before testing the receiver. Mist’s list_mist_env_vars tool lets an agent check which hosted environment keys exist without returning their plaintext values. These keys are Fluid-managed: do not generate a replacement or try to overwrite them with set_mist_env_var. Externally hosted backend. Store the webhook secret from the rotation response in your hosting provider’s server-side secret storage and make it available to your verifier. Fluid’s Mist attachment flow does not provision an independently hosted backend. Local development. Supply the same Droplet secret separately in your local backend environment, using an authorized secret source. fluid mist env pull skips Mist-managed variables, including FLUID_DROPLET_SECRET; it does not download this secret for you. If you cannot obtain the current secret securely, report the missing local setup instead of generating a different signing key. Don’t rotate the secret only for local testing: rotation replaces the key every deployed backend verifies with. The signed JSON has this shape:
company identifies the requesting member’s company, which can differ from the Droplet publisher. Treat the member identifiers as opaque. Use the signed tenant plus member.public_id for your mapping, or a verified tenant-scoped mapping of member.external_id. The external ID may be null. member.id and member.public_id are separate identifiers and can differ. Do not require equality or substitute one for the other. If your mapping was built from a customer record’s member_id, match the signed member.id within the signed company instead. Use one identity field consistently and deny unknown mappings. For legacy extension-targeted queries, widget_type identifies the installed extension template. Droplet UUID queries omit it. It is not proof that a particular script initiated the call. Values under params are untrusted operation input, even though their bytes are covered by the signature. Never use an ID inside params to override the signed company or member.

Verify the raw body

Verify X-Fluid-Signature using the Droplet webhook secret and HMAC-SHA256 over the exact bytes of X-Fluid-Timestamp + "." + raw_body. Do this before JSON parsing or any data lookup. Compare signatures in constant time and reject timestamps outside your accepted freshness window. Use verifySignature from the existing @fluid-app/droplet-sdk package on your Node backend. It checks the signature in constant time and uses a five-minute freshness window by default. Inside your request handler, pass the raw bytes and the existing Droplet secret:
Use the low-level verifySignature export for this data receiver. The SDK’s withFluidWebhook wrapper handles webhook events and their secret selection; the member-data request keeps its operation/company/member payload and uses the Droplet-level secret. Read the original body as a buffer. Parsing and re-serializing JSON can change whitespace or key order and invalidate the signature. After verification, check that X-Fluid-Shop matches the signed company.fluid_shop; the header alone is not identity proof. Reject unknown operations, validate parameters for each operation, and authorize the signed member against your own tenant records before looking up commissions. Return only the data that member may see. Return a JSON object; Fluid refuses non-object, oversized, failed, or invalid backend responses. Use this connection for read-only operations. Timestamp freshness limits replay but does not make requests single-use. Signature verification is not replay protection for payments or other mutations.

Dispatch operations to your own APIs

Use one registered endpoint as the entry point to your backend’s read operations. The section and backend agree on operation names such as commissions.summary and commissions.history. Your backend can call different internal APIs for each operation; Fluid continues to use the same registered URL. The example below runs after signature/freshness verification and JSON parsing. backend represents your application code: implement resolveMember, summary, and history using your own tenant mappings, permission checks, and data-source credentials.
Implement resolveMember as a lookup scoped to both supplied identifiers, and enforce the member’s data permissions in your backend. For an Exigo-backed integration, use that mapping to select the correct Exigo tenant and member before calling Exigo with server-held credentials. Map handler exceptions to a rejected HTTP response without exposing credentials or private error details. For example, summary can return { "total": "125.00", "currency": "USD" }; that is the object the section receives from client.query. Return the operation result directly from your endpoint rather than adding your own data envelope around it. To add another read operation, implement and authorize it in your backend, then call its name from the section and render its response. This does not require a new Fluid route or a new member token in the browser.

Render the result in a section

Place a small HTML shell in your section or app block. In a trusted Droplet extension, use {% droplet_data_attributes %} on the root to identify its owning Droplet:
The tag emits data-droplet-data, escaped data-droplet-uuid containing the owning Droplet’s public identifier, and legacy data-extension metadata containing the current section or block URI. It accepts no arguments, so a Droplet extension does not need to hardcode its own identifier. Snippets rendered inside the placement inherit its URI; nested app blocks use their own URI. Outside an extension section or block, the tag emits nothing. The tag outputs public metadata only. It does not fetch member data, embed credentials, load JavaScript, or authorize requests. The JavaScript client and backend checks below still apply. Use the tag after the UUID-aware Liquid helper is deployed. For a native widget or custom Liquid root outside a Droplet extension, configure the known public Droplet UUID explicitly and expose it as data-droplet-uuid. The tag does not discover or choose a Droplet for such a root. For an older deployment that supports only extension queries, keep the authorized published template’s actual extension_uri in data-extension and pass extension: root.dataset.extension instead of dropletUuid. Do not pass both options. This is an explicit legacy integration, not automatic fallback after a UUID query fails; native/custom roots must not invent an extension identifier. Fluid exposes window.Fluid.droplets.createQueryClient before theme scripts run on member pages when MEMBER_STOREFRONT is enabled. You do not need an SDK import, CDN URL, version string, or extra script tag. Await client creation: the first call loads the SDK, and concurrent sections share that module load while receiving separate clients. A failed SDK load rejects client creation; show an error and let the member reload the page. If the section is removed while loading, client creation is refused. After the stable-loader deployment, Fluid loads the client through a stable URL that advances to verified SDK releases. You do not need to update your section for each SDK version. An open page keeps its loaded SDK until you reload it. The global loader also works on member pages without a layout. It preserves other window.Fluid helpers and is not installed on public storefront pages or when MEMBER_STOREFRONT is disabled. Loading the SDK does not grant access to member data; the existing session, CSRF, installation and backend checks still apply. Load this JavaScript once from your section or widget’s script asset. Each placement gets its own client:
Use textContent or your framework’s escaped rendering for backend values. Do not insert them with innerHTML. Replace the JSON display with your commission card after validating the expected response fields. The latest query on one client wins. You can pass { signal: abortController.signal } as the third argument to cancel explicitly, and call client.destroy() for teardown. The helper aborts detached requests and invalidates content on page exit, focus/visibility changes, or loss of the session cookie. One shared session check every 15 seconds also clears rendered results when the login context changes, including a rapid account switch that leaves the presence cookie set. Failed or timed-out session checks clear private results. Browser throttling can delay periodic checks. Its onInvalidate callback must clear every private value your section rendered. The helper does not persist responses. Do not put private results in local storage, shared query caches, template settings, or generated HTML. Sections share the page with other scripts; this connection does not provide iframe isolation between them.

Backend errors

With the error-relay API deployment and SDK 0.11.2 or later, Fluid preserves the Droplet backend’s 4xx/5xx HTTP status: a backend 403 stays 403. The standard error response includes the bounded backend JSON or text body at error.details.backend_response. In a section’s catch handler, use error.status and error.response.error.details.backend_response; error.response is optional because transport and session failures may have no backend response. The SDK rechecks the login before exposing backend error contents. Clear rendered errors in onInvalidate, just like successful data, and render them as text. Only return errors that are suitable for the requesting member to see. Redirects, connection failures, invalid successful responses and replies exceeding 256 KiB remain gateway errors; backend response headers are not forwarded.

Builder and CLI previews

Use sample data in previews. Set preview: true when creating a client in a sample render; queries from that client are refused. The helper also refuses iframe and member-preview URL contexts. Test real member data on a live account-host page with a real login.

Place it in the builder or directly in Liquid

For a native widget or custom Liquid root, keep its existing placement and explicitly configure the installed Droplet’s known public UUID for the query client. No Droplet theme-extension upload is needed for this path, and Fluid does not automatically select a Droplet for a native widget. When using a Droplet-owned theme extension, install the Droplet, publish that extension, and open the member page in the builder. Choose the section in Widgets, place it, set its options, and save/publish the page. An app block needs a host section that accepts @app blocks. See builder placement. For an agent placing that Droplet extension directly in theme files, use the same extension URI in the literal tag and the existing page schema:
Merge this entry into the page’s existing schema instead of creating a second schema block or replacing other sections. The URI contains the extension ID, not the Droplet ID. Push and publish the member page through the theme CLI. See direct Liquid placement.

Verify the complete connection

Test with two signed-in members mapped to different backend records. Each must see only their own values. Then test sign-out during a slow response, a rapid account switch while the page stays open, revoked installation/site access, a disabled company flag, malformed signatures, stale timestamps, unknown operations, and identity fields injected into params. For local development, expose your local backend through a public HTTPS tunnel. Keep destination checks enabled; a localhost URL is intentionally refused. An unsigned request sent directly to the tunnel must fail. For the browser helper, the existing MEMBER_STOREFRONT flag also enables this data path for installed Droplets; no separate Droplet data flag is required. Verify your backend’s signature checks, tenant mapping and operation permissions before connecting it. The company flag is not a per-operation permission system.

Instructions for Mist and other AI agents

Use this browser-helper workflow when creating a Droplet and its member-page sections. Read the backend contract, section example, and sequence diagram together. Packaging a section alone does not complete the private-data connection.

Collect the inputs

Before writing files, obtain the target environment, Droplet owner company, member-site company, theme and member page, and the existing Droplet UUID if you are extending one. Obtain authorized management tooling for each company; the owner and member-site company may differ. Also obtain the deployed backend URL, the hosting type (Mist or external), the tenant/member mapping, and the read operations the section should display. Ask for missing identities, permissions or data contracts instead of inventing them. Keep secrets in the authorized backend environment; do not include their values in generated files, chat output or completion reports. Confirm the member-storefront flag and the API, SDK, Liquid helper and global-loader deployments listed at the top of this guide. A merged PR, a successful ZIP import or a rendered member name does not establish that this connection is available in the target environment. For a Mist-hosted backend, use the managed secret after attaching the correct Droplet and deploying the app. Verify the hosted key names without reading or printing their values. For external hosting, obtain a secret-storage reference; for local testing, confirm the secret is configured separately.

Build the backend and section together

  1. Agree on the operation contract. Write down each operation’s name, allowed parameters, authorized data scope and result object before building the UI. For example, commissions.summary accepts an optional period and returns { "total": "125.00", "currency": "USD" }. These are application-defined fields, not a built-in Fluid commissions API. Implement both the backend handler and the section’s field validation against the same contract.
  2. Implement the receiving backend. Use the raw-body verifier before parsing JSON or accessing data. Dispatch only known read operations, resolve the signed company and member together, and check their permissions. Keep data-source credentials on the backend. Return the result object directly, without adding another data envelope.
  3. Create or reuse the Droplet. Register the receiver as data_url, and follow the secret setup for your hosting. Use the registration recipe below for a new Droplet. For an existing Droplet, update only the requested fields and verify that its secret is already available to the backend. The embedded admin UI and lifecycle webhook URLs serve different purposes from the data receiver.
  4. Author the UI. For a Droplet-owned theme extension, package fluid.extension.toml with type = "theme" and a name, plus sections/commission_summary/index.liquid. Give the section target: "section", settings and a presets entry. Use a section or app block for member pages; head/body app embeds are not injected there. Follow the extension package guide.
  5. Connect each rendered placement. Use {% droplet_data_attributes %} inside a Droplet extension, or explicitly configure the known public Droplet UUID on a native/custom root, then await window.Fluid.droplets.createQueryClient(root, { dropletUuid: root.dataset.dropletUuid, onInvalidate }). Use the complete section script as the starting point. Mount each root once, give each placement its own client, and query operation names rather than backend URLs. The global loads the SDK; you still supply and load your section’s JavaScript.
  6. Render and clear private content. Handle initialization, loading, empty and error states; validate the returned fields and render them with escaped output. Clear every private value in onInvalidate, and prevent an older asynchronous result from repopulating cleared content. Call client.destroy() when your application tears down the placement. Use sample data in previews; never show sample commissions as a successful live query.
  7. Install the Droplet and place the UI. A Droplet extension is optional for UUID queries. When using one, follow deployment without the admin: upload the complete ZIP, wait for publication, verify an active installation in the member-site company, and retrieve the real extension_uri. For that extension, read the existing member template, then merge a literal section tag and matching schema entry using that URI and a stable placement ID. Preserve unrelated sections and settings. Push and publish through the theme workflow, then test the live account-host page.

Register a new Droplet for this workflow

Use the owner company’s authorized management tooling. Creation uses POST /api/company/v2026-10/droplets with the fields nested under droplet; title and embed_url are required. A minimal registration for this example is:
After creating or updating the Droplet, verify that the authorized response or read-back contains your exact receiver URL under data_url, or widget_data_url on an older deployment. Do not continue if the receiver is missing or different; a successful registration alone does not confirm that the field was applied. Replace these example addresses with your deployed URLs. Capture droplet.uuid. For Mist hosting, attach this Droplet to the Mist app to provision its secret. For external hosting, rotate the webhook secret once and securely store webhook_secret_rotation.webhook_secret from that response. Creation alone does not establish an active installation or a published extension; verify that the Droplet is permitted to run and available to the target company before installing it. If using a Droplet theme extension for the UI, use the widget deployment recipe to upload the ZIP and place its template. A UUID query itself does not require that extension.

Avoid these substitutions

  • Do not fetch the Droplet backend directly from section JavaScript or add a new Fluid route for each operation. The section calls the global client; your backend dispatches operations at its registered endpoint.
  • Do not use a Liquid member ID, an ID in params, or the shop header alone as authentication. Resolve identity from the verified signed body and apply backend permissions.
  • Do not embed the webhook secret, an installation token or data-source credentials in Liquid, JavaScript, section settings or the ZIP. Do not store private responses in persistent browser storage or shared caches.
  • Do not build a sandboxed widget package for this workflow. These UIs are Liquid sections, app blocks, native widgets or custom Liquid roots, and they share the page with other scripts.
  • Do not bypass a missing global client by inventing a CDN import or sending raw private-data requests. Check deployment and flag availability, and show an unavailable state until the supported connection is available.

Verification and completion report

Run the complete connection checks, including two members with different records, sign-out during a slow response, account switching, and backend signature/permission refusals. Check that preview rendering uses samples and that a missing global client produces a controlled unavailable state. Report the Droplet UUID, owner and member-site companies, configured receiver URL, target template, placement ID and changed files, plus extension publication status and URI only when using an extension. Include the operation names and result shapes, the checks actually run and their outcomes, and any missing deployment, secret provisioning or live-account access. Report generated, uploaded, published and live-tested as separate states; never claim a live connection solely because packaging or a preview succeeded.

Reusable task brief