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.
Member sites must be turned on for your company by Fluid. To ask for them, contact support. This page is about member sites v2, which replaces theme member sites. 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.fetchallows. 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 lintchecks eachfluid.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
accessfield 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.jsonon 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.
- 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.
config/access.json
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>: reador<family>: write, such astasks: read.writedoesn’t includeread, 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, declareprofile: readand callfluid.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’sdisplay_nameonly for a site with no declaration or one that listsmember.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.
Use
read for calls that read and write for calls that change something.
- The mode comes from the request. A
GETrequest needsread. Any other method that Fluid forwards needswrite. - 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.
addressesandsubscriptionsare read only: a write is refused for every declaration. Push devices undernotificationsare read only too. session: readis never declared. Every bridged call needs the viewer’s session first, so Fluid grantssession: readto 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.
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.jsonand 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 pushleaves it alone.
pull, push and publish. Publish, restore, copy and take down a member site covers what a restore does to a declaration.
Check a declaration
The CLI carries the file with the other site files:pullwrites it.pullwritesconfig/access.jsoninto the folder when the site has a draft declaration. When the site no longer has one,pulldeletes 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.pushsends it.pushsends the file with the other site files, and a push with no edit sends nothing for it.pushrefuses an invalid declaration before it sends any request.
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
usesor 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.fetchcall the declaration lacks is a warning. When the file is valid, lint checks eachfluid.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 aPATCHto/api/member/v2026-10/mewithoutprofile: 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 withfluid.fetch("/api/member/v2026-10/me"), which needsprofile: read.
See what a site declares
The site’s payload in the member API and the company API carries the published declaration asaccess, 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 and Get a member site.
Handle a refusal
The owner of a site that published a declaration gets a403 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.
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.