Who may read, sign up and sign in to a space
A space's reader surface has three questions to answer about every reader: are they who they say they are, may they come in, and have they agreed to what you require of them? This guide covers the policy settings that answer the second and third, the moderator queue for reader accounts that need approval, and the public documents a custom front end reads to render the right sign-in step. How readers actually prove who they are is configured separately, in Reader sign-in methods and Sign readers in from your own app with a JWT.
Two policies, each with an org default and a space override
Both policies below work the same way. The organization default is an ordinary org setting (PUT /orgs/{orgId}/settings/{key}, reset with DELETE on the same address). A space may store an override on top of it. Resolution is space, then org, then the built-in default, and every field is resolved on its own.
Two rules apply to every override:
An override that states no field does not turn the policy off. It inherits the org default, exactly as having no override does. So
PUT {}is harmless.To remove an override,
DELETEit. That removes the key rather than storing an empty record, and it is idempotent: deleting a space with no override answers 200, never 404.
Reads and writes require the space.update capability. The bodies are strict: an unknown key is a 422 rather than a 200 that stores nothing.
What a required legal document withholds
When a document is marked "members must accept it", the legal gate decides what a reader who has not accepted the current edition is kept from.
| What is withheld |
|---|---|
| Nothing. Acceptance is recorded when it happens. The default. |
| Posting, commenting, voting and applying. Reading stays open. |
| The space's content, until the reader accepts. |
| The session itself: sign-in is not completed until the edition is agreed. |
anonymous says what a read gate does about a reader it cannot identify: require_identity (the default) asks them to sign in, because an acceptance recorded against nobody is not evidence; cookie records it against a cookie-scoped pseudonymous subject, which stops re-asking the same browser and is marked verification: 'anonymous' in the register and the CSV export.
PUT /orgs/{orgId}/spaces/{spaceId}/legal-gate
{ "scope": "read", "anonymous": "require_identity" }A read or signin gate on a space that offers no reader sign-in is refused with a named 422, because there would be nobody to record an acceptance against and no way through. GET answers the stored fields plus hasOverride and inherits; the org-level keys are legal.require_acceptance and legal.anonymous_acceptance. See Legal documents: binding and acceptance for the documents themselves.
How far one reader identity carries
By default a reader who signed in to one space is a stranger in the next, even on the same domain. Set scope to org and one identity carries across every space of the organization on that host.
PUT /orgs/{orgId}/spaces/{spaceId}/reader-identity
{ "scope": "org" }The values are space (default) and org; the org-level key is reader.identity_scope. Identity is not admission: signing in somewhere does not admit a reader to a space whose own gate they have not passed. Readers already signed in per space are adopted rather than asked again.
Limiting a space to some networks
A third way to decide who may come in needs no sign-in at all: a network allow-list. settings.networkAccess.ipAllowList lists IP addresses or CIDR blocks (203.0.113.0/24), set with PUT /orgs/{orgId}/spaces/{spaceId}/network-access or in the studio under Settings → Network access. Empty, the default, means everyone. With a list, a reader from any other address sees a "Not available from this network" page, and the space's JSON, feeds, markdown twins and widgets answer 403 space.network_refused. The network is checked before a password or sign-in is asked for, and it applies to every space type except status pages, which keep their own list. See Limit a space to some networks.
Approving reader accounts
A new reader account waits in a queue until a moderator decides when the reader sign-in registry resolved for the space has approveNewMembers: true (on the organization's registry or the space's override), or when the organization setting users.must_approve_users is on. A reader whose email domain is listed in users.auto_approve_email_domains skips the queue that the organization setting creates, but never a registry's approveNewMembers. A pending reader still gets a session, so the reader surface can tell them they are awaiting approval. Listing, approving and rejecting need the forum.moderate capability.
GET /orgs/{orgId}/spaces/{spaceId}/reader-signups?status=pending
POST /orgs/{orgId}/spaces/{spaceId}/reader-signups/{signupId}/approve
POST /orgs/{orgId}/spaces/{spaceId}/reader-signups/{signupId}/rejectstatus is pending (the default), approved or rejected; any other value is a 422 rather than quietly showing you the pending list. Each row carries id, userId, email, name, status, source and createdAt, newest first, up to 500. Approving grants the reader a membership so they can post. Rejecting leaves them with read access but no posting.
What a custom front end reads
A reader surface has to know which sign-in methods to offer and which policy applies before it renders anything. Both documents are public, hold no secrets, and need no token:
GET /public/orgs/{orgId}/spaces/{spaceId}/reader-auth
GET /public/orgs/{orgId}/spaces/{spaceId}/reader-policyreader-auth is the resolved, public-safe sign-in configuration (the same registry you write with PUT .../reader-auth, with every secret left out). reader-policy returns { legalGate, readerIdentity }, each field naming the level that decided it (space, org or default) so a client never has to infer it. Both are rate limited to 60 requests a minute per client.
The sign-in round trips
These are the public addresses a reader's browser passes through. None needs a token. Call them on the host the space is read on (<mount>/_api/... on a custom domain, /api/... on ours), because the cookies they set are host-only.
GET /public/orgs/{orgId}/spaces/{spaceId}/gate answers the space's gate
mode(none,password,jwtoroidc) withspaceName,spaceIdandspaceSlug, so one sign-in page of your own can serve every space.GET /orgs/{orgId}/reader-auth/setupprints the two addresses a redirect sign-in needs.startUrl, which is GET /public/orgs/{orgId}/spaces/{spaceId}/visitor-oidc/start, sends the reader to the identity provider:?method=picks a provider from the registry (google,microsoft,oidcand so on) and?returnTo=is checked against the space's addresses, defaulting to the space's primary address.redirectUri, which is GET /public/visitor-oidc/callback, is the one callback you register at your provider for every space. The signed state carries the organization, the space and the return path; the callback sets the reader cookie (12 hours) and, when the reader gets an account, the session cookie, then redirects back.A sign-in that returns to another host, such as your own domain, finishes in one more hop. The callback cannot set cookies there, so it hands the reader to GET /public/visitor-oidc/adopt under the space's mount with a ticket that is bound to that origin, lives 60 seconds and works once. An expired or replayed ticket sends the reader on without signing them in.
POST /public/orgs/{orgId}/spaces/{spaceId}/reader-auth/jwtredeems a JWT your own app minted, and GET /public/orgs/{orgId}/spaces/{spaceId}/reader-auth/jwt?jwt=… does the same from a link and redirects; see Sign readers in from your own app with a JWT.
Signing a reader out
POST /public/reader/sign-out signs a reader out on the host that answers. It revokes the session the reader sign-in created, and the Atlas session with it, and clears every reader cookie the request carries: forumulate_session, fm_signed_in and each cflx_vis_* pass. Call it same-origin, because a host-only cookie can only be cleared by its own host. The body is optional; send orgId and spaceId (and returnTo) and redirectTo in the answer is the space's signOutUrl carrying the return address, so one click also signs the reader out of your own app. A plain HTML form post is answered with a 303 to that address.
POST /auth/logout is narrower: it revokes the forumulate_session the request carries and clears that one cookie, answering { "ok": true }. Use the reader sign-out above for a reader, since logout leaves the reader passes in place.
In the API
Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.
Reader Auth Public
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the reader auth in a space |
|
Reader Policy
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| One space's legal-acceptance gate override |
| |
| Replace one space's legal-acceptance gate override |
| |
| Remove one space's legal-acceptance gate override |
| |
| One space's reader-identity scope override |
| |
| Replace one space's reader-identity scope override |
| |
| Remove one space's reader-identity scope override |
|
Reader Policy Public
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the reader policy in a space |
|
Reader Signup
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List reader signups in a space |
| |
| Approve the reader signup |
| |
| Reject the reader signup |
|
Space Network Access
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| A space's network allow-list |
| |
| Set a space's network allow-list |
|