Retool user permissions work at two levels: organization-wide groups (Settings → Roles & Permissions) and per-app sharing (Share button → app access). Grant Use, Edit, or Own access per app per user or group. Hide app features using {{ current_user.groups.includes('groupName') }} in component Hidden properties for role-specific interfaces.
| Fact | Value |
|---|---|
| Tool | Retool |
| Difficulty | Beginner |
| Time required | 15-20 min |
| Compatibility | Retool Cloud and Self-hosted |
| Last updated | March 2026 |
Per-App Access Control and User-Specific Interfaces
Retool permissions have two layers that work together. The first is organization-level: which actions a user can perform system-wide (build apps, manage resources, invite users). This is configured in Settings → Roles & Permissions.
The second layer is app-level: for each individual app, who can view it, who can edit it, and who owns it. This is where you grant specific teams access to specific tools. A Finance team member might have Viewer-level org permissions but Use access to exactly the Finance Dashboard app — and nothing else.
Within an app, you can create role-specific interfaces by conditionally hiding components based on {{ current_user.groups }}. This lets you build a single app that shows different features to managers, sales reps, and viewers — without maintaining multiple apps.
Prerequisites
- Admin or app Owner access in Retool
- At least one published Retool app to configure permissions on
- Users already added to the Retool organization
- Basic understanding of Retool's group system (All Users, Viewer, Editor, Admin)
Step-by-step guide
Open the Share panel for a specific app
Open the app in edit mode. Click the Share button in the top-right toolbar (it looks like a person with a + symbol). The Share panel opens showing the current access list for this app: which users and groups have Use, Edit, or Own access. By default, newly created apps may only be visible to Admins and the creator.
Expected result: The Share panel is open and shows the current access list for the app.
Understand the three app access levels
Each app has three access levels. Use: the user can open and run the app, trigger queries, interact with all components — but cannot modify the app's design or logic. Edit: the user can modify the app, add components, change queries, and update settings. Own: the user can delete the app, manage its sharing settings, and do everything Edit can do. Most end users should have Use access only.
1// App access levels comparison:2// Use:3// - Open and run the app4// - Trigger queries (click buttons, submit forms)5// - See all components visible to their role6// - Cannot: edit app design, add queries, change settings78// Edit:9// - All of Use10// - Modify app components, queries, event handlers11// - Change app settings12// - Cannot: delete app, manage sharing1314// Own:15// - All of Edit16// - Delete the app17// - Manage app sharing settings18// - Usually: the app creator + senior buildersExpected result: You understand which access level to grant each user type before proceeding.
Grant app access to users or groups
In the Share panel, click '+ Add' (or 'Invite people') to add a user or group. Type the user's email or group name. Select the access level (Use, Edit, or Own) from the dropdown. Click Save or Confirm. Repeat for each user or group. You can also select a group like 'Finance' or 'Support' to grant access to all members at once instead of individual users.
Expected result: The users or groups appear in the Share panel with their assigned access level. They can now open the app.
Configure the All Users group as a baseline
The All Users group contains every member of your Retool organization. If you grant Use access to All Users on an app, every org member can run it. This is appropriate for truly org-wide tools (company directory, IT request form). For department-specific apps, do not grant All Users access — restrict to specific groups. You can configure All Users access in the Share panel the same way as any other group.
Expected result: All Users (or restricted groups) have the appropriate baseline access to the app.
Create role-specific interfaces with current_user.groups
Within an app, reference the current logged-in user's groups to show or hide components. The current_user object has groups (array of group names), email, fullName, and id properties. Use these in Hidden, Disabled, and other Inspector property fields to create differentiated interfaces for different roles without building separate apps.
1// Show a Manager-only section:2// Container Hidden property:3{{ !current_user.groups.includes('managers') }}45// Show Delete button only for Admin:6// Button Hidden property:7{{ !current_user.groups.includes('admin') }}89// Show different tabs based on role:10// Tab 'Overview' — visible to all (no Hidden condition)11// Tab 'Analytics' — Hidden: {{ !current_user.groups.some(g => ['managers', 'admin'].includes(g)) }}12// Tab 'Settings' — Hidden: {{ !current_user.groups.includes('admin') }}1314// Show user's own records only for non-managers:15// This goes in a SQL query's WHERE clause:16// WHERE (assigned_to = {{ current_user.email }}17// OR {{ current_user.groups.includes('managers') }})Expected result: Components show or hide based on the logged-in user's group membership, creating a role-appropriate interface.
Filter query data based on the current user
Beyond hiding UI elements, you can use current_user properties to filter the data returned by queries. For example, a sales rep should only see their own leads, while a manager sees all leads. Reference current_user.email or current_user.id in SQL WHERE clauses to apply row-level data restrictions per user.
1-- SQL Query: getMyLeads2-- Shows the current user's leads, or all leads for managers3SELECT l.*, u.name as owner_name4FROM leads l5JOIN users u ON u.id = l.assigned_to6WHERE 7 -- Non-managers see only their leads8 (9 {{ current_user.groups.includes('managers') }}10 OR l.owner_email = {{ current_user.email }}11 )12ORDER BY l.created_at DESC;Expected result: Sales reps see only their leads in the table; managers see all leads — from the same app with the same query.
Complete working example
1// === Pattern 1: Data filtering by user role (SQL Query) ===2// Query: getAccessibleRecords3// SELECT * FROM records4// WHERE5// {{ current_user.groups.includes('admin') }} -- Admin sees all6// OR {{ current_user.groups.includes('managers') }} -- Managers see all7// OR owner_id = {{ current_user.id }} -- Others see own records89// === Pattern 2: Permission check before mutation (JS Query) ===10// JS Query: performSensitiveAction11const user = current_user;12const allowedGroups = ['admin', 'managers'];1314if (!user.groups.some(g => allowedGroups.includes(g))) {15 utils.showNotification({16 title: 'Access denied',17 description: `This action requires ${allowedGroups.join(' or ')} access.`,18 notificationType: 'error'19 });20 return;21}2223// Proceed with the privileged action24await sensitiveQuery.trigger();25utils.showNotification({ title: 'Action completed', notificationType: 'success' });2627// === Pattern 3: Dynamic greeting + role info (Text component value) ===28// {{ `Hello ${current_user.fullName}. Your role: ${current_user.groups.filter(g => !['All Users', 'viewer'].includes(g)).join(', ') || 'Viewer'}` }}2930// === Pattern 4: Show/hide columns in Table ===31// In Table Inspector → Column settings → Hidden:32// 'Internal Notes' column: {{ !current_user.groups.includes('admin') }}33// 'Salary' column: {{ !current_user.groups.some(g => ['hr', 'admin'].includes(g)) }}Common mistakes
Why it's a problem: Forgetting that current_user.groups is case-sensitive, causing includes() to always return false
How to avoid: Either standardize all group names to lowercase in Settings → Roles & Permissions, or use .map(g => g.toLowerCase()).includes('groupname') in your expressions.
Why it's a problem: Granting Edit access when Use access is sufficient for an end user
How to avoid: Use access lets users run queries, interact with components, and view data. Only grant Edit when the person needs to modify the app's design or logic. Most business users only need Use.
Why it's a problem: Relying on query data filtering in SQL as the only security measure
How to avoid: {{ current_user.groups.includes('managers') }} in a SQL WHERE clause filters at the app layer. Tech-savvy users can potentially bypass this. Implement row-level security in your database and use the app-layer check only for UX. For sensitive data, always have both.
Why it's a problem: Using current_user in public (unauthenticated) apps without null checks
How to avoid: In public apps, current_user may be null or have empty groups. Use {{ current_user?.groups?.includes('admin') }} with optional chaining to prevent errors in unauthenticated contexts.
Best practices
- Grant access to groups, not individual users — group-based access is easier to maintain as team membership changes.
- Use the Share panel's group access rather than hardcoding email addresses in query WHERE clauses for access control.
- Combine app-level sharing (who can open the app) with in-app role checks (current_user.groups) for defense in depth.
- Keep group names in lowercase and consistent — 'admin' not 'Admin', 'managers' not 'Managers'. Case-sensitive string matching causes hard-to-debug permission issues.
- Back up in-app UI restrictions with server-side query guards in JS Queries — UI Hidden properties can be circumvented.
- Audit app access lists quarterly to remove users who have changed roles or left teams.
- Use Retool Modules for sub-features that need consistent permission logic across multiple apps.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a Retool app used by three teams: Sales, Managers, and Admins. I want to: (1) give Sales only Use access (no editing), (2) show Sales reps only their own deals in the table, (3) show Managers all deals and an analytics panel that Sales cannot see, (4) show Admins a Settings section that only they can access. How do I configure the Share panel access levels and write the {{ current_user.groups }} expressions for each conditional?
In my Retool app, write: (1) a SQL query WHERE clause that shows all records to admin and managers but only the current user's records (WHERE owner_email = {{ current_user.email }}) to regular users, (2) a Container Hidden property that shows a 'Management Tools' section only to users in the 'managers' or 'admin' group, and (3) a JS Query permission guard that checks current_user.groups before running a delete mutation.
Frequently asked questions
Can a user have Use access to an app but still trigger write queries (INSERT, UPDATE, DELETE)?
Yes. Use access controls whether the user can open and interact with the app, not which queries can execute. A user with Use access can trigger any query the app exposes — including mutations. To restrict write access, add role checks inside the queries themselves or gate the triggering buttons with 'Only run when' conditions based on current_user.groups.
How do I find all apps that a specific user has access to in Retool?
In Settings → Users, click on the user to view their profile. The user detail page shows their group memberships. To see all apps they have access to, go to the Apps list and filter or check each app's Share settings. Retool does not currently offer a single view of 'all apps accessible to user X' — you need to check per-app or per-group.
What happens to app access when a user is removed from Retool?
When a user is removed from the organization (Settings → Users → Remove), they immediately lose access to all Retool apps and cannot log in. Their app access entries are removed automatically. For SSO-based users, disabling the account in your identity provider also terminates active Retool sessions within the session timeout period.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation