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

# Query parameters and caching

> Know which query parameters keep a storefront page on the CDN, which give it its own cached copy, and which send every request to the origin.

Fluid serves storefront pages from a CDN. The query string decides which of three
things happens to a request:

* The page is served from the plain URL's cached copy.
* The page gets a cached copy of its own, one per distinct value.
* The page is rendered fresh for every request.

If your theme adds parameters to storefront links, this decides what those links
cost your visitors.

## The three groups

<CardGroup cols={3}>
  <Card title="Ignored" icon="link-slash">
    Fluid does not recognize the parameter. It is removed, and the visitor gets
    the plain URL's cached page.
  </Card>

  <Card title="Cached separately" icon="layer-group">
    The parameter changes the page in a known, limited way, so it earns a cached
    copy of its own.
  </Card>

  <Card title="Not cached" icon="server">
    The parameter can change the page in ways that cannot be cached safely. Every
    request renders fresh.
  </Card>
</CardGroup>

## Ignored parameters

Any parameter Fluid does not recognize is ignored. Marketing tags are the common
case:

```text theme={null}
?utm_source=  ?utm_medium=  ?utm_campaign=  ?utm_term=  ?utm_content=
?gclid=  ?fbclid=  ?msclkid=  ?ttclid=  ?mc_cid=  ?igshid=  ?_ga=
```

So is any parameter you invent for your own front-end code:

```text theme={null}
https://store.example.com/home/shop?grid=compact
```

That URL is served from the same cached page as `/home/shop`.

<Warning>
  An ignored parameter is removed **before the page renders**, so
  `request.query_parameters` and `params` do not contain it in Liquid. Your
  browser URL keeps it, so client-side JavaScript still reads it normally.

  If you need a parameter your Liquid reads, you cannot invent one. Use a
  parameter from the **Not cached** table below, and expect the page to render
  fresh every time.
</Warning>

Visitor tracking is unaffected. Attribution is reported from the browser with the
URL the visitor actually has, so campaign tags still reach your analytics.

### Check a parameter your theme reads

If your Liquid reads a parameter — through `request.query_parameters` or
`params` — check whether it reaches your template. Request the URL and read one
header:

```bash theme={null}
curl -sI "https://store.example.com/home/shop?your_param=1" \
  | grep -iE 'x-cdn-cache|x-query-variant'
```

| Response                                  | What it means for your template                                          |
| ----------------------------------------- | ------------------------------------------------------------------------ |
| `X-CDN-Cache: SKIP`                       | The parameter reaches your Liquid. The page renders fresh every time.    |
| `X-Query-Variant` present                 | The parameter reaches your Liquid, and this URL has its own cached copy. |
| Neither — `HIT` or `MISS` with no variant | The parameter is ignored. **Your Liquid does not receive it.**           |

If you land in the third row and your template needs the value, move the logic
to your front-end code, which still sees the parameter in the browser URL.

## Parameters cached separately

Each distinct value gets its own cached copy of the page.

| Parameter                              | Where it applies | Notes                                                                    |
| -------------------------------------- | ---------------- | ------------------------------------------------------------------------ |
| `count`                                | Every page       | Items per page. Whole number up to 200.                                  |
| `page`                                 | Every page       | Page number. Whole number up to 200.                                     |
| `per_page`                             | Every page       | Items per page on category and collection pages. Whole number up to 200. |
| `filterrific[with_category_id][]`      | Shop             | Up to four category ids.                                                 |
| `filterrific[by_collection][]`         | Shop             | Up to four collection ids.                                               |
| `filterrific[with_option_value_ids][]` | Shop             | Up to four option value ids.                                             |
| `filterrific[with_prices][]`           | Shop             | Up to four price ranges, each `"<min> <max>"`.                           |
| `filterrific[sorted_by]`               | Shop, join       | One of the sort values your theme offers.                                |

A filtered grid is cached per combination, so the first visitor to a facet waits
for a render and everyone after them does not:

```text theme={null}
https://store.example.com/home/shop?filterrific[with_category_id][]=42&page=2
```

<Note>
  The shop filter parameters are cached only on `/shop`, and `filterrific[sorted_by]`
  only on `/shop` and `/join`. On any other page they do not change the product
  grid, so Fluid does not cache them there — those requests render fresh instead.

  `count`, `page`, and `per_page` are cached everywhere, because a paginated
  section can appear on any template.
</Note>

A value outside the limits above is not cached. It still works; the page just
renders fresh. Deep pagination past page 200 is the case you are most likely to
meet.

## Parameters that are not cached

These reach the origin on every request, and nothing is written to the cache.

| Group                         | Parameters                                                                                                                     |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Preview and editing           | `preview`, `preview_theme_id`, `theme_template_id`, `edit`, `draft`, `exit_preview`, `skip_redirect`                           |
| Template and navbar selection | `version`, `navbar_version`, `library_navbar_version`, `navbar_template_id`, `library_navbar_template_id`                      |
| Language, country, currency   | `lang`, `locale`, `fluid_locale`, `country`, `country_id`, `country_code`, `currency`, `timezone`, `filterrific[by_language]`  |
| Attribution and identity      | `credit`, `fluid_c`, `affiliate_id`, `contact_id`, `token`, `jwt`, `subscription_token`, `return_to`, `auth_code`, `auth_uuid` |
| Search                        | `search_query`, `q`, `filterrific[search_query]`                                                                               |
| Theme switches                | `subscribe`, `subscription_plan`, `library_id`, `library_index`, `screen`                                                      |
| Resource selection            | `id`, `ids`, `slug`, `product_id`, `variant_id`, `medium_id`, `collection_id`, `category_id`                                   |
| List controls                 | `sorted_by`, `limit`, `cursor`, `offset`                                                                                       |

Any `filterrific` filter not in the cached table is also uncached.

<Warning>
  Use these deliberately. A storefront link that carries one of them renders on
  every click, so a navigation menu or a product card that adds `?lang=` to every
  URL takes your whole catalog off the CDN.

  Set the visitor's language with the locale selector rather than a link
  parameter. See [Navbar locale selector](/themes/navbar-locale-selector).
</Warning>

## Check what a URL does

Every storefront response carries a header naming the outcome:

| Header            | Value           | Meaning                                                         |
| ----------------- | --------------- | --------------------------------------------------------------- |
| `X-CDN-Cache`     | `HIT`           | Served from cache.                                              |
| `X-CDN-Cache`     | `MISS`          | Rendered fresh, and the result was cached for the next visitor. |
| `X-CDN-Cache`     | `SKIP`          | Rendered fresh, and nothing was cached.                         |
| `X-Query-Variant` | e.g. `count-50` | Present when the URL has its own cached copy, naming which one. |

```bash theme={null}
curl -sI "https://store.example.com/home/shop?utm_source=newsletter" | grep -i x-cdn-cache
```

Request the same URL twice. A cacheable URL answers `MISS` and then `HIT`. A URL
that answers `SKIP` both times is never cached.

## Writing links that stay cached

* Prefer a path over a parameter. `/home/categories/serums` is cached; a filter
  parameter that reproduces it is cached separately.
* Keep marketing tags. They cost nothing — they are ignored and the visitor gets
  the cached page.
* Put your own state in a parameter only when your front-end code reads it. Liquid
  will not see it.
* Keep language and country out of storefront links.
