What It Is
Service Portal is ServiceNow's Angular-based framework for building custom self-service interfaces that exist completely outside the standard ServiceNow UI. Unlike the platform's native forms and lists, Service Portal gives you pixel-perfect control over user experience through a widget-based architecture where each component can contain custom HTML, CSS, client scripts, and server scripts. It solves the fundamental problem of providing external users — customers, vendors, employees who don't need platform access — with branded, mobile-responsive interfaces that feel like modern web applications rather than enterprise software. Each portal runs as a separate web application with its own URL suffix (like /sp or /csp), complete with its own navigation, branding, and user authentication flow.
Architecturally, Service Portal lives in the Service Portal application and operates as a presentation layer that sits above ServiceNow's standard data and business logic layers. The framework consists of four main record types: Portals (sp_portal) define the overall site configuration, Pages (sp_page) define individual URLs and their layout, Widgets (sp_widget) contain the actual functionality and UI components, and Instances (sp_instance) represent specific configurations of widgets placed on pages. This hierarchical structure allows for massive reusability — a single widget can appear across multiple pages and portals with different configurations.
The execution environment runs entirely server-side through Rhino JavaScript for server scripts and client-side through Angular 1.x for client scripts and templates. Widget server scripts execute with the same session context as the logged-in user, giving you full access to GlideRecord, GlideSystem, and all standard ServiceNow APIs. Client scripts run in the browser and communicate back to server scripts via the built-in $http service or custom GlideAjax calls. The framework handles all the Angular bootstrapping, dependency injection, and routing automatically — you just write the business logic. Data binding works exactly like standard Angular, with two-way binding between your client controller and the HTML template.
You cannot function without Service Portal in three specific scenarios: when you need external users to interact with ServiceNow data without platform licenses, when you need mobile-responsive interfaces that work seamlessly across devices, and when business requirements demand complete control over user experience and branding. The Employee Service Center, Customer Service Management portals, and any B2B vendor portals all depend entirely on this framework. Standard ServiceNow UI simply cannot provide the modern, consumer-grade experience that external users expect, nor can it handle the performance requirements of high-traffic public-facing sites. Even internal employee portals often require Service Portal because the standard UI's complexity and feature density overwhelms non-technical users who just need to submit requests or check status.
Service Portal management splits across multiple roles and skill sets. Platform administrators handle portal-level configuration, user access, and integration with authentication systems and knowledge bases. Developers build and maintain custom widgets, write the server and client scripts, and implement complex business logic that bridges portal functionality with backend ServiceNow processes. UX designers control the CSS frameworks, page layouts, and overall user experience design. Unlike most ServiceNow features where admins can handle everything through configuration, Service Portal requires actual development skills — HTML, CSS, JavaScript, and Angular — to create anything beyond basic functionality. The person managing your Service Portal needs to understand both ServiceNow's data model and modern web development practices.
Recent ServiceNow releases have largely shifted focus to Next Experience as the future of custom user interfaces, making Service Portal a legacy framework that's maintained but not enhanced. Vancouver and Xanadu haven't introduced major Service Portal changes, instead pushing new capabilities into Workspaces and Next Experience components. The practical impact is that while existing Service Portal implementations continue working perfectly, new projects should carefully evaluate whether Service Portal's extensive customization capabilities outweigh the long-term benefits of adopting Next Experience. For complex, highly-customized external portals, Service Portal remains the only viable option, but for internal employee experiences, Next Experience increasingly provides better integration with the platform's future direction.
Where to Find and Configure It
Primary configuration lives at Service Portal > Service Portal Configuration where you manage portals, pages, widgets, and all the core framework components. The Service Portal > Portals link takes you directly to the portal list (sp_portal table) where you create new portals, set URL suffixes, configure themes, and define homepage settings. Access the Widget Editor through Service Portal > Widgets for building and modifying widget functionality.
Development work happens in Studio under System Applications > Studio where you can create scoped widgets and portal components that belong to specific applications. Navigate to System Definition > Tables and filter for tables starting with sp_ to see all Service Portal data structures. The Page Designer interface is accessible through any portal by appending ?id=page_designer to the URL, letting you drag-and-drop widgets onto pages and configure them visually. Access control configuration lives under User Administration > Access Control (ACL) where portal-specific ACLs control what data portal users can access.
See Service Portal in action by visiting any active portal URL (typically https://instance.servicenow.com/sp), or check Service Portal > Service Portal Configuration > Portal Suffixes for a complete list of available portals. Widget instances and their configurations are visible in Service Portal > Widget Instances (sp_instance table). Scoped applications can contain their own portal components, but global portal configuration always takes precedence — scoped widgets can be added to global portals, but scoped portals cannot override global portal settings or security configurations.
How It Works Step by Step
Service Portal operates through a request-response cycle that begins when a user accesses a portal URL. The framework first identifies which portal matches the URL suffix, then determines which page to display based on the URL path and the portal's homepage configuration. Once the target page is identified, ServiceNow loads all widget instances configured for that page, executes their server scripts to gather data, then renders the Angular application with the populated widget templates and client scripts.
The execution model follows a strict server-first approach where widget server scripts run completely before any client-side code executes. Server scripts have full access to the ServiceNow database and APIs, populating the data object that becomes available to client scripts and templates. Client scripts then run in the browser with access to this pre-loaded data, the Angular framework, and browser APIs, but they must use AJAX calls to communicate back to the server. This separation ensures that sensitive business logic and data access stays server-side while providing rich interactivity on the client.
Portal-specific caching and inheritance behavior significantly impacts performance and functionality. Widget server scripts are cached based on user session and widget configuration, meaning changes to server scripts require cache clearing or user session refresh to take effect. Page layouts inherit from portal themes, which cascade CSS and JavaScript includes down to individual widgets. The framework maintains a widget dependency tree to ensure that shared widgets (like headers and footers) load before page-specific widgets, and it automatically handles Angular dependency injection for custom services and directives defined in widget client scripts.
Enjoying this? Get one deep-dive per week.
Join 1,000+ ServiceNow pros — scripts, GlideRecord patterns, Flow Designer techniques, and career moves. Free.
The Execution Order
- User requests portal URL, ServiceNow matches URL suffix to portal record and identifies target page
- Portal theme CSS and JavaScript files are loaded, establishing base styles and dependencies
- Page layout containers are identified and widget instances are loaded for each container
- Widget server scripts execute in dependency order, populating data objects with database queries and business logic results
- Widget HTML templates render with server data, generating the initial DOM structure
- Angular application bootstraps, executing widget client scripts and establishing data binding
- Client scripts set up event handlers, watchers, and any ongoing client-side behavior
- Portal becomes interactive, with subsequent user actions triggering client script functions or AJAX calls back to server scripts
// Server script - executes before page render
(function() {
// Query data for the widget
var gr = new GlideRecord('incident');
gr.addQuery('assigned_to', gs.getUserID());
gr.addQuery('state', '!=', 7); // Not closed
gr.orderByDesc('sys_updated_on');
gr.setLimit(10);
gr.query();
data.incidents = [];
while (gr.next()) {
data.incidents.push({
number: gr.getValue('number'),
short_description: gr.getValue('short_description'),
state: gr.getDisplayValue('state'),
sys_id: gr.getValue('sys_id')
});
}
data.user_name = gs.getUser().getDisplayName();
})();Real-World Scenarios
Building a Customer Support Portal with Case Submission
Your organization needs external customers to submit support cases without requiring ServiceNow licenses, while maintaining complete control over branding and user experience. The portal must authenticate customers against an external system and pre-populate case forms with customer data.
Create a new portal at Service Portal > Portals with URL suffix customer and enable Enable ID provider for external authentication. Configure the homepage to use a custom page containing a case submission widget. Create a new widget called Customer Case Form with server script that queries customer data based on the logged-in user's email, then populates form fields automatically. Set up ACLs on the sn_customerservice_case table to allow create access for users with the sn_customerservice.customer role.
Watch for session timeout issues with external authentication providers — implement automatic token refresh in your widget client scripts. The customer data lookup in your server script needs error handling for cases where the customer record doesn't exist or has incomplete data. Test thoroughly with users who have multiple customer accounts, as ServiceNow's user impersonation can behave unexpectedly when the same email address exists in multiple customer records.
Creating Role-Based Widget Visibility for Employee Portal
Your employee portal needs different widgets visible to different user groups — managers see approval widgets, HR sees employee management tools, and regular employees only see basic self-service options. The visibility must be dynamic and based on ServiceNow roles, not static page configurations.
// Widget server script for conditional visibility
(function() {
data.show_widget = false;
data.user_roles = [];
// Get user's roles
var roleGR = new GlideRecord('sys_user_has_role');
roleGR.addQuery('user', gs.getUserID());
roleGR.query();
while (roleGR.next()) {
var roleName = roleGR.role.getRefRecord().getValue('name');
data.user_roles.push(roleName);
}
// Define required roles for this widget
var requiredRoles = ['manager', 'hr_admin'];
// Check if user has any required role
for (var i = 0; i < requiredRoles.length; i++) {
if (data.user_roles.indexOf(requiredRoles[i]) > -1) {
data.show_widget = true;
break;
}
}
})();Role inheritance in ServiceNow can cause widgets to appear for users who shouldn't see them — always test with actual user accounts, not administrator impersonation. Consider caching role checks in the user's session rather than querying on every widget load, as role lookups can significantly impact portal performance with many concurrent users. Set up widget-level ACLs as a backup security measure, since client-side visibility controls can be bypassed by determined users viewing page source or manipulating browser developer tools.
Implementing Multi-Step Form Wizard with Data Persistence
Business users need to complete complex onboarding forms that span multiple pages with dozens of fields, but they must be able to save progress and return later without losing data. The form wizard must validate data at each step and prevent users from advancing until required fields are completed.
// Client script for form wizard navigation
function($scope, $http) {
var c = this;
c.currentStep = 1;
c.totalSteps = 4;
c.formData = c.data.saved_data || {};
c.nextStep = function() {
if (c.validateCurrentStep()) {
c.saveProgress();
c.currentStep++;
}
};
c.validateCurrentStep = function() {
var required = c.getRequiredFields(c.currentStep);
for (var i = 0; i < required.length; i++) {
if (!c.formData[required[i]]) {
c.showError('Please complete all required fields');
return false;
}
}
return true;
};
c.saveProgress = function() {
$http.post('/api/now/sp/widget/' + c.data.widget_id + '/save_draft', {
step: c.currentStep,
data: c.formData,
user: c.data.user_id
});
};
}Browser session storage alone isn't reliable for long-term draft persistence — users may switch devices or clear browser data. Store draft data in a custom ServiceNow table with appropriate cleanup policies to remove abandoned drafts after a set timeframe. Handle browser back/forward button navigation explicitly in your client script, as Angular's routing can break when users navigate away from your wizard and return. Consider implementing auto-save functionality that triggers every few seconds or on field blur events, but throttle these saves to avoid overwhelming the server with unnecessary requests.
The Classic Mistake
Creating custom widgets that make direct server calls without proper session and security context validation.
// BAD: Direct server calls without proper context
function loadUserData() {
var ga = new GlideAjax('MyCustomAjaxProcessor');
ga.addParam('sysparm_name', 'getUserInfo');
ga.addParam('user_id', c.user.userID);
ga.getXML(function(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
c.data.userInfo = JSON.parse(answer);
c.server.update();
});
}
// Called directly on widget load
loadUserData();This fails because Service Portal widgets run in a different session context than the main ServiceNow interface, and direct GlideAjax calls bypass the portal's security model. Users see blank widgets or permission errors, while the server logs show access denied messages because the ajax processor can't validate the portal session. ServiceNow internally treats these as separate security contexts, but this isn't obvious because regular platform GlideAjax works fine. The portal framework expects all server communication to flow through the widget's server script using c.server.get() or spUtil.get() methods.
// GOOD: Use portal's built-in server communication
function loadUserData() {
var requestData = {
action: 'getUserInfo',
user_id: c.user.userID
};
c.server.get(requestData).then(function(response) {
c.data.userInfo = response.data.userInfo;
});
}
// Server Script handles the actual data retrieval
if (input && input.action == 'getUserInfo') {
var gr = new GlideRecord('sys_user');
if (gr.get(input.user_id)) {
data.userInfo = {
name: gr.getDisplayValue('name'),
email: gr.getDisplayValue('email')
};
}
}Never make direct GlideAjax calls from portal widgets — always use c.server.get() or spUtil.get() to maintain proper portal session context and security.
When to Use This vs Alternatives
Service Portal is the right choice when you need a fully customizable self-service experience with complex business logic, custom branding, and deep integration with ServiceNow workflows. It excels when users need guided experiences that span multiple forms, approvals, and knowledge articles in a single cohesive interface.
Choose Service Portal When
You need custom Angular widgets with complex client-side logic, multi-step wizards, or heavily branded experiences that don't fit standard ServiceNow UI patterns. Employee Center and Next Experience can't match Service Portal's flexibility for custom business processes like multi-department approval workflows or integration dashboards. Service Portal also wins when you need fine-grained control over responsive design, custom CSS frameworks, or JavaScript libraries that newer frameworks don't support.
Use Next Experience Instead When
You want modern UI components, better performance, and ServiceNow's latest design patterns without heavy customization. Next Experience handles standard self-service scenarios (catalog requests, incident reporting, knowledge search) more efficiently than Service Portal. Choose Next Experience for new implementations unless you specifically need Service Portal's custom widget capabilities or have existing Service Portal investments to protect.
Use Both Together When
You're migrating from Service Portal to Next Experience but need to maintain custom widgets during the transition. Run Service Portal for specialized functions (custom dashboards, legacy integrations) while directing standard self-service traffic to Next Experience. This hybrid approach lets you modernize incrementally while preserving business-critical customizations that can't be easily replicated.
Platform Interactions & Side Effects
- ACLs require
snc_internalorpublicroles for portal users to access records, bypassing normal role-based security model - Widget instances create records in
sp_instancetable, storing JSON configuration data that doesn't migrate cleanly in Update Sets - Business Rules trigger normally from widget server scripts, but
gs.getUser()returns portal user context, not the authenticated user - Portal sessions write to separate
sysevent_in_emailand notification queues, causing delivery delays compared to platform notifications - CSS and JavaScript dependencies get cached aggressively by
glide.ui.escape_all_scriptsystem property, breaking widget updates until cache clear - Portal homepage performance degrades exponentially with widget count due to individual Angular digest cycles on each widget
- Knowledge Base articles require
publiccan_read ACL onkb_knowledgetable for portal display, exposing internal articles unintentionally - Catalog item variables render through
sp_variable_layoutrecords, ignoring platform form designer configurations and client scripts - Widget server script errors don't appear in standard System Log, only in browser developer console and
sp_logtable - Portal theming overrides global UI16 CSS, affecting platform forms accessed through portal context until session expires
Debugging and Troubleshooting
The most common failure symptoms include widgets showing as blank rectangles, infinite loading spinners, or JavaScript console errors about undefined Angular modules. Users typically see partial page loads where some widgets render correctly while others fail silently. Admins notice missing data in widgets that worked in development but fail in production, often due to ACL restrictions or session context issues.
Check the browser's developer console first for client-side Angular errors, then examine the sp_log table for server-side widget script errors. Enable portal debugging by setting glide.service_portal.debug system property to true, which adds detailed timing information to the sp_log table. Monitor the Application Log > Service Portal for authentication and authorization failures.
Look for specific error messages like "Access denied" in sp_log, "Module not found" JavaScript errors for missing dependencies, and "Cannot read property of undefined" errors indicating data structure problems in widget server scripts. Portal session timeout errors appear as "Invalid session" messages in the browser network tab, while widget instance configuration problems show as "Widget not found" entries in the portal log.
Diagnostic Checklist:
- Verify portal user has
snc_internalor appropriate role inSystem Security > Users and Groups - Check ACLs on target tables have
publicrole read access inSystem Security > Access Control - Clear portal cache via
System Web Services > Service Portal > Portal Suffixrecord - Test widget in isolation using
/$portal.do?sysparm_widget=widget_idURL parameter - Review widget dependencies in
Service Portal > Widgetsfor missing CSS/JS includes - Enable
glide.service_portal.debugsystem property and checksp_logtable - Validate portal session hasn't expired by checking network tab for 401 authentication errors
Quick Reference
- Widget server scripts run in scoped application context but can access global tables without prefix using
global.namespace - Portal pages support maximum 50 widget instances before performance degrades significantly due to Angular digest limits
- CSS files in widget dependencies must be under 1MB and load synchronously, blocking portal rendering until complete
- Portal search indexes only
kb_knowledgeandsc_cat_itemtables by default, requiring custom search sources for other content - Widget option schema validation fails silently, storing invalid JSON in
sp_instance.optionsfield without error messages - Portal theming CSS variables don't inherit from parent themes, requiring complete redefinition for child portal themes
- Anonymous portal access requires
glide.authenticate.sso.redirect.idpsystem property set to empty string to prevent SSO redirects - Widget client controller functions execute before server data loads, requiring
$scope.$watchfor data-dependent initialization - Portal breadcrumbs stored in
sp_portal_pagerecords don't update automatically when page titles change insp_pagetable - Portal URL parameters containing dots get truncated due to Apache mod_rewrite rules, breaking page routing for complex parameters