> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fluid.app/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For new direct REST integrations, use the v2026-04 surfaces. The @fluid-app FairShare SDK continues to use its own published public-v2025-06 contract.
> Authenticate with the header Authorization: Bearer <token>; public storefront read endpoints require no auth.
> Lists use cursor pagination via the page[cursor] and page[limit] query params; follow meta.pagination.next_cursor until it is null.
> When the same operation exists on more than one surface, use the newest: dated API versions are newer than unversioned ones, and later dates win (v2026-04 > v2025-06 > unversioned v0/v1.1). Fall back to a legacy or unversioned operation only when no newer versioned equivalent exists — the company-v0 notes below list the known superseded operations. /api/company/v1 and /api/v1/... paths are documented in no spec here and must never be used (/api/v1.1/... is distinct and documented in company-v0). Use page/per_page offset pagination only where a spec documents it — in practice the unversioned company-v0 admin surface; every versioned surface uses cursor pagination.
> Navigation menu management is documented in themes/navigation-menus. These unversioned admin endpoints (/api/menus and nested menu_items) are verified against the implementation but are not yet in the synced OpenAPI specs. Use that reference for menu payloads and its flat page/per_page pagination; missing spec coverage does not make these endpoints unavailable.
> The OpenAPI specs under api-reference/ are the authoritative contracts; prefer them over prose when in doubt. api-reference/storefront-v2026-04.yaml covers the v2026-04 storefront surface (/api/v202604/... paths); api-reference/auth-v0.yaml covers the unversioned auth surface (/api/... paths — authentication, MFA, social auth, and token exchange); api-reference/checkout-v2026-04.yaml covers the v2026-04 checkout surface (/api/checkout/v2026-04/... paths — carts, cart auth, discounts, items, subscriptions, orders, enrollments, and store config); api-reference/public-v2025-06.yaml covers the Public SDK surface used by the @fluid-app FairShare SDK, including its parallel cart lifecycle, browser integrations, versioned payment callbacks, unversioned public utilities, and the cart price-override operation; api-reference/payment-v2026-04.yaml covers the v2026-04 payment gateway admin surface (/api/payment/v2026-04/... paths, bearer-authenticated — gateway CRUD, gateway purchase/authorize/$0-verify, transaction list/show and capture/void/credit, and merchant payment configuration); api-reference/payments-v2026-04.yaml covers the v2026-04 cart payment surface (/api/payments/v2026-04/carts/{cart_token}/... paths, authenticated by the cart token in the path with no bearer — payment-method selection, VGS card tokenization, 3D Secure verification, and PayPal/Braintree/Klarna/Apple Pay flows); api-reference/commerce-v2026-04.yaml covers the v2026-04 commerce order-editing surface (/api/v202604/orders/{order_id}/edits paths, bearer-authenticated — post-checkout order edits that atomically insert items and add adjustments/discounts, with an optional dry-run preview); api-reference/webhooks-v0.yaml covers the unversioned webhooks surface (/api/... paths — webhook registration, delivery payloads, callback registrations, company events, and webhook/callback schemas); api-reference/company-v0.yaml covers the legacy unversioned company admin surface (/api/... paths, bearer-authenticated — company settings and management, customers, users, roles, subscription plans, subscription bundles, subscriptions, media, pages, catch-ups, inventory levels, domains, agreements, and admin order actions). company-v0 caveats: it is the legacy v0 admin contract and its lists use flat page/per_page offset pagination, which is expected there despite the general cursor-pagination rule; where an operation exists in both company-v0 and a versioned spec, prefer the versioned spec — the subscriptions lifecycle (list/create/show/update, cancel, pause, reactivate, resume, retry, skip, failed-cycle-waiver, discounts) and subscription bundles are superseded by checkout-v2026-04, and company pages/media CRUD plus the public pages, categories, products, and media list endpoints are superseded by storefront-v2026-04. Subscription plan management (/api/subscription_plans, resource-wrapped {"subscription_plan": {...}} bodies) exists only in company-v0. api-reference/members-v2025-06.yaml covers the v2025-06 unified Member identity surface (/api/v2025-06/members/... paths, bearer-authenticated — member list/create/show/update, lookup by email/username/external_id/legacy_customer_id, member-type assignment, and the sponsor genealogy read). Prefer it over the customers and reps surfaces when the member type matters: /customers does not serialize member_type. api-reference/analytics-v2026-04.yaml covers the v2026-04 Home dashboard analytics surface (/api/v202604/analytics/dashboard/... paths, bearer-authenticated — read-only endpoints for the Home > Overview, Home > Live, and Home > Field tabs, each accepting an optional country ISO alpha-2 query param that scopes aggregations to a single country).
> api-reference/analytics-v0.yaml covers the unversioned analytics surface that backs the fluid-admin Traffic tab (/api/analytics/... and /api/analytics/traffic/... paths, bearer-authenticated — the legacy shares/views/visitors summary plus traffic overview, ranked campaigns, sources, geographies, flows, and per-rep breakdown, all sharing one reporting-period contract).
> Successful responses wrap the resource payload alongside a top-level integer status and a meta object.
> Portal Definition authoring edits and synchronizes the portal JSON resource graph. Widget Package authoring builds either a company-owned or Droplet-owned Remote DOM package. These are separate contracts; do not imply that one defines the other.
> For Widget Package worker code, use only @fluid-app/portal-sdk/widgets/worker. Use only the Portal Definition and Widget Package workflows and public entry points documented here; do not infer support for undocumented surfaces.
> Every portal function and declarative capability used by a widget must appear in that widget's uses array. Use the same typed function value in uses; do not invent capability-name strings.
> Widget styling must use the portal's semantic theme variables for colors, typography, spacing, radii, borders, focus, and charts whenever a token represents the visual decision. Do not create a separate light or dark palette or duplicate theme controls as widget properties.
> Prefer worker-safe Fluid UI components exported by @fluid-app/portal-sdk/widgets/worker when they fit the interaction. When no exported component fits, use semantic HTML, accessible behavior, and the portal theme variables.
> A Portal Definition push updates the remote working definition. A portal version is an immutable snapshot, and activation is a separate live release operation.

# Connect a widget to its Droplet backend

> Query member-specific backend data from an installed Droplet section using Fluid's signed connection.

<Warning>
  This connection requires the `MEMBER_STOREFRONT` 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.
</Warning>

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.

This connection reuses the existing Droplet backend signing scheme and webhook secret. The member-storefront flow adds member-session and extension-access checks before forwarding the authenticated member and company context. Your backend still verifies the request and authorizes access to that member's data.

## What you need before connecting

| Component | Required setup |
| - | - |
| Droplet | Register a Droplet that is permitted to run, with an active installation in the member's company. |
| Theme extension | Publish the Droplet's theme extension with an active section or app block. |
| Data endpoint | Set the Droplet's `widget_data_url` to a public HTTPS endpoint that accepts signed JSON POST requests. |
| Signing secret | Use the same Droplet's `webhook_secret`. Mist hosting provisions it automatically when you attach the Droplet; external hosting needs manual configuration. |
| Member storefront | Enable `MEMBER_STOREFRONT` for the test company after the API and SDK release are available. |
| Member access | Use a real signed-in member who can access the member site on its live account host. |
| Private data | Maintain a mapping from the signed Fluid company/member identity to the tenant and member in your own data source. |
| Section script | Use `window.Fluid.droplets.createQueryClient`, supply the installed template's `extension_uri`, and implement `onInvalidate` to clear private content. |

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](/themes/droplet-theme-extensions) and [place it on a member page](/themes/member-storefront/droplet-widgets) 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, member-storefront flag, 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.

```mermaid theme={null}
sequenceDiagram
    actor Member
    participant Section as Liquid section JavaScript
    participant Helper as Droplet query helper
    participant Fluid
    participant Backend as Your Droplet backend
    participant Data as Your private data source

    Member->>Section: Open a member page
    Section->>Helper: query(operation, params)
    Helper->>Fluid: Read cookie session and CSRF information
    Fluid-->>Helper: CSRF token and login context
    Helper->>Fluid: Submit extension URI, operation and params with CSRF
    Fluid->>Fluid: Check member, site, the member-storefront flag and installed extension
    alt Fluid authorizes the query
        Fluid->>Backend: POST to widget_data_url with signed identity and input
        Backend->>Backend: Verify raw-body HMAC and timestamp freshness
        Backend->>Backend: Map tenant/member and authorize the operation
        alt Backend authorizes the request
            Backend->>Data: Read this tenant and member's records
            Data-->>Backend: Private records
            Backend-->>Fluid: Operation result as a JSON object
            Fluid-->>Helper: Uncached data and login context
            Helper->>Fluid: Recheck current login context
            Fluid-->>Helper: Current session
            Helper-->>Section: Return data only if the login is unchanged
            Section-->>Member: Render escaped data or an empty state
        else Verification or permission fails
            Backend-->>Fluid: Reject the request
            Fluid-->>Helper: Safe error
            Helper-->>Section: Render an error state
        end
    else Fluid refuses the query
        Fluid-->>Helper: Refusal without a backend request
        Helper-->>Section: Render an error state
    end
    loop While a section holds private results
        Helper->>Fluid: Periodically recheck login context
        Fluid-->>Helper: Current context or verification failure
        Helper-->>Section: Clear results if the context changed or verification failed
    end
```

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. For external hosting, 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.

**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`.

Use `process.env.FLUID_DROPLET_SECRET` as the secret argument to the [raw-body verifier](#verify-the-raw-body). 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 create-time webhook secret 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 existing secret securely, report the missing local setup instead of generating a different signing key.

The signed JSON has this shape:

```json theme={null}
{
  "operation": "commissions.summary",
  "params": { "period": "2026-09" },
  "widget_type": "fluid://extensions/42/sections/commission_summary",
  "company": { "id": 1842, "fluid_shop": "northwind.fluid.app" },
  "member": {
    "id": "01990000-0000-7000-8000-000000000001",
    "public_id": "01990000-0000-7000-8000-000000000001",
    "external_id": "exigo-4821"
  }
}
```

`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.

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:

```javascript theme={null}
import { verifySignature } from "@fluid-app/droplet-sdk";

const rawBody = Buffer.from(await request.arrayBuffer());
const verification = verifySignature({
  rawBody,
  timestamp: request.headers.get("x-fluid-timestamp"),
  signature: request.headers.get("x-fluid-signature"),
  secret: process.env.FLUID_DROPLET_SECRET ?? "",
});
if (!verification.valid) {
  return new Response("Invalid signature", { status: 401 });
}

const query = JSON.parse(rawBody.toString("utf8"));
// Validate the query, resolve the signed tenant/member, and authorize the operation.
```

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.

```javascript theme={null}
async function dispatchVerifiedQuery(query, backend) {
  const { company, member, operation, params } = query;
  if (!Number.isSafeInteger(company?.id) || typeof member?.public_id !== "string") {
    throw new Error("Invalid signed identity");
  }
  if (!params || typeof params !== "object" || Array.isArray(params) ||
      Object.keys(params).some((key) => key !== "period")) {
    throw new Error("Invalid parameters");
  }
  const period = params.period;
  if (period !== undefined &&
      (typeof period !== "string" || !/^\d{4}-(0[1-9]|1[0-2])$/.test(period))) {
    throw new Error("Invalid period");
  }
  if (!["commissions.summary", "commissions.history"].includes(operation)) {
    throw new Error("Unknown operation");
  }
  const identity = await backend.resolveMember({
    companyId: company.id,
    memberPublicId: member.public_id,
  });
  if (!identity || identity.canReadCommissions !== true) {
    throw new Error("Member cannot read commissions");
  }
  if (operation === "commissions.summary") {
    return backend.summary(identity, period);
  }
  return { entries: await backend.history(identity, period) };
}
```

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:

```liquid theme={null}
<section data-commission-example {% droplet_data_attributes %}>
  <h2>Your commissions</h2>
  <p role="status" aria-live="polite">Loading…</p>
  <pre data-output></pre>
  <button type="button">Refresh</button>
</section>
```

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 `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.

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 extension's script asset. Each placement gets its own client:

```javascript theme={null}
async function mountCommissions(root) {
  const status = root.querySelector('[role="status"]');
  const output = root.querySelector("[data-output]");
  let revision = 0;
  let client;
  try {
    client = await window.Fluid.droplets.createQueryClient(root, {
      extension: root.dataset.extension,
      onInvalidate() {
        revision += 1;
        output.textContent = "";
        status.textContent = "Refresh to load member data.";
      }
    });
  } catch {
    if (root.isConnected) status.textContent = "Unable to start. Reload the page to try again.";
    return;
  }
  async function load() {
    const pending = client.query("commissions.summary", {});
    const currentRevision = revision;
    status.textContent = "Loading…";
    try {
      const data = await pending;
      if (revision !== currentRevision || !root.isConnected) return;
      status.textContent = Object.keys(data).length ? "Loaded" : "No commissions yet.";
      output.textContent = Object.keys(data).length ? JSON.stringify(data, null, 2) : "";
    } catch {
      if (revision !== currentRevision || !root.isConnected) return;
      status.textContent = "Unable to load. Sign in or try again.";
      output.textContent = "";
    }
  }
  root.querySelector("button").addEventListener("click", () => void load());
  void load();
}

for (const root of document.querySelectorAll("[data-commission-example]")) {
  void mountCommissions(root);
}
```

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](/themes/member-storefront/droplet-widgets#place-a-widget-in-the-builder).

For an agent editing theme files, use the same extension URI in the literal tag and the existing page schema:

```liquid theme={null}
{% section 'fluid://extensions/42/sections/commission_summary', id: 'commissions' %}

{% schema %}
{
  "sections": {
    "commissions": {
      "id": "commissions",
      "type": "fluid://extensions/42/sections/commission_summary",
      "settings": {}
    }
  }
}
{% endschema %}
```

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](/themes/droplet-theme-extensions#place-a-page-section-directly-in-liquid).

## 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.

The existing `MEMBER_STOREFRONT` flag also enables this data path for installed published extensions; 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 workflow when creating a Droplet and its member-page sections. Read the [backend contract](#prepare-the-backend), [section example](#render-the-result-in-a-section), and [sequence diagram](#how-the-data-reaches-the-section) 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](#verify-the-raw-body) 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 follow the [secret setup for your hosting](#register-the-destination-and-save-the-secret). 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](/themes/droplet-theme-extensions#package-structure).
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](#render-the-result-in-a-section) 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](/themes/member-storefront/droplet-widgets#deploy-a-widget-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:

```json theme={null}
{
  "droplet": {
    "name": "Northwind Commissions",
    "embed_url": "https://commissions.northwind.example/admin",
    "widget_data_url": "https://commissions.northwind.example/member-data"
  }
}
```

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, 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](/themes/member-storefront/droplet-widgets#deploy-a-widget-without-the-admin) 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](#verify-the-complete-connection), 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

```text theme={null}
Create or update a Fluid Droplet and a member-page section using
/themes/member-storefront/droplet-data#instructions-for-mist-and-other-ai-agents.

Use the supplied environment, owner company, member-site company, theme,
page, backend and operation contract. Obtain missing inputs before making
assumptions about identities or permissions. Keep secret values out of output.

Build the signed read-only backend receiver and the Liquid theme extension.
Use droplet_data_attributes and await window.Fluid.droplets.createQueryClient
for each placement. Implement private-output cleanup and preview sample states.
Install the published extension, discover its real URI, and preserve the
existing template when adding its section tag and schema entry.

Verify with real member sessions and backend permission tests. Return the
artifact identifiers, operation contracts, deployment states, test evidence
and remaining blockers. Do not mark preview-only work as live-tested.
```
