Access Control
Control who can view your documentation with flexible access control options.
Overview
Control who can view your documentation by configuring access control settings. You can make your documentation fully public, fully private, or a mix of both.
Access control is configured in the dashboard under Settings > Access Control and takes effect after you publish (or re-publish) your documentation.
If you store your documentation source in your own Git repository, keep that repository private so the source content is not publicly visible. Access control settings apply only to the deployed documentation site, not to the visibility of your Git repository.

Access modes
-
Public: Anyone can access your documentation. No authentication required.
-
Private: All pages require authentication. Choose from Password, Documentation.AI, Email verification, JWT, or OAuth 2.0.
-
Partial: A mix of public and protected pages. Choose from Password, Documentation.AI, Email verification, JWT, or OAuth 2.0 for the protected pages.
Authentication methods
Documentation.AI offers five authentication methods to protect your documentation:
| Method | Available in | User-level access | Personalization |
|---|---|---|---|
| Password | Private, Partial | No (shared password) | No |
| Documentation.AI Auth | Private, Partial | Yes (organization members) | No |
| Email verification | Private, Partial | Yes (verified emails, per-user roles) | No |
| JWT | Private, Partial | Yes (per-user roles) | Yes |
| OAuth 2.0 | Private, Partial | Yes (per-user roles) | Yes |
Password
Simple shared password protection. Best for internal docs shared with a small team.
-
In Private mode, all pages are protected.
-
In Partial mode, you configure which pages are public and which require the password.
Set up password authentication ->
Documentation.AI Auth
Members of your organization sign in with their Documentation.AI account. You manage who can read from Settings > Members, and removing someone ends their access within 5 minutes. Every signed-in reader sees all protected pages. Available in Private and Partial modes, on all plans.
Set up Documentation.AI Auth ->
Email verification
Readers enter their email and a one-time code we send them, with no account or identity provider needed. Let every verified email in, or have an admin accept each reader and choose how long they can read and which roles they get. Available in Private and Partial modes, on Pro plans or higher.
JWT
Integrate with your existing authentication system using JSON Web Tokens. Supports per-user access roles and personalization. Available in Private and Partial modes.
OAuth 2.0
Integrate with OAuth 2.0 or OpenID Connect (OIDC) providers like Okta, Auth0, or Azure AD. Supports per-user access roles and personalization. Available in Private and Partial modes.
Set up OAuth 2.0 authentication ->
Role-based access control
With JWT, OAuth 2.0 and Email verification, you can control which pages each user can see using the access-roles property in your documentation.json navigation. With Email verification, a reader's roles are the ones an admin picks when accepting their request.
Users whose token includes at least one matching role can view the page. Pages without access-roles are visible to all authenticated users. A special wildcard role * grants access to all pages.
Password and Documentation.AI authentication don't use roles: every signed-in reader sees all protected pages, and access-roles is ignored. The same is true of Email verification with Automatic access.
Role-based access control is available with JWT, OAuth 2.0 and Email verification (with Request access) in both Private and Partial modes.
How it works
Add the access-roles property to any navigation node in your documentation.json. It accepts an array of role name strings.
{
"navigation": {
"tabs": [
{
"tab": "Guides",
"groups": [
{
"group": "Getting Started",
"pages": [
{ "title": "Introduction", "path": "intro" },
{ "title": "Quick Start", "path": "quick-start" }
]
},
{
"group": "Engineering",
"access-roles": ["engineering", "platform"],
"pages": [
{ "title": "Architecture", "path": "engineering/arch" },
{
"title": "Runbooks",
"path": "engineering/runbooks",
"access-roles": ["sre"]
}
]
}
]
},
{
"tab": "Admin",
"access-roles": ["admin"],
"groups": [
{
"group": "Operations",
"pages": [
{ "title": "Deploy Guide", "path": "admin/deploy" }
]
}
]
}
]
}
}
In this example:
-
Introduction and Quick Start are visible to all authenticated users (no
access-rolesset). -
Architecture is visible to users with the
engineeringorplatformrole (inherited from the group). -
Runbooks is visible only to users with the
srerole (overrides the group's roles). -
Deploy Guide is visible only to users with the
adminrole (inherited from the tab).
Inheritance rules
-
Default: If no
access-rolesis set anywhere, pages are visible to all authenticated users. -
Closest wins: The closest (nearest) explicit
access-rolesin the navigation tree determines which roles are required for that node and its subtree. -
Explicit override: A child node can override its parent by setting its own
access-roles. -
Empty array: Setting
access-roles: []explicitly makes a node visible to all authenticated users, even if a parent has role restrictions.
Navigation filtering
When a user authenticates with JWT, OAuth 2.0 or Email verification, the sidebar, search results, page navigation, and raw markdown export (/md/) are automatically filtered based on their roles. Pages the user cannot access do not appear anywhere in the UI, and the page renderer blocks direct URL access to restricted content.
If a user navigates directly to a restricted URL, they see an "Access Restricted" page instead of the content.
JWT claims format
Your JWT or OAuth provider must include the accessRoles field in the token claims:
{
"host": "docs.example.com",
"firstname": "Alice",
"company": "Acme Inc",
"accessRoles": ["engineering", "sre"]
}
The field names are fixed: host, firstname, company, and accessRoles.
Per-user content in MDX
With JWT or OAuth 2.0 authentication, you can personalize page content for each authenticated reader using these variables directly in your MDX:
{user.firstname}: the reader's first name from the token claims{user.company}: the reader's company from the token claims{user.accessRoles}: the reader's assigned access roles
Anonymous visitors see blank values for these variables, so you can safely use them in Partial mode pages that are also visible to the public.
The signed-in reader's identity is also available as window.dai.user in custom scripts, so you can build personalized experiences beyond standard MDX content.
Signing out
Signed-in readers can sign out from the navbar, where a sign-out button sits next to their name. Readers who signed in with a password see the button on its own.

Where a reader lands after signing out depends on how they signed in:
- OAuth 2.0 with a Logout URL: they continue to your provider's logout page, which ends their session there too.
- Every other method: they land on a You've signed out page, and stay signed out until they choose Back to documentation. A private site doesn't sign them straight back in.
Signing out of your own product or identity provider doesn't sign a reader out of your docs. Their docs session lasts until it expires, or until they use the docs' sign-out.
Use your own sign-out button
To replace the built-in button, turn off Show sign-out button under Settings > Access Control > Sign Out, then re-publish. Readers stay signed in; only the button goes. Then add your own with a custom script:

// Show a Sign out button in the navbar to signed-in readers.
if (window.dai.auth.signedIn) {
const button = document.createElement('button');
button.textContent = 'Sign out';
button.addEventListener('click', () => window.dai.auth.signOut());
document.querySelector('.dai-navbar-buttons')?.append(button);
}
The built-in button calls the same window.dai.auth.signOut(), so both behave the same way.
| API | What it does |
|---|---|
window.dai.auth.signedIn | true while a reader is signed in. Read it when you need it: it stays correct after a sign-in or sign-out. |
window.dai.auth.method | How the reader signed in: password, documentationai, email_otp, jwt_eddsa or oauth2. null when signed out. |
window.dai.auth.signOut(options) | Signs the reader out and leaves the page. Pass { redirect: '/goodbye' } to choose where they land. Resolves false if a dai:sign-out listener cancelled it, and rejects if the request fails. |
window.dai.on(name, handler) | Listens for the dai:<name> event on document. Returns a function that stops listening. |
Before sign-out starts, a dai:sign-out event fires on document. Its event.detail is { method, redirect }, and calling event.preventDefault() stops the sign-out.
// Ask before signing out.
window.dai.on('sign-out', (event) => {
if (!window.confirm('Sign out of the docs?')) event.preventDefault();
});
On embedded docs, sign readers out from your own app with DocumentationAI.signOut() instead.