REST API 403: Forbidden — Field-Level ACL Diagnosis GuideSummary<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Integration user authenticates successfully but API response returns empty fields or HTTP 403 — how to distinguish table-level blocks from field-level ACL silencing, use Debug Security Rules and Access Analyzer to identify the blocking rule, and apply the canRead() background script technique. Release<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } All Instructions<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Primary symptom: The integration user authenticates successfully (no 401) and the Table API returns HTTP 200 OK — but one or more fields in the response are empty (""), null, or absent. Alternatively the call returns HTTP 403 with "detail": "Failed API level ACL Validation". ServiceNow ACLs operate at two independent levels — table and field. A 403 HTTP error usually indicates a table-level block or a missing API access role. Empty fields on a 200 response typically indicate a field-level ACL silently blocking read access for the integration user's role set. 401 vs. 403 — The Critical Distinction Attribute401 Unauthorized403 ForbiddenHTTP status401403 (or silent empty fields on 200)Root causeAuthentication failure — wrong credentials, expired token, OAuth token URL wrong after instance migrationAuthorization failure — user is known but lacks permission on the resourceResponse detail"User Not Authenticated" or OAuth error"Failed API level ACL Validation" / "User Not Authorized", or field is silently empty on HTTP 200First diagnostic stepCheck credentials, Application Registry, OAuth token URL — see KB0546297Check user roles; run Debug Security Rules or Access Analyzer (Steps 2–3) If unsure which error type: Enable Debug Security Rules and retry the failing call. The output will show explicitly whether ACL evaluation passed or failed. If no ACL failure appears, redirect to the 401 path. Table-Level ACL vs. Field-Level ACL ACL TypeObject ExampleResult if DeniedTable ACLincident, sc_requestHTTP 403 with "Failed API level ACL Validation". Entire request blocked — no records returned.Field ACLincident.work_notes, incident.close_notesHTTP 200 OK — record returns normally but restricted fields are empty strings or absent. No HTTP error raised.Row-level ACLincident with condition scriptHTTP 200 OK — query returns zero records even when matching records exist. Step 1 — Confirm Roles & Application Access 1. Reproduce the failing call as the integration user To use REST API Explorer, the integration user must have the rest_api_explorer role (grants access to the Explorer UI). On Vancouver and later instances, web_service_admin is also required — a post-Vancouver security patch added a "Doc Service API" ACL that blocks the Explorer's dropdowns from populating without it.Two ways to reproduce as the integration user: Impersonate in the Explorer: As an admin, impersonate the integration user first, then navigate to REST API Explorer. The call will execute under the integration user's role set.External client (Postman, curl): Supply the integration user's credentials directly. This is the most accurate reproduction since it bypasses any Explorer-specific role requirements. Important caveat: If the table has "Allow access to this table via web services" disabled, the REST API Explorer will display a warning rather than opening the Table API for that table — so the setting is surfaced visually, not silently ignored. However, a warning in the Explorer is not the same as a 403 in production. Always confirm findings with an external client (Postman or curl) under the integration user's credentials, since that replicates exactly what the production integration call will receive.Record the HTTP status code and which fields are empty or absent in the response. HTTP 403 → table-level or API role issue. HTTP 200 with missing fields → field-level ACL. System Web Services → REST → REST API Explorer 2. Check the integration user's roles Navigate to the user record → Roles tab. Key roles to verify: snc_platform_rest_api_access — required for Table API, Attachment API, Import Set API, and Aggregate API when the REST API ACL is activated. This role replaced the deprecated rest_service role (deprecated from Kingston onwards).itil — broad read/write on incident, problem, change tables; inherits snc_platform_rest_api_access. If the user has zero roles, the response will be HTTP 403 at table level — assign snc_platform_rest_api_access and the appropriate table role, then retest. User Administration → Users → [integration user] → Roles tab 3. Check the table's Application Access settings On scoped application tables, the 403 may come from Application Access rather than ACLs. Confirm: Accessible from = "All application scopes" and Allow access to this table via web services is checked. Both must be correct regardless of the user's roles. System Definition → Tables → [table name] → Application Access tab Step 2 — Debug Security Rules Debug Security Rules is a built-in ServiceNow tool that places debug icons on each field of a form and outputs a detailed ACL evaluation log for the current session. It shows which ACL rules passed or failed by name. Debug Security Rules evaluates ACLs for the currently logged-in user. To diagnose the integration user's access, impersonate them after enabling debugging — or use Access Analyzer, which evaluates any user's access without impersonation. 1. Enable Debug Security Rules Navigate to System Security → Debugging → Debug Security Rules and click the module to enable it for your session. Alternatively append &sysparm_debug=security to any instance URL. System Security → Debugging → Debug Security Rules 2. Impersonate the integration user Profile icon → Impersonate User → enter the integration user's name. Then open a record on the affected table. 3. Read the debug output Debug icons appear next to each field. Click any icon to see ACL evaluation results. Output format:ACL: incident.work_notes (read) — Failed (false)The rule name (e.g., incident.work_notes) is what you need for Step 5. Step 3 — Access Analyzer Access Analyzer is a dedicated ServiceNow diagnostic application that evaluates, compares, and simulates user access. It is the cleanest way to identify which ACL is blocking a specific user without needing to impersonate them. Availability: Free app from the ServiceNow Store. Reference: KB2291441. 1. Navigate to Access Analyzer → Evaluate Access System Security → Access Analyzer → Evaluate Access 2. Set the user and resource Select the integration user. Set the resource to the affected table (e.g., incident) and optionally a specific record sys_id. Click Analyze permissions. 3. Review results Results show each operation (read, write, create, delete) as Passed, Blocked, Skipped, or Undefined. Click any blocked operation to see which ACL rules fired, the required roles, conditions, and scripts. Results are exportable for sharing with customer admins. When to use which: Use Access Analyzer when you cannot impersonate the integration user or need exportable output. Use Debug Security Rules for quick inline inspection during a live screenshare. Step 4 — canRead() Background Script When a programmatic confirmation is needed — or when the tools above are unavailable — use GlideRecord canRead() and canWrite() in Scripts-Background to enumerate exactly which fields are blocked. Admin access required. Scripts-Background requires the admin role. Walk the customer through this or run it during a screenshare. Use below script of check 403 for Inbound API to ServiceNow. Targeted Field Check ************************// Replace <values> before running // System Definition → Scripts-Background // sys_id of the integration user to test as var targetUserSysId = '<SYS_ID_OF_INTEGRATION_USER>'; var originalUserId = gs.getSession().impersonate(targetUserSysId); gs.print('Running as: ' + gs.getUserName()); var gr = new GlideRecord('incident'); gr.get('<VALID_RECORD_SYS_ID>'); // Step 1 — check record-level read access first. // If false, this is a table-level ACL block — field checks below are not meaningful. var res = gr.canRead(); gs.print('Can Read record ' + gr.number + ': ' + res); // Step 2 — only proceed to field-level checks if record is readable var fields = ['number', 'short_description', 'work_notes', 'close_notes', 'caller_id', 'description', 'assignment_group', 'state']; for (var i = 0; i < fields.length; i++) { var f = fields[i]; var result = gr.isValidField(f) ? gr[f].canRead() : 'FIELD_NOT_FOUND'; gs.print('canRead(' + f + '): ' + result); } // De-impersonate — pass back the original user's sys_id returned above gs.getSession().impersonate(originalUserId); gs.print('Reverted to: ' + gs.getUserName());*****************Expected output — two stages:Stage 1 — record-level:Can Read record INC0012345: true → record is accessible; proceed to field checks.Can Read record INC0012345: false → table-level ACL block. Stop here — field results are not meaningful. Fix the table ACL or missing role first.Stage 2 — field-level (only meaningful if Stage 1 = true):canRead(work_notes): false → field ACL blocking this field.canRead(short_description): true → field accessible normally.Take any false field names from Stage 2 into Step 5 to locate the blocking ACL rule. Step 5 — Locate the Blocking ACL Rule 1. Filter Access Control (ACL) by field name Filter: Name contains [table].[field] (e.g., incident.work_notes), Operation = read, Active = true. System Security → Access Control (ACL) 2. Examine the ACL record Open the matching ACL. Check: Roles tab — roles listed are granted access. If the user's roles are absent, access is denied.Condition — must evaluate true for access to be granted.Script (Advanced) — if present, must return true. A failing script overrides role grants.Admin bypass — admin and security_admin users bypass ACLs. If an admin is also blocked, escalate immediately. 3. Document the role gap Roles on the ACL's Roles tab but absent from the user are the gap. Record the exact role name to provide to the customer admin.