> ## 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.
> To sign a new merchant up for Fluid programmatically, call POST https://api.fluid.app/api/company with no credentials, as described in /api/agent-signup. Do not automate the signup form or book a demo. The merchant must open the emailed sign-in link and create an API token before authenticated calls are possible.
> After signup, to launch a merchant's store (back-office sync, payments onboarding, store setup, a theme copied from their current site, and a preview link before billing), follow /api/agent-launch.
> 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. The same applies to /api/company/v1 and /api/v1/... paths: prefer a newer documented equivalent, and use one only when none exists (/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.
> Fluid has three navigation APIs; don't mix them up. Storefront website menus (navigation bars, footers) are /api/menus and nested menu_items, in api-reference/content-v0.yaml (API Reference: Website > Navigation menus), with a how-to in themes/navigation-menus; their list uses flat page/per_page pagination. The Fluid mobile app's navigation is /api/v2/mobile_navigations, in api-reference/mobile-v2.yaml (API Reference: Mobile app > Navigation); its list also uses page/per_page. Portal navigations belong to a portal definition (Fluid OS), in api-reference/fluid-os-v0.yaml (API Reference: Portal > Portal navigation), and each has a platform of web or mobile.
> 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/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.
> The Help Center (/help/...) is for merchants, admins and reps using Fluid. Its admin pages mirror the admin's routes: the screen at admin.fluid.app/settings/taxes is documented at /help/admin/settings/taxes. Use the Help Center for how-to questions about the admin, and the Developer Platform and API Reference for building integrations.
> Help Center pages describe what a company admin sees. A reader's role can hide screens and actions; admins manage roles on Settings > Roles (/help/admin/settings/roles). If someone can't find a screen or button, their role's permissions are the first thing to check.
> Send people who need Fluid support to /help/getting-help. Don't invent support email addresses, phone numbers or response times.

# Style storefront error pages (404 and 503)

> Add 404 and 503 templates to your theme so missing pages and server errors show your storefront's branding, and learn which error pages a theme can't style.

When a visitor asks for a page that doesn't exist, or the storefront hits an error, Fluid renders an error template from your theme.
A theme has two error templates, and each one covers its own set of errors:

* `error_page/404/index.liquid` renders for pages that can't be found.
* `error_page/503/index.liquid` renders when something goes wrong while Fluid builds a page.

Without them, visitors see a plain Fluid page instead of your storefront.

## How Fluid picks an error template

Fluid looks up error templates by name in your **active** theme, or in the theme you're previewing (see [Preview your error pages](#preview-your-error-pages)):

* The template type is `error_page`, and the template name is the status code, `404` or `503`. A template with any other name never renders, including one named `default`.
* The template must be the default for its name, and it must have a published version. Fluid renders the published version, not your latest saved draft.
* The 404 and 503 templates are independent. Making one of them the default doesn't change the other.

There's one pair of error templates per theme. Theme region rules don't apply to them, so you can't assign a different error template to a country.

## When each template shows

| What happened | Template | Status code | Visitor's URL |
| - | - | - | - |
| A path that matches no storefront route, such as `/home/spring-catalog/2024` | 404 | `404` | Unchanged |
| A page disabled for the visitor's country by a theme region rule | 404 | `404` | Unchanged |
| A page set to its own direct path, opened through a rep's link such as `/jane-doe/about-us` | 404 | `404` | Unchanged |
| A product, page, collection, category, post, media item, playlist, or enrollment pack that doesn't exist or is no longer available, or a rep site for a username that doesn't exist | 404 | `404` | Unchanged |
| An unexpected error while Fluid renders a page | 503 | `503` | Unchanged |
| A request that fails Fluid's security checks, such as a form submitted with an expired session | 503 | `503` | Unchanged |

A path with a single segment, such as `/spring-sale`, is read as a rep's username.
When no rep has that username, Fluid shows your home page rather than a 404.

Fluid renders the 404 for a missing record in place, at the URL the visitor asked for, when the request accepts HTML.
A request that doesn't accept HTML, such as one that asks only for JSON, is redirected to `/404` instead, and `/404` answers JSON requests with a JSON error body rather than a page.

A rep site link that names no rep, `/my/home`, redirects to your home page rather than showing a 404.
A share link, `/s/<token>`, that is unknown or has nowhere to go redirects to `/404` instead.

## Pages your theme can't style

Some responses never reach your theme:

* **Storefront paused for billing.** If Fluid pauses your storefront over an unpaid balance, visitors see a fixed "temporarily unavailable" page with status `503`. Search engines are told not to index it, and it isn't cached. See the **Status** card on [Billing](/help/admin/settings/billing#status).
* **Storefront unreachable.** If Fluid can't reach your storefront's servers, visitors get a plain-text `502` response.
* **Rate limiting.** A visitor who sends too many requests gets a `429` response with a JSON body.
* **Blocked addresses.** A request from a blocked IP address gets a `403` response with a JSON body.
* **Unknown storefront.** If Fluid can't tell which company a domain belongs to, it shows its own static error page.

Fluid has no maintenance, password, or coming-soon mode for themes to style.

## What happens without error templates

When the active theme has no published default template for a status code, Fluid shows its own static page instead.
That page is dark, carries the Fluid WeCommerce branding, and has none of your navigation.
An unexpected error that falls back to this page returns status `500` rather than `503`.

Whether your theme already has error templates depends on how you created it:

| Theme source | Error templates |
| - | - |
| A copy of the Base theme | Included |
| A copy of the Vox or Fluid theme | Not included |
| `fluid theme init` | Not included |
| An imported theme | Only if the import includes them |
| A copy of another company theme | Whatever that theme has |

Add both `error_page/404/index.liquid` and `error_page/503/index.liquid` to every theme you publish.
Importing a theme that includes them makes each one the default and publishes it.
Pushing them with `fluid theme push` publishes each one, and makes it the default for its status code when that code has no default yet. The 404 and 503 are handled separately, so pushing both makes both the default.

<Note>
  Until recently, a push made only the first error template the default, and themes pushed then weren't corrected.
  If you pushed both error templates before that, open the **Error Page** folder in the [Page Editor](/help/admin/page-editor#error-pages) and use **Make Default** on any error template that doesn't show the bookmark icon.
</Note>

## Layout and variables

An error template renders inside your theme layout, `layouts/theme.liquid`, so your navbar, footer, and storefront Global Embeds appear on it.
Use the `layout` tag to change that:

* `{% layout 'checkout' %}` renders the template inside `layouts/checkout.liquid`.
* `{% layout none %}` renders the template without a layout. The template must then output the whole document.

### What `content_for_header` adds on error pages

On other storefront pages, `{{ content_for_header }}` adds the page title, meta tags, Fluid's scripts, and your theme's stylesheets.
On an error page, it outputs only the storefront Global Embeds placed in the head.

So your error pages depend on your layout for these:

* **Stylesheets.** Load your theme CSS from the layout with `asset_url` and `stylesheet_tag`, as the [developer guide](/themes/developer-guide#theme-file-structure) recommends. A theme that relies on Fluid to add its stylesheets renders error pages without them.
* **Title.** Fluid doesn't add a `<title>`. Give error pages one from the layout, as shown below.

```liquid theme={null}
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  {% if request.page_type == 'error_page' %}
    <title>{{ error.status_message }} | {{ company.name }}</title>
  {% endif %}
  {{ content_for_header }}
  {{ 'theme.css' | asset_url | stylesheet_tag }}
</head>
```

### Variables

Error templates receive these values:

| Variable | Description |
| - | - |
| `error.status_code` | `404` or `503` |
| `error.status_message` | `Page not found` or `Something went wrong`. Always English |
| `base_url` | Storefront base URL |
| `logo_url` | Company logo, or Fluid's logo when the company has none |
| `shop_url` | Attributable shop URL |

The [global variables](/themes/theme-variables#global-variables) are available too, including `company`, `localization`, `country`, `request`, `params`, `affiliate`, `routes`, `privacy_policy_path`, `terms_conditions_path`, `navbar`, `footer`, `sections`, and the on-demand collections such as `products` and `collections`.

A few of them read differently on an error page:

* `request.page_type` is `error_page`.
* `request.path` depends on why the page shows. When a missing record shows the 404, it's the path the visitor asked for, such as `/home/products/winter-blend`. In the other cases, it's the error route, such as `/404` or `/500`, not the path the visitor asked for.

## Localize the messages

<Warning>
  Most error pages render in English, whatever language the visitor has selected, and the `t` filter reads your English locale file, `locales/en.json`, on them.
  A 404 for a missing enrollment pack, playlist, or rep site renders in the visitor's language.
</Warning>

Put your error text in your locale files, and give every key an English value.
Add `default:` straight after `t` to give each string your own fallback text.
When no locale file defines the key, the fallback shows, and a `blank` check on the result is true.
Without `default:`, a missing key renders as "Translation missing" text on the page.
Keep other filters after `default:`: a filter between `t` and `default:` turns the missing-key text into ordinary text, and `default:` keeps it.

```json locales/en.json theme={null}
{
  "error": {
    "page_not_found": "We can't find that page",
    "page_not_found_message": "The link may be out of date, or the page may have moved.",
    "service_unavailable": "Something went wrong on our end",
    "service_unavailable_message": "Please try again in a few minutes.",
    "continue_shopping": "Continue shopping",
    "go_home": "Back to the home page"
  }
}
```

Add the same keys to your other locale files, for the error pages that follow the visitor's language.

The Base theme's error templates use `t` with `default:`, and its locale files define their `error.*` keys in all 19 of its languages.
A theme copied from Base before those keys were added has the templates without the keys, so its error pages show the templates' English fallback text.

## Example template

This template works as both error templates. Save the same file as `error_page/404/index.liquid` and `error_page/503/index.liquid`, and it branches on `error.status_code`.

```liquid error_page/404/index.liquid theme={null}
{% layout 'theme' %}
{{ 'template-error-page.css' | inline_asset_content }}

{% if error.status_code == 503 %}
  {% assign heading = 'error.service_unavailable' | t | default: "Something went wrong on our end" %}
  {% assign message = 'error.service_unavailable_message' | t | default: "Please try again in a few minutes." %}
{% else %}
  {% assign heading = 'error.page_not_found' | t | default: "We can't find that page" %}
  {% assign message = 'error.page_not_found_message' | t | default: "The link may be out of date, or the page may have moved." %}
{% endif %}

<section class="error-page">
  <a class="error-page__logo" href="{{ base_url }}">
    <img src="{{ logo_url }}" alt="{{ company.name | escape }}" width="160">
  </a>

  <p class="error-page__code">{{ error.status_code }}</p>
  <h1 class="error-page__title">{{ heading }}</h1>
  <p class="error-page__message">{{ message }}</p>

  <div class="error-page__actions">
    {% if error.status_code != 503 %}
      <a class="error-page__button" href="{{ shop_url }}">{{ 'error.continue_shopping' | t | default: "Continue shopping" }}</a>
    {% endif %}
    <a class="error-page__link" href="{{ base_url }}">{{ 'error.go_home' | t | default: "Back to the home page" }}</a>
  </div>
</section>
```

Keep the page's CSS in `assets/`, the same as any other template's CSS.
The Base theme names these assets after the template, `assets/template-error-page-404.css` and `assets/template-error-page-503.css`, and loads each one with `inline_asset_content` from its template.
A shared file such as `assets/template-error-page.css` works the same way.

## Preview your error pages

You can preview an error template in two ways:

* **The Page Editor's preview area** renders the template you're editing, from any theme, including saved changes you haven't published. `error.status_code` and `error.status_message` are blank there, so the example above shows its 404 text for both templates.
* **Preview** in the Page Editor opens the template on your storefront: `/404` for a 404 template, and `/errors/503` for a 503 template. The link names the template's theme, so it works for a theme that isn't active.

Error pages also follow a theme preview. While you preview another of your company's themes on the storefront, or run `fluid theme dev`, the 404 and 503 pages come from that theme, including the 404 a missing record shows.
If that theme has no published default template for the status code, you see Fluid's static page, not your active theme's template. That's what visitors will see once you publish the theme.

## Search engines and caching

* **Status codes.** Error pages return a real error status. An unknown path or a missing record returns `404` at the URL the visitor asked for, and `503` tells search engines the problem is temporary, so they retry later.
* **Caching.** Fluid's CDN caches only successful HTML pages. Error responses are sent as `private, no-store`, so a visitor never sees a cached error page and a fixed page shows up straight away.
* **Indexing.** Fluid doesn't add a `noindex` tag to your error pages. The error status keeps them out of search results, so you don't need one.

## Related pages

* [Theme variables](/themes/theme-variables#error-page-template)
* [Developer guide](/themes/developer-guide)
* [Page Editor in the Help Center](/help/admin/page-editor#error-pages)
* [Fluid CLI](/themes/cli)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.