> ## 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, run `fluid signup` from the Fluid CLI, or 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. Before authenticated calls are possible, the merchant must sign the agent in to the Fluid CLI with the emailed code and --shop set to the company's fluid_shop (which also confirms the signup), or open the emailed confirmation link and give the agent an API token.
> 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 on an operation whose spec documents those params — mostly the unversioned company-v0 admin surface, plus some lists on other surfaces, versioned ones included; page every other list with cursors.
> 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 except two operations that take no credential, POST /api/company (create a company) and GET /api/agreements/{id} — 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.

# Declare what a member site uses

> List the member API scopes a member site's scripts use in config/access.json, check them with fluid member-site lint, and publish the declaration with the site. Fluid refuses a call the declaration lacks.

A member site's JavaScript reads the signed-in owner's data and acts as the owner through `window.fluid.fetch`. Each path it calls needs a scope, such as `profile: read`. A site says which scopes its scripts use in one file, `config/access.json`. Because a site is code its owner wrote, Fluid doesn't hand that code everything. Once the site publishes a declaration, Fluid lets the owner's code reach only what the file lists. Lint checks your scripts against the file, and Fluid publishes it with the site.

This page lists every scope a site can declare. It also covers how to write the file, how to check it before you publish and where the published declaration is reported. To see the calls themselves, read [Read viewer data in site scripts](https://docs.fluid.app/member-sites/site-scripts.md).

> [!NOTE]
>
> Member sites must be turned on for your company by Fluid. To ask for them, [contact support](https://docs.fluid.app/help/getting-help.md). This page is about [member sites v2](https://docs.fluid.app/member-sites/overview.md), which replaces [theme member sites](https://docs.fluid.app/themes/member-storefront/member-sites.md). Theme member sites are deprecated and will be removed.

## Know how a declaration works

* **A declaration limits the owner's code.** Each entry is a scope, such as `tasks: write`. Once the site has published a declaration, it narrows what the owner's code can reach to the scopes it lists. Fluid refuses a call whose scope the file lacks, so a call you forgot to declare fails for you first.
* **A site with no declaration behaves as it did before.** The owner of a member's site can call every route `fluid.fetch` allows. Every other viewer gets no data from the site's scripts, with or without a declaration.
* **A declaration only narrows.** The routes that no site can reach, and the routes a site can read but not change, stay that way for every declaration. A guest gets nothing either way.
* **Lint checks your scripts against it.** `fluid member-site lint` checks each `fluid.fetch(path, { method })` call against the scope of that path. A site with no file isn't checked this way.
* **A declaration is a draft until you publish.** Fluid publishes the declaration together with the site's settings. A site's `access` field reports the declaration the site last published, never the draft.
* **Only member-owned sites have one.** A company site takes no declaration. The file API refuses `config/access.json` on a company site, and a company site never lists it.
* **Nobody else gains anything.** Sharing a site, sharing it again and a site's declaration never give a share holder, an admin or a visitor a scope. Only the signed-in owner of a member-owned site gets data from the site's scripts. See [Know who gets data](https://docs.fluid.app/member-sites/site-scripts.md#know-who-gets-data).
* **Narrowing is immediate.** A take-down, an owner who is no longer active and a publish that drops a scope all refuse the next call. Fluid checks the site on every request.

## Write a declaration

`config/access.json` is one JSON object with one key, `uses`. It holds a list of scope strings.

```json config/access.json theme={null}
{
  "uses": [
    "profile: read",
    "tasks: read",
    "tasks: write"
  ]
}
```

This declaration fits a site whose scripts read the member, list tasks, and complete them:

```js theme={null}
await fluid.fetch("/api/member/v2026-10/me");
await fluid.fetch("/api/member/v2026-10/tasks");
await fluid.fetch("/api/member/v2026-10/tasks/1/complete", { method: "POST" });
```

It lets the owner's code read the signed-in member and their tasks, and complete a task. It asks for nothing else. A call to change the member's details, such as `fluid.fetch("/api/member/v2026-10/me", { method: "PATCH", body })`, needs `profile: write`. This file doesn't list it, so Fluid refuses the call with a `403` that names the scope.

* A **scope** is `<family>: read` or `<family>: write`, such as `tasks: read`. `write` doesn't include `read`, so a site that does both declares both.
* The file is at most 4 KiB and lists at most 64 entries, with no entry twice.
* Fluid refuses a file with an unknown key, a repeated key, an entry that isn't a string, an unknown field, family or mode, or `session: read`.
* Don't declare `session: read`. Lint reports it as an error.
* `{ "uses": [] }` declares nothing. That is not the same as having no file. With no file, the site behaves as it did before. With an empty list, the site asks for nothing, so its scripts reach no data. Lint doesn't check calls when there is no file, and it warns on every call when the list is empty.
* A file written earlier may still list entries such as `member.email`. Fluid accepts them so that file stays valid, but your scripts don't need them. To read the member in a script, declare `profile: read` and call `fluid.fetch("/api/member/v2026-10/me")`.
* A field entry grants no call. A declaration of member fields alone lists no scope, so the site's scripts reach no data. The one field Fluid reads is `member.full_name`. The session answer, `GET /api/member-storefront/session`, carries the owner's `display_name` only for a site with no declaration or one that lists `member.full_name`. Otherwise it is blank.

## Declare scopes

A scope names a family of the member API and a mode. These are the families a site can declare.

| Family | What it covers |
| - | - |
| `addresses` | The member's saved addresses |
| `agreements` | The agreements that apply to the member and their acceptance |
| `ai` | Conversations with the assistant, their messages and feedback |
| `calendar` | Calendar events and catch-ups |
| `carts` | The member's carts |
| `contacts` | Contacts and their notes and tasks, contact imports, groups and notes |
| `media` | Media, direct uploads, playlists and their items |
| `messages` | Conversations and messages, and sending a message from a native widget |
| `mysite` | MySite settings, themes, links and statistics |
| `notifications` | Notification preferences, push devices and the activity feed |
| `orders` | The member's orders |
| `plan` | The member's Pro plan, the plans on offer and paid add-ons |
| `points` | The points balance and ledger |
| `profile` | The signed-in member and their profile and social links |
| `search` | Catalog search |
| `shares` | Tracked shares, referral shares and photo shares, including from a native widget |
| `stats` | Dashboard totals and top assets |
| `subscriptions` | The member's subscriptions, their bundles, discounts and waivers, and a native widget's subscription summary |
| `tasks` | The member's tasks, including completing and reopening one from a native widget |
| `widgets` | The member storefront's native widget routes for data, schedules, sales goals and earnings goals |

Use `read` for calls that read and `write` for calls that change something.

* **The mode comes from the request.** A `GET` request needs `read`. Any other method that Fluid forwards needs `write`.
* **The family comes from the route.** Fluid takes it from the route that serves the request, so a request can't borrow another family's scope by changing its path.
* **A route with no family is refused.** A site that has a declaration can't call a route that appears in no family above.
* **Some routes are read only.** `addresses` and `subscriptions` are read only: a write is refused for every declaration. Push devices under `notifications` are read only too.
* **`session: read` is never declared.** Every bridged call needs the viewer's session first, so Fluid grants `session: read` to any declaration that holds at least one other scope. A declaration with no scope, member fields alone included, gets no session. The owner's frame gets no handshake and a blank session, so the site's scripts reach no data.
* **A declaration never opens a route that the member API keeps off the browser.** Saved payment methods, the member's companies, the member API's Exigo routes, and site management and shares stay refused for every site.

The path decides the scope. [Read viewer data in site scripts](https://docs.fluid.app/member-sites/site-scripts.md#know-the-scope-of-a-path) shows the scope of common paths. The [API reference](https://docs.fluid.app/api-reference) lists the rest of the member API. When you aren't sure which scope a path needs, call it from a script and run `fluid member-site lint`: it names the scope the file lacks. A native widget route, such as the team tree, needs `widgets: read`, not a `team` scope.

## Publish, change and remove a declaration

Write the file with the file API, or with the CLI by editing the pulled folder and pushing it. A declaration you write is a draft until the site publishes.

* **Publish.** Publishing the site publishes the declaration together with the settings.
* **Change.** Edit the file, push it and publish again.
* **Remove.** Delete `config/access.json` and push. That clears the draft declaration only: the published one stays until you publish the site. Publishing a site with no draft declaration removes the one it had published, and the site returns to the behavior it had with no declaration.
* **Copy.** A member's copy of a member's site starts with the original's published declaration as its own draft. A company admin's copy of a member's site starts with none, because a company site takes no declaration. After that, changes to either site never change the other.
* **Company sites.** The file API refuses the path on a company site, and `fluid member-site push` leaves it alone.

See [Write a site file](https://docs.fluid.app/api-reference/site-files/write-a-site-file.md) and [Publish a site](https://docs.fluid.app/api-reference/sites/publish-a-site.md) for the API, and the [CLI page](https://docs.fluid.app/member-sites/cli.md) for `pull`, `push` and `publish`. [Publish, restore, copy and take down a member site](https://docs.fluid.app/member-sites/publishing.md) covers what a restore does to a declaration.

## Check a declaration

The CLI carries the file with the other site files:

* **`pull` writes it.** `pull` writes `config/access.json` into the folder when the site has a draft declaration. When the site no longer has one, `pull` deletes your local copy if you haven't edited it since the last pull or push. A copy you edited stays, unless you pull with `--force`.
* **`push` sends it.** `push` sends the file with the other site files, and a push with no edit sends nothing for it. `push` refuses an invalid declaration before it sends any request.

Run `fluid member-site lint` in the folder before you push. It sends no request.

* **An invalid file is an error.** Lint reports each problem the file API would refuse: not valid JSON, a key other than `uses` or a repeated key, an entry that isn't a string or appears twice, an unknown field, family or mode, `session: read`, more than 64 entries, and a file over 4 KiB.
* **A `fluid.fetch` call the declaration lacks is a warning.** When the file is valid, lint checks each `fluid.fetch(path, { method })` call in a script against the scope of that path. It warns when the declaration doesn't list the scope, such as a `PATCH` to `/api/member/v2026-10/me` without `profile: write`. It also warns when the route is one a site can never reach, and when it can't read the call, for example because the path or method comes from a variable.
* **A Liquid file that reads `member.<field>` is a warning.** Lint warns whatever the declaration holds, because member data is leaving member site Liquid. Read it in a script with `fluid.fetch("/api/member/v2026-10/me")`, which needs `profile: read`.

A warning doesn't fail the command.

## See what a site declares

The site's payload in the member API and the company API carries the published declaration as `access`, with `declared` and the list of `uses`. `declared` is `false` for a site that has published no declaration, and for a company site. Menu entries in the member's account menu carry it too.

The summary reports the declaration the site published. See [Show a site](https://docs.fluid.app/api-reference/sites/show-a-site.md) and [Get a member site](https://docs.fluid.app/api-reference/member-sites/get-a-member-site.md).

## Handle a refusal

The owner of a site that published a declaration gets a `403` for a call whose scope the file lacks. The body's `details.error` is `member_site_scope_undeclared`, and `details.scope` names the missing scope, such as `profile: write`. `fluid.fetch` resolves to that `Response`, so read it and add the scope. See [Handle the Response](https://docs.fluid.app/member-sites/site-scripts.md#handle-the-response).

Only the owner sees the scope named. Every other refusal keeps one body, `member_site_unavailable`, whether the site is unknown, unpublished or company-owned, the viewer isn't its owner, or no scope covers the path. A guest gets the local `401` with the error `guest`.

## Fix a declaration

| You see | Why | What to do |
| - | - | - |
| A `403` that names `orders: read` | The site published a declaration without that scope | Add the scope, push and publish the site |
| A write to a family fails and a read works | The file lists `read` and not `write` for the family | Add the `write` scope, or remove the call |
| A write to `addresses` or `subscriptions` fails whatever you declare | Those families are read only for every declaration | Remove the call |
| Every call fails for the owner | The declaration lists no scope | List the scopes your scripts use, push and publish the site |
| Lint warns about a `fluid.fetch` call | The script calls a path whose scope the file lacks, a route no site can reach, or a call lint can't read | Add the scope, remove the call, or write the path and method as literals |
| Lint reports `session: read` as an error | The file lists a scope you never declare | Remove the entry |
| Lint warns about `member.<field>` in a Liquid file | Viewer data is leaving member site Liquid | Read it in your script with `fluid.fetch("/api/member/v2026-10/me")` and declare `profile: read` |
| A share holder or visitor gets no data | Only the owner gets data from the site's scripts | This is by design. Sharing grants nothing |
| The CLI skips `config/access.json` | The site is a company site | Company sites take no declaration |
| The site's `access` doesn't show your edit | The declaration is still a draft | Publish the site |


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