Skip to main content
This connection requires the member-storefront pilot, the MEMBER_STOREFRONT_DROPLET_DATA company flag, the member-query API deployment, and Member Storefront SDK 0.11.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.
An installed Droplet section can query its backend on a signed-in member’s account-host page. Fluid authenticates the member, checks the installed extension, and signs the backend request. The section receives the returned JSON and renders it in the browser. This connection 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.

What you need before connecting

The theme extension 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 extension reference to Fluid.
  3. Fluid checks the live member/site access, company flags, active installation, published extension, and active section or block.
  4. Fluid signs the server-derived member and company context, then posts to the Droplet’s registered widget_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 widget_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

Use the Droplet-management API in your integration to configure widget_data_url, for example https://commissions.northwind.example/member-data. Droplet creation and updates accept this field. Changing the endpoint in your section’s settings does not register a backend destination. Fluid returns webhook_secret when you create the Droplet; save it securely at that point. Ordinary Droplet read and update responses do not return this secret. Confirm that your backend has the original 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 widget_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. 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. widget_type identifies the installed extension template. 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. This Node example accepts a five-minute window:
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. Do not use the sample verifier as 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 extension section or app block. Use {% droplet_data_attributes %} on the root element to identify the rendered extension automatically:
The tag emits data-droplet-data and an escaped data-extension containing the current section or block URI. It accepts no arguments, so you do not need to hardcode an extension ID or add an extension URI setting. 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 Liquid helper is deployed; on an older deployment, supply the installed template’s actual extension_uri in data-extension manually. Fluid exposes window.Fluid.droplets.createQueryClient before theme scripts run on member pages when both company flags are 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. 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 either flag 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 extension’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.

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

Install the Droplet, publish its 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 editing 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. Enable the data flag only after verifying your backend’s signature checks, tenant mapping and operation permissions. The company flag enables this data path for its installed published extensions; it is not a per-operation permission system.

Instructions for Mist and other AI agents

Use this 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, a secret-storage reference for its Droplet webhook secret, 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 pilot flags 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.

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 widget_data_url, and retain the create-time webhook_secret in secret storage. 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 a theme extension section. 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. Put {% droplet_data_attributes %} on its root, then await window.Fluid.droplets.createQueryClient(root, { extension: root.dataset.extension, 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 and place the extension. 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. 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

The Droplet-management API is reference-pending. Confirm its availability in your environment and use the owner company’s authorized management tooling. Creation uses POST /api/droplets with the fields nested under droplet; name and embed_url are required. A minimal registration for this example is:
Replace these example addresses with your deployed URLs. Capture droplet.uuid and securely store droplet.webhook_secret from the create 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. Use the widget deployment recipe to upload the ZIP and resolve the installed template’s URI.

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 widgets are Liquid theme-extension sections or app blocks and 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, extension publication status, actual extension URI, target template, placement ID and changed files. 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