ServiceNow Integration Setup

Prerequisites

  • A ServiceNow instance (e.g., acme.service-now.com) and a user with the admin role to register an OAuth application
  • A ServiceNow user to authorize the connection whose roles allow reading the sys_user table (an admin, or a user with user_admin), with an email on your organization's domain
  • ZLH running with a reachable callback URL

Step 1: Register an OAuth Application in ServiceNow

  1. In the filter navigator, open System OAuth > Application Registry
  2. Click New, then Create an OAuth API endpoint for external clients
  3. Fill in:
    • Name: ZombieLicenseHunter
    • Redirect URL: {YOUR_ZLH_BASE_URL}/integrations/servicenow/callback
    • Client Secret: leave blank and ServiceNow generates one
    • Leave the token lifespans at their defaults; ZLH refreshes the access token automatically
  4. Submit, then reopen the record and add both scopes in the OAuth Entity Scopes related list:
    • useraccount — required for the authorization flow
    • Table API — required for reading sys_user over REST; without it the connection succeeds but every sync fails
  5. Copy the Client ID and the Client Secret (click the lock icon on the field to reveal it)

Step 2: ZLH-Side Values

  • The Redirect URL registered in Step 1 must be exactly {YOUR_ZLH_BASE_URL}/integrations/servicenow/callback — protocol, host, port and path
  • Scope requested: useraccount. Self-hosted deployments can override it with SERVICENOW_SCOPES; the scopes on the Application Registry record must cover whatever is requested
  • The Client ID and Client Secret are entered per integration in the UI, not in environment variables

Step 3: Connect via the ZLH UI

  1. In ZLH, navigate to Integrations > Add Integration
  2. Select ServiceNow
  3. Enter the Client ID and Client Secret from Step 1
  4. Enter your ServiceNow Instance as a full URL, e.g. https://acme.service-now.com — a single .service-now.com hostname with no path or port
  5. Click Connect — you are redirected to your instance to sign in and approve the application
  6. Approve the consent prompt; you are redirected back to ZLH with the integration active and a first sync queued

The connection and every sync run as the ServiceNow user who approved the prompt.

What Gets Synced

ZLH reads the sys_user table through the Table API, 100 records per page, and imports only records with active=true. For each user it captures:

  • Email and the ServiceNow sys_id (used as the stable identifier)
  • Roles, stored as the user's license types (one entry per role)
  • Last login time

Not synced: deactivated users, groups, CMDB or asset data. Display names are not stored.

Troubleshooting

Issue Fix
"invalid instance: host must be under service-now.com" when saving Enter the instance as https://<name>.service-now.com. Custom domains, paths, ports and query strings are rejected
ServiceNow shows an error page instead of the sign-in prompt The Redirect URL on the Application Registry record does not match {YOUR_ZLH_BASE_URL}/integrations/servicenow/callback exactly
"Authorization failed: ..." after ServiceNow redirects back ServiceNow refused the request. Check the Redirect URL and that useraccount is in the OAuth Entity Scopes, then retry
"Failed to complete OAuth handshake. Please check your credentials and try again." The Client ID or Client Secret does not match the Application Registry record. Re-copy the secret (lock icon) and retry
"Access denied: you can only connect accounts from your organization's domain" The ServiceNow user who approved the prompt has an email on a different domain from your ZLH login. Reconnect with a user on your domain
Sync fails with servicenow: unexpected status 403 The token has no REST access. Add the Table API scope to the OAuth application, then click Reconnect on the integration's card on the ZLH home page
Sync fails with servicenow: unexpected status 401 The refresh token expired or was revoked (see Refresh Token Lifespan on the record). Click Reconnect on the integration's card on the ZLH home page
Users are missing Only records with active=true are imported. Deactivated users never appear
License types are empty for a user The user's roles field is empty in ServiceNow. ZLH stores the roles exactly as ServiceNow reports them