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

# Droplet theme extensions and app embeds

> Create Droplet theme extensions, use them in the visual builder, and place sections or enable app embeds through theme files without the UI.

A Droplet theme extension packages Liquid templates that render inside a company's storefront theme. Use sections for content merchants place on individual pages, and app embeds for features they enable across the storefront.

Droplet app embeds are included on standard storefront pages; they are not injected into member-site pages.

| Extension content | Location in the ZIP            | Schema target        | How merchants use it                                |
| ----------------- | ------------------------------ | -------------------- | --------------------------------------------------- |
| Page section      | `sections/<name>/index.liquid` | `section` or omitted | Add Section → Apps in the page editor               |
| App embed         | `sections/<name>/index.liquid` | `body` or `head`     | Enable it in the theme editor's App Embeds panel    |
| App block         | `blocks/<name>/index.liquid`   | Omitted              | Add it inside a section that supports `@app` blocks |

For example, use a section for an announcement or review panel, a body embed for a floating widget, and a head embed for global scripts or styles.

## Before you upload

<Note>
  The upload workflow requires the feature-gated Theme Extension card to be available in your environment. Contact Fluid if you do not see it.
</Note>

1. Create a Droplet, or open one owned by your company. See [Creating Droplets](/guides/creating-droplets).
2. Have a Fluid administrator enable `DROPLET_THEME_EXTENSIONS` for the **Droplet owner company**. This flag is off by default and exposes the Theme Extension upload card on Droplet create/edit forms.
3. Install the Droplet in the company whose storefront will use the extension. An active installation is required, including when the owner company tests its own Droplet.

The flag controls the upload UI. Installing the Droplet makes its published templates available, and the App Embeds toggles control which embeds appear in a theme.

## Package structure

Put `fluid.extension.toml` at the ZIP root:

```text theme={null}
storefront-announcements/
├── fluid.extension.toml
├── sections/
│   ├── announcement/
│   │   └── index.liquid
│   └── floating_widget/
│       └── index.liquid
├── blocks/                  # Optional standalone app blocks
├── components/              # Optional reusable Liquid snippets
├── assets/                  # Optional CSS, JavaScript, and images
└── locales/                 # Optional translation JSON
```

The manifest declares the extension type and display name:

```toml theme={null}
type = "theme"
name = "Storefront Announcements"
version = "1.0.0"
```

`type` and `name` are required. `theme` is the supported type; `version` is informational.

Create the ZIP from inside the extension directory so the manifest is at its root:

```bash theme={null}
cd storefront-announcements
zip -r ../storefront-announcements.zip fluid.extension.toml sections
```

Include any additional directories you use in the same archive. A complete theme layout or page template is not required.

## Create a page section

Create `sections/announcement/index.liquid`:

```liquid theme={null}
<section data-section-id="{{ section.id | escape }}">
  <h2>{{ section.settings.heading | default: 'Hello from your Droplet' | escape }}</h2>
  <p>{{ section.settings.message | default: 'Edit this message in the page editor.' | escape }}</p>
</section>

{% schema %}
{
  "name": "Announcement",
  "target": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Hello from your Droplet"
    },
    {
      "type": "textarea",
      "id": "message",
      "label": "Message",
      "default": "Edit this message in the page editor."
    }
  ],
  "presets": [{ "name": "Announcement" }]
}
{% endschema %}
```

Page sections read settings from `section.settings`. After installation, a merchant can add the section through **Add Section → Apps**, edit its settings, and save or publish the page.

## Create an app embed

Create `sections/floating_widget/index.liquid`:

```liquid theme={null}
<aside
  aria-label="Droplet announcement"
  style="position: fixed; bottom: 1rem; right: 1rem; z-index: 1000; max-width: min(20rem, calc(100vw - 2rem)); padding: 1rem; background: white; color: black; border: 1px solid currentColor; border-radius: 0.75rem;"
>
  {{ block.settings.message | default: 'Your app embed is live.' | escape }}
</aside>

{% schema %}
{
  "name": "Floating Widget",
  "target": "body",
  "settings": [
    {
      "type": "text",
      "id": "message",
      "label": "Message",
      "default": "Your app embed is live."
    }
  ]
}
{% endschema %}
```

**App embeds read settings from `block.settings`, even though their files live under `sections/`.** The renderer merges schema defaults with saved embed settings.

* `target: "body"` adds the rendered content to `content_for_layout`, which your layout outputs inside the body.
* `target: "head"` adds the rendered content to `content_for_header`, which your layout outputs inside the head. Use head-compatible content, such as scripts or styles, for this target.

The current App Embeds panel provides enable/disable toggles. It does not expose a settings editor, so give embeds useful schema defaults.

## Upload and publish the extension

1. Open **Droplets** and create or edit your Droplet.
2. In **Theme Extension**, choose **Upload Extension Zip** and select one ZIP.
3. Save the Droplet. The extension is imported in the background.
4. Check the Droplet's extension status. Wait for **Published** before testing it in the theme editor. If it reports **Error**, inspect the import error on the Droplet detail page.

**Cancel extension upload** discards the pending selection before saving. It does not delete a published extension. Disabling the feature flag while editing also discards any pending selection.

To publish an update, upload a new ZIP and save again. Import replaces the extension's templates and assets; the package must contain everything you want to keep. There is one theme extension per Droplet, and installed companies use the latest published version rather than selecting a version independently.

## Use extension sections in the builder

### Add a section to a page

1. Install the Droplet in the company whose storefront you are editing. Confirm that its theme extension is published.
2. Open the page or theme template in the visual builder.
3. Click **Add Section**, then select **Apps**. The Apps tab appears when installed extensions provide eligible page sections.
4. Find the section under its Droplet name and add it at the desired position. For the example above, select **Announcement**.
5. Select the new section in the canvas or **Layers**. Edit its settings in the sidebar, such as **Heading** and **Message**.
6. Use the builder to reorder or remove the placement as you would another page section.
7. Save your changes and use the page's normal publish workflow. Check the storefront page using that template.

Each placement has its own settings. Removing a placement removes that instance from the page; it does not uninstall the Droplet or delete its extension. Edit your page's placement settings, rather than copying the extension's Liquid into a theme-owned section.

Head/body embeds do not appear in this section picker. Use the sidebar's **Apps** panel for them.

### Add an app block inside a section

A host section must declare `{ "type": "@app" }` in its schema's `blocks` list before the builder offers installed app blocks.

1. Select a section that supports app blocks.
2. Click **Add Block** and choose the extension block from the app choices.
3. Select the block in **Layers** and edit its settings in the sidebar.
4. Save and publish the page through its normal workflow.

If you author the host section, preserve its existing block handling and add an app-block branch to its Liquid loop:

```liquid theme={null}
{% for block in section.blocks %}
  {% case block.type %}
    {% when '@app' %}
      {% render block %}
  {% endcase %}
{% endfor %}
```

The builder saves a specific extension URI for the selected block. At render time, that block is exposed to the host as `@app`, and `{% render block %}` renders its extension content with `block.settings`.

## Enable an app embed in the builder

1. Install the Droplet in the storefront company and confirm its extension is published.
2. Open the theme editor for the theme you want to configure.
3. Click **Apps** (the puzzle-piece icon) in the left sidebar to open App Embeds.
4. Find your embed under **Head** or **Body**, then turn it on. The toggle saves the configuration for that theme.
5. Open or reload a storefront page to check the result. Turn the toggle off to stop including the embed.

Embeds start disabled. Enabling one applies it site-wide within that theme; it does not add a draggable section to a page. Page sections are added separately through **Add Section → Apps**.

## Add extension content without the builder UI

Use these file-based recipes when editing a theme directly or giving an AI agent a theme task. The Droplet must still be actively installed in the storefront company and its extension must be published. Editing a template does not install a Droplet.

### Obtain the real extension URI

Extension templates use this identifier format:

```text theme={null}
fluid://extensions/{app_extension_id}/sections/{section_name}
fluid://extensions/{app_extension_id}/blocks/{block_name}
```

Use the exact URI from an existing saved placement, the extension template's `extension_uri` metadata, or the extension developer. The identifier after `extensions/` is the **theme extension ID**, not the Droplet ID, installation ID, or theme ID. The final segment is the template's directory name, without `/index.liquid`.

The examples below assume an installed extension with ID `42` and the templates from this guide. Replace each example URI with the real URI for your environment. If an agent cannot obtain it, have the agent request it before editing; do not guess an ID or substitute a plain section name.

### Place a page section directly in Liquid

For a section with `target: "section"` (or no target), add a literal `section` tag at the desired position in the page template, such as `home_page/default/index.liquid`. Add the matching instance to that page's existing schema:

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

{% schema %}
{
  "sections": {
    "droplet_announcement": {
      "id": "droplet_announcement",
      "type": "fluid://extensions/42/sections/announcement",
      "settings": {
        "heading": "Free shipping this weekend",
        "message": "Standard shipping is included on orders over $75."
      }
    }
  }
}
{% endschema %}
```

This is a minimal page fragment. Merge it into the existing Liquid and schema; do not replace the rest of the page or append a second `{% schema %}` block.

The tag's `id` and the key in `sections` must match. Keep the full extension URI in both the tag and the instance's `type`. Give each placement a unique ID, and keep that ID stable when updating its settings. The tag's position in the Liquid determines where the section renders.

The section reads this instance's values from `section.settings`. Use setting IDs declared by the extension's schema. Two placements of the same extension section can use the same URI and different instance IDs and settings.

Keep the page as a Liquid template with embedded schema JSON. Upload or push the changed theme files through your normal [theme development workflow](/themes/cli), then save/publish the intended template and verify it on the storefront. A local file edit alone does not update the live site.

### Enable a global embed through theme configuration

For `target: "head"` or `target: "body"`, edit the storefront company's theme file `config/settings_data.json`. This is the file-based equivalent of enabling the embed in **Apps**; do not place this widget using the page-section recipe above, which supplies `section.settings` rather than the embed's `block.settings`.

If `current` is an object, merge an entry into `current.blocks`:

```json theme={null}
{
  "current": {
    "blocks": {
      "droplet_floating_widget": {
        "type": "fluid://extensions/42/sections/floating_widget",
        "disabled": false,
        "settings": {
          "message": "Need help? Our team is here for you."
        }
      }
    }
  }
}
```

This example shows only the relevant configuration. Preserve all unrelated theme settings, blocks, and presets. If the embed already exists, update that entry instead of adding a duplicate. Keep its existing block key.

If `current` is a preset name such as `"Default"`, leave that string in place and edit the active preset's `blocks` instead:

```json theme={null}
{
  "current": "Default",
  "presets": {
    "Default": {
      "blocks": {
        "droplet_floating_widget": {
          "type": "fluid://extensions/42/sections/floating_widget",
          "disabled": false,
          "settings": {
            "message": "Need help? Our team is here for you."
          }
        }
      }
    }
  }
}
```

Use the boolean `false` to enable the entry and `true` to disable it. Saved `settings` override the embed schema defaults and are available as `block.settings`. The extension template's schema chooses head or body placement; adding a `target` field to this configuration does not change that placement.

Your layout must output `{{ content_for_header }}` inside its head and `{{ content_for_layout }}` inside its body. Preserve those outputs in custom layouts so enabled embeds can appear. Publish/synchronize the changed theme configuration through your normal theme workflow and verify a storefront page using that theme.

### Instructions for AI agents

Provide the agent with the target company/theme, the page template if placing a section, the exact extension URI, the extension schema, and the requested settings. Use this task brief:

```text theme={null}
Add the supplied Droplet extension to this existing Fluid theme.

1. Verify that the target company has an active installation and that the
   extension is published. Obtain the exact extension URI and schema;
   ask for missing information instead of inventing IDs or setting names.
2. Read the existing page, layout, and theme configuration before editing.
3. For a page section, add a literal section tag and a matching entry in
   the page's existing schema.sections map. Use the same full URI and a
   unique, stable instance ID. Preserve all existing placements.
4. For a head/body app embed, merge its entry into the active theme
   configuration's blocks map. Handle both an inline current object and
   a current preset name. Set disabled to the boolean false.
5. Preserve existing Liquid, settings, blocks, presets, and layout outputs.
   Do not copy extension source into the theme or fabricate an app_embed tag.
6. Validate the schema/config JSON, publish through the existing theme
   workflow, and check the storefront. Report the changed files, instance
   ID or block key, and the verification result.
```

## Troubleshooting

| Symptom                                  | What to check                                                                                                                                    |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| No Theme Extension upload card           | Confirm `DROPLET_THEME_EXTENSIONS` is enabled for the Droplet owner company.                                                                     |
| Extension stays unpublished              | Import runs in the background. In local development, ensure the background job worker is running; otherwise check the import status and error.   |
| Import reports a missing manifest        | Put `fluid.extension.toml` at the ZIP root and use `type = "theme"`.                                                                             |
| Nothing appears under Apps or App Embeds | Confirm the extension is published and the storefront company has an active installation of the Droplet. Ownership alone is not an installation. |
| Embed is listed but invisible            | Enable its toggle on the correct theme, reload the storefront, and check that visible markup uses `target: "body"`.                              |
| Embed settings render blank              | Use `block.settings`, define schema defaults, and use valid setting IDs.                                                                         |
| Section is missing from Add Section      | Use `target: "section"` or omit `target`, and provide a `presets` entry. Head/body embeds appear in App Embeds instead.                          |

The ZIP limit is **50 MB**. Keep the archive focused on your extension files.
