Custom Access Request Page
This feature is available in our custom plans. Please contact us to get access.
Introduction
Ever had users message you frantically because they can't access a dashboard? With custom 403 pages, you can turn that dead-end "Access Denied" screen into something actually useful.
Instead of users hitting a wall, you can:
- Send them to a request form - Link to Google Forms, Jotform, whatever you use
- Show who to contact - "Need the sales dashboard? Email [email protected]"
- Give them next steps - Link to your wiki, ticket system, or training docs
- Pre-fill their info - Their name, email, and the dashboard they need are already filled in
This helps streamline the access request process and reduce the number of adhoc Slack messages.
Common Use Cases
The "Request Access" Button
This is what most teams use. User can't see a dashboard? They click a button, fill out a form, done. You get notified and can approve it.
"Here's How to Get Access"
Some companies have specific processes - maybe you need to complete data training first, or submit a ticket through ServiceNow. Link directly to those resources so users can help themselves.
Quick Setup
- Go to Organization Settings → Custom 403 Page
- Grab one of the templates below (or write your own)
- Replace the example links with your actual form URLs
- Hit save
That's it. Next time someone hits a restricted dashboard, they'll see your custom message instead of a generic error.
Ready-to-Use Templates
Template 1: "Request Access" Button
- Preview
- HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>403 - Access Forbidden</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
min-height: 100vh;
background: #f5f5f5;
color: #374151;
line-height: 1.5;
font-size: 14px;
}
.navbar {
background: #ffffff;
border-bottom: 1px solid #e5e7eb;
padding: 16px 24px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
}
.logo {
height: 32px;
width: auto;
}
.main-content {
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 73px);
padding: 24px;
}
.container {
max-width: 520px;
width: 100%;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
border: 1px solid #e5e7eb;
overflow: hidden;
}
/* Modal Header */
.modal-header {
padding: 24px 24px 20px 24px;
border-bottom: 1px solid #e5e7eb;
position: relative;
}
.error-title {
font-size: 18px;
font-weight: 500;
color: #111827;
margin-bottom: 8px;
}
.error-message {
color: #9ca3af;
font-size: 14px;
font-weight: 400;
line-height: 1.5;
}
/* Modal Body */
.modal-body {
padding: 24px;
}
.info-section {
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 6px;
padding: 16px;
}
.info-title {
font-size: 14px;
font-weight: 500;
color: #111827;
margin-bottom: 12px;
}
.info-grid {
display: grid;
gap: 8px;
}
.info-item {
display: grid;
grid-template-columns: 70px 1fr;
gap: 12px;
font-size: 14px;
align-items: start;
}
.info-label {
color: #6b7280;
font-weight: 400;
text-align: left;
}
.info-value {
color: #111827;
word-break: break-all;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
background: #ffffff;
padding: 4px 8px;
border-radius: 4px;
border: 1px solid #e5e7eb;
font-size: 13px;
}
/* Modal Footer */
.modal-footer {
padding: 16px 24px 24px 24px;
border-top: 1px solid #f3f4f6;
display: flex;
justify-content: flex-end;
gap: 8px;
}
.dashboard-button {
display: inline-flex;
align-items: center;
background: #ffffff;
color: #374151;
padding: 6px 12px;
border: 1px solid #d1d5db;
border-radius: 4px;
font-weight: 500;
font-size: 13px;
text-decoration: none;
transition: all 0.2s ease;
min-height: 28px;
cursor: pointer;
}
.dashboard-button:hover {
background: #f9fafb;
border-color: #9ca3af;
}
.request-button {
display: inline-flex;
align-items: center;
background: #3b82f6;
color: #ffffff;
padding: 6px 12px;
border: none;
border-radius: 4px;
font-weight: 500;
font-size: 13px;
text-decoration: none;
transition: all 0.2s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
min-height: 28px;
}
.request-button:hover {
background: #2563eb;
}
@media (max-width: 640px) {
.navbar {
padding: 12px 16px;
}
.logo {
height: 28px;
}
.main-content {
padding: 16px;
}
.modal-header {
padding: 20px 20px 16px 20px;
}
.modal-body {
padding: 20px;
}
.modal-footer {
padding: 16px 20px 20px 20px;
flex-direction: column;
}
.error-title {
font-size: 16px;
}
.info-item {
grid-template-columns: 1fr;
gap: 4px;
}
.info-label {
font-size: 13px;
}
}
</style>
</head>
<body>
<nav class="navbar">
<img src="https://media.holistics.io/ef25c515-holistics-logo-colored.svg" alt="Holistics" class="logo">
</nav>
<div class="main-content">
<div class="container">
<!-- Modal Header -->
<div class="modal-header">
<h1 class="error-title">403 - Access Forbidden</h1>
<p class="error-message">
You don't have permission to access this resource. Please request access to continue.
</p>
</div>
<!-- Modal Body -->
<div class="modal-body">
<div class="info-section">
<h2 class="info-title">Request Details</h2>
<div class="info-grid">
<div class="info-item">
<span class="info-label">User:</span>
<span class="info-value">{{USER_NAME}}</span>
</div>
<div class="info-item">
<span class="info-label">Email:</span>
<span class="info-value">{{USER_EMAIL}}</span>
</div>
<div class="info-item">
<span class="info-label">Company:</span>
<span class="info-value">{{TENANT_NAME}}</span>
</div>
<div class="info-item">
<span class="info-label">Path:</span>
<span class="info-value">{{CURRENT_PATH}}</span>
</div>
<div class="info-item">
<span class="info-label">Host:</span>
<span class="info-value">{{CURRENT_HOST}}</span>
</div>
</div>
</div>
</div>
<!-- Modal Footer -->
<div class="modal-footer">
<a href="/dashboards" target="_top" class="dashboard-button">
Back to Dashboard
</a>
<a href="" target="_top" class="request-button">
Request Access
</a>
</div>
</div>
</div>
</body>
</html>

Remember to replace the link in the Request Access button with the link to your form
<a href="link-to-your-form" target="_top" class="request-button">
Request Access
</a>
Template 2: Connecting to Form Services
Want to pre-fill the user's info in your form? Here's how to set that up with different services:
Google Forms
<!-- Create your form with these fields: Email, Name, Dashboard URL, Reason -->
<!-- Get the prefilled link from Forms > More > Get pre-filled link -->
<a href="https://forms.gle/YOUR_FORM_ID?entry.emailFieldId={{ USER_EMAIL }}&entry.nameFieldId={{ USER_NAME }}&entry.urlFieldId={{ CURRENT_URL }}"
target="_top"
style="background: #4285f4; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">
Request via Google Forms
</a>
Jotform
<!-- Jotform accepts URL parameters for field prefilling -->
<a href="https://form.jotform.com/YOUR_FORM_ID?email={{ USER_EMAIL }}&name={{ USER_NAME }}&dashboard={{ CURRENT_PATH }}"
target="_top"
style="background: #FF6100; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">
Request via Jotform
</a>
Microsoft Forms
<!-- For Office 365 users -->
<a href="https://forms.office.com/r/YOUR_FORM_ID?email={{ USER_EMAIL }}&name={{ USER_NAME }}"
target="_top"
style="background: #0078d4; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">
Request via Microsoft Forms
</a>
Typeform
<!-- Typeform uses # for hidden fields -->
<a href="https://yourcompany.typeform.com/to/YOUR_FORM_ID#email={{ USER_EMAIL }}&name={{ USER_NAME }}&dashboard={{ CURRENT_URL }}"
target="_top"
style="background: #262627; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">
Request via Typeform
</a>
Variables You Can Use
These automatically fill in with the user's actual info:
Variable | Description | Example Output |
---|---|---|
{{ USER_NAME }} | User's display name | "Jane Smith" |
{{ USER_EMAIL }} | User's email | "[email protected]" |
{{ TENANT_NAME }} | Your organization | "Acme Corp" |
{{ CURRENT_PATH }} | Dashboard path | "/dashboards/sales-metrics" |
{{ CURRENT_URL }} | Full URL attempted | "https://app.holistics.io/dashboards/123" |
{{ CURRENT_HOST }} | Domain name | "app.holistics.io" |
Heads up: URL Encoding
Some form services get confused by special characters in URLs. If your form isn't pre-filling correctly, try URL encoding:
<!-- Some form services need encoded URLs -->
<a href="https://forms.gle/abc123?dashboard={{ CURRENT_URL | urlencode }}" target="_top">
Request Access
</a>
What Works (and What Doesn't)
You can use:
- HTML and CSS (inline or in
<style>
tags) - Links to anywhere (just add
target="_top"
) - All the variables listed above
You can't use:
- External JavaScript sources or inline scripts (security limitations)
- External CSS links or files
- Form inputs or buttons (links only)
Important: All links must include target="_top"
to navigate properly:
<!-- Correct -->
<a href="https://forms.gle/abc123" target="_top">Request Access</a>
<!-- Won't work -->
<a href="https://forms.gle/abc123">Request Access</a>