Skip to main content

About ServiceNow

ServiceNow is an IT service management (ITSM) platform that manages tickets, incidents, change requests, problems, and the service catalog. Connecting ServiceNow to Serval enables native bidirectional ticket sync, service-catalog import and ordering, knowledge-base sync (with optional mirroring of articles into Serval’s native knowledge base), and workflow automation across incidents, problems, changes, and HR cases. Serval connects to a single ServiceNow instance using either OAuth 2.0 (client credentials) - the default and recommended option - or HTTP Basic authentication, and acts as a dedicated integration service account. Authentication: OAuth 2.0 (client credentials), or Basic auth Data sync: Bidirectional. Serval polls ServiceNow for ticket and comment updates (with an optional Business Rule webhook for near-real-time comment sync), keeps groups, roles, departments, companies, locations, and cost centers current with scheduled background syncs, can mirror synced knowledge articles into a read-only native Serval knowledge base, and writes back via the Table, Service Catalog, and Change Management REST APIs on demand when workflows run. Sync frequency is admin-configurable per sync.

What the ServiceNow integration enables

CapabilityDescription
Ticket syncNative bidirectional sync between Serval tickets and ServiceNow incidents, catalog requested items, and HR cases - including comments and work notes. Serval formatting is converted to clean plain text on the way in.
Service catalog importImport ServiceNow catalog items so employees can order them through Serval - as a confirmed form, a pure conversation, or a link back to ServiceNow - with reference data, access rules, and source-system popularity carried over.
Improve with CatalystHand any imported catalog item to Catalyst, which redesigns the ITSM form as a conversational Serval experience and submits the request back to ServiceNow.
Knowledge baseSync ServiceNow knowledge articles into Serval so the AI agent can answer from your documented guidance - and optionally mirror them into a read-only native Serval knowledge base.
Workflow automationBuild workflows that create and update incidents, problems, and change requests, including approvals and HR cases.
Access profiles and resource syncSync ServiceNow groups, roles, departments, companies, locations, and cost centers, and provision or deprovision group and role membership for access control.
User criteria importImport ServiceNow user criteria as Serval access profiles so the same people who can request an item in ServiceNow can request it in Serval.
Configurable sync settingsSet per-sync frequency (hourly through weekly, or automatic) and an optional ServiceNow encoded-query filter that scopes which catalog items are imported.
Real-time comment sync (optional)A single ServiceNow Business Rule pushes new comments to Serval for near-instant delivery instead of waiting for the next poll.
Live agent handoff (optional)Hand a conversation off to a human ServiceNow agent through Virtual Agent Bot-to-Bot and bridge the agent’s replies back into the Serval ticket.
Anything exposed by ServiceNow’s REST APIs can be accessed through Serval (see the ServiceNow product documentation).

Catalog import

Each catalog item is imported in one of three modes, chosen with the Collect inputs as control (and the split Import button) on the catalog import page:
ModeWhat the employee experiences
Form”The agent shows a form to confirm the fields before submitting.”
Chat”The agent gathers what’s needed conversationally, then submits - no form.”
Link”The agent sends a link to complete the request in the external system.”
Items Serval cannot faithfully render fall back to link-only automatically, with the reason shown - client scripts, scripted UI policies, UI scripts, unsupported required field types, or dynamic JavaScript reference qualifiers. Per-item controls let you toggle form confirmation (Form vs. Chat), set Force link-only mode (always redirect employees to the ServiceNow form), unimport, or disable an item. Forms with reference fields - lookups into ServiceNow tables - prompt a Set up reference data step. Confirming Set up reference data creates entity ingestion configs for those tables so pickers resolve real options; use Re-sync to refresh them later. Until reference data is set up, the item shows a Reference data not set up state. While browsing, catalog items appear as structured cards with field counts and a Ready status, a category filter, and a Most requested sort backed by source-system popularity (“Ordered N times in the source system”). Each item’s Understanding tab shows everything Serval captured from ServiceNow - variables, client scripts, UI policies (rendered as When/Show/Hide/Require/Lock rules, with scripted ones flagged), script includes, and the user criteria that gate access - along with an AI second opinion on whether the item can be filled conversationally. If a sync gets stuck, it can be cancelled or restarted with Sync now; stale syncs are detected automatically.

Improve with Catalyst

Every imported catalog item has an Improve with Catalyst action. It launches a Catalyst session pre-loaded with the item’s full captured understanding - variables, client scripts, UI policies, script includes, user criteria, and the imported workflow as a starting point - plus a built-in skill that reimagines the ITSM form as a conversational Serval experience: one orchestrating skill that asks only what the employee must actually decide, small collection workflows per branch, and a submit workflow that posts the request back to ServiceNow and links the resulting requested item to the ticket. There are two entry points: for natively importable items (“Turn this form into a conversation”), and for items too complex to import, where Catalyst redesigns around the client scripts and UI policies that block native import. Resources Catalyst builds stay attached to the catalog item under a Built with Catalyst list (with Skill/Workflow badges and an Unlink action), even after the session ends. This is the recommended path for items that were flagged link-only.

Importing user criteria as access profiles

The User Criteria tab in the catalog import area imports ServiceNow user criteria as Serval access profiles. Pick a ServiceNow connection, click Sync user criteria to pull criteria from ServiceNow (progress is live-polled, and failures show error details with copyable run identifiers), then review each criterion and choose Import as access profile. The detail panel for each criterion shows:
  • Who it matches - groups, roles, user attribute conditions (attribute criteria such as department or location map to Serval access-profile attribute conditions), and individual users.
  • What it makes requestable - the catalog items the criterion gates, including whether each link is an allow (“Available”) or deny (“Not available”) relationship.
  • Proposed access profile - a preview of the Serval equivalent before you import.
Criteria backed by ServiceNow match scripts show a Cannot be fully mirrored warning, and a criterion with no captured conditions warns that importing it would grant access to everyone on the team. A bulk Set up all access profiles action imports everything outstanding, with progress shown. Each catalog item’s Access tab cross-links to the user criteria that gate it and back; once imported, the access profiles gate the corresponding catalog items in Serval the same way ServiceNow does.

Mirroring knowledge articles into Serval

Knowledge sync can do more than index articles for search: once syncing is enabled for a knowledge source, the source’s settings panel shows an Import to Serval Knowledge Base toggle (off by default). Its help text reads: “When enabled, synced articles from ServiceNow are also written into a Serval native knowledge base. Disable to stop syncing without deleting already-imported pages.” When enabled:
  • Mirrored knowledge bases appear in the Knowledge Bases sidebar under an External Connections section.
  • Mirrored documents are sync-locked and read-only, with a banner - “This document is synced from ServiceNow and is read-only.” - and a View in ServiceNow link.
  • Article HTML is converted to native editor blocks (tables, lists, and headings are supported), and the folder hierarchy from ServiceNow is preserved.
  • Re-syncs update content in place and record version history.
  • Clicking a knowledge item reference offers a destination choice: Open in Serval or Open in ServiceNow.
Disabling the toggle stops future writes but keeps already-imported pages - they freeze rather than delete. Sources or folders with nothing syncable show a No documents sync status.

Sync settings: frequency and catalog filter

A Sync settings dialog - reachable from the app instance’s Workflow Sync settings and from the catalog import page - exposes two controls for catalog sync:
  • Catalog filter - an optional ServiceNow encoded query (for example category=hardware^active=true). Only matching catalog items are imported; leaving it blank imports everything. The filter is combined (AND) with the standard active-items filter. This field appears only for integrations that support it - currently ServiceNow.
  • Sync frequency - Automatic (recommended, the default), Every hour, Every 6 hours, Every 12 hours, Once a day, or Once a week.
Database/entity ingestion syncs are configurable too: each entity ingestion config’s side panel has the same Sync frequency selector. Two things to know about cadence:
  • Unset means the platform default: catalog sync fan-out runs every 30 minutes for ServiceNow, and entity ingestion ticks every 4 hours.
  • A configured interval is a floor, not an exact cadence - scheduled ticks are skipped until the interval has elapsed since the last completed sync. For entity syncs, values at or below about 4 hours behave like Automatic; only the 6-hour, 12-hour, daily, and weekly settings genuinely throttle.

How formatting translates to ServiceNow

ServiceNow journal fields (comments and work notes) and description/close-notes fields render plain text only, so everything Serval writes into them is automatically converted from Serval’s rich formatting to clean plain text. This applies to the built-in two-way ticket sync for all three ServiceNow ticket types - incidents, catalog requested items, and HR cases - covering ticket descriptions on create and update, and message bodies posted as comments or work notes.
Serval formattingWhat ServiceNow receives
Links and images”text (url)” - ServiceNow auto-linkifies bare URLs
Bullet and numbered listsNormalized list lines beginning with ”- “
Bold, italic, headings, code markersStripped, text preserved
@-mentionsResolved to the user’s name
Custom workflows that write their own content into ServiceNow text fields should run it through the markdown-to-ServiceNow-text helper the ServiceNow SDK provides, so raw formatting markers do not leak into the record (see the gotcha below).

How the connection works

Serval connects to one ServiceNow instance and acts as a single dedicated integration user. You pick one of two authentication methods when you connect:
  • OAuth 2.0 (Client Credentials) - the default and recommended option. On each request, Serval exchanges your Client ID and Client Secret for a short-lived bearer token from your instance’s token endpoint (https://<instance>.service-now.com/oauth_token.do) and sends that token on every API call. ServiceNow runs those calls as the OAuth Application User you designate (the same dedicated integration user described below), so that user’s roles and table ACLs determine exactly what Serval can do. Serval does not request any OAuth scopes - the token is bounded entirely by the integration user’s permissions, not by a scope list.
  • Basic authentication - Serval stores the integration user’s username and password and sends them as standard HTTP Basic auth on every call. Use this when inbound OAuth client credentials aren’t available on your instance, or for a quick test setup.
Whichever method you choose, you create the same dedicated integration user and assign it the same roles and ACLs. The only difference is how Serval authenticates as that user.
Serval only ever calls hosts ending in service-now.com - requests to any other host are refused before they leave Serval. Data flows both ways: Serval polls your instance for ticket and comment changes, keeps groups, roles, departments, companies, locations, and cost centers current with scheduled background syncs, and writes back (creating and updating incidents, problems, change requests, HR cases, and catalog requests) on demand when a workflow runs.
Live agent handoff (Virtual Agent Bot-to-Bot) uses one extra shared token that is not part of the connect form. You generate it later from the ServiceNow app’s Ticket Sync settings - see Live agent handoff at the end of this page.

Get your credentials

You configure everything on the ServiceNow side first, then paste two values into Serval. The authoritative provider reference is the ServiceNow product documentation; the exact clickpath for inbound OAuth is in ServiceNow’s official guide, Enable OAuth with inbound REST.
Use a dedicated service account, never a personal or full-admin account. Serval runs every call as this user, so least-privilege here is what bounds what Serval can do - and keeps your ServiceNow audit trail clean (sys_created_by / sys_updated_by will show this account).
1

Create a dedicated integration user

In ServiceNow, go to User Administration → Users and create a new user. Recommended values:
FieldValue
User IDserval.integration
First nameServal
Last nameIntegration
ActiveChecked
Web service access onlyChecked (blocks interactive login)
Internal Integration UserChecked (if available in your ServiceNow version)
Give the user both a first and last name. ServiceNow’s inbound-OAuth user picker omits users that are missing either name - a known quirk of the picker (see this ServiceNow Community thread) - so you won’t be able to select the account as the OAuth Application User later.
If you plan to use Basic authentication, also click Set Password → Generate Password and copy the password now - you will not be able to view it again. You’ll paste it into Serval. If you use OAuth 2.0, you typically do not need a password for Serval: API access is authorized via Client ID and Client Secret, and the integration user is designated on the OAuth application below.
2

Create a custom role for Serval

Creating a custom role with granular permissions is the recommended approach for production environments. It follows the principle of least privilege and provides better security than out-of-the-box roles like itil or admin. (The simpler OOB-role alternative is covered in the next step.)
Go to User Administration → Roles and create:
FieldValue
Namex_serval_integration
DescriptionCustom role for Serval integration with least-privilege API access
Then open the role and, in the Contains Roles related list, add these base roles:
RolePurpose
rest_api_explorerEnables REST API access
personalize_choicesAllows reading sys_choice values
personalize_dictionaryAllows reading sys_dictionary for field definitions
These base roles provide the foundational REST API capabilities; granular table access comes from the ACLs in the next step.
3

Grant table access via ACLs

Serval requires specific read/write access to ServiceNow tables. Create Access Control List (ACL) rules that grant only the necessary permissions.
Serval uses ServiceNow’s Table API, Service Catalog API, Attachment API (all active by default), and the Change Management REST API, which requires the Change Management - REST API plugin (com.snc.change_management.rest_api) - active by default on most instances.

Incident Management Tables

TableReadCreateWritePurpose
incidentYesYesYesIncident records (all standard fields)
sys_journal_fieldYes--Comments and work notes

HR Service Delivery Tables

Required only if you sync or create HR cases.
TableReadCreateWritePurpose
sn_hr_core_caseYesYesYesHR case records, including comments and work notes

Service Catalog Tables

TableReadCreateWritePurpose
sc_cat_itemYes--Catalog item details
sc_categoryYes--Catalog categories
sc_requestYesYes-Service requests
sc_req_itemYesYesYesRequested items
item_option_newYes--Catalog item variable definitions
io_set_itemYes--Variable set to catalog item mapping
question_choiceYes--Choices for select-type catalog variables
catalog_script_clientYes--Catalog client scripts (fillability analysis, Understanding tab)
catalog_ui_policyYes--Catalog UI policies (When/Show/Hide/Require rules)
catalog_ui_policy_actionYes--UI policy actions
sys_script_includeYes--Script includes referenced by catalog client scripts
sys_attachment-Yes-File attachment uploads for requested items (written through the Attachment API, but the user still needs create permission)

Configuration Tables (Read-Only)

TableReadCreateWritePurpose
sys_userYes--User lookup for assignment
sys_user_groupYes--Assignment group lookup
sys_choiceYes--Field dropdown values
sys_dictionaryYes--Field definitions
cmdb_ci_serviceYes--Business services

Knowledge Base Tables (Read-Only)

TableReadCreateWritePurpose
kb_knowledge_baseYes--Knowledge base metadata
kb_categoryYes--Knowledge categories
kb_knowledgeYes--Knowledge articles
kb_article_templateYes--Article template metadata (templated articles)
kb_article_template_definitionYes--Article template definitions; templated articles also read their extended template tables

User Criteria Tables (Read-Only)

These tables determine which users have access to knowledge articles and catalog items, and back the user criteria import.
TableReadCreateWritePurpose
user_criteriaYes--User criteria definitions (users, groups, companies, etc.)
kb_uc_can_read_mtomYes--Knowledge article access - users who CAN read
kb_uc_cannot_read_mtomYes--Knowledge article access - users who CANNOT read
sc_cat_item_user_criteria_mtomYes--Catalog item access - users who CAN access
sc_cat_item_user_criteria_no_mtomYes--Catalog item access - users who CANNOT access

Change Management Tables

TableReadCreateWritePurpose
change_requestYesYesYesChange request records
sysapproval_approverYes-YesChange request approval records (write is needed to act on approvals)
Creating change requests uses the Change Management REST API, which requires the Change Management - REST API plugin (com.snc.change_management.rest_api) to be active on your ServiceNow instance. This plugin is active by default on most instances.

Problem Management Tables

TableReadCreateWritePurpose
problemYesYesYesProblem records

Access Management & Resource Sync Tables

These tables are required if you use Serval’s access profile capabilities for syncing organizational resources and provisioning/deprovisioning users.
If you are not using access profiles, you can skip these ACLs. The sys_user and sys_user_group tables listed above under Configuration Tables still require read access for basic functionality regardless.
TableReadCreateWriteDeletePurpose
sys_user--Yes-Write access for department assignment (read access already listed above)
sys_user_grmemberYesYes-YesGroup membership provisioning and deprovisioning
sys_user_roleYes---Role definitions for resource sync
sys_user_has_roleYesYes-YesRole assignment provisioning and deprovisioning
core_companyYes---Company records for resource sync
cmn_departmentYes---Department records for resource sync
cmn_locationYes---Location records for resource sync
cmn_cost_centerYes---Cost center records for resource sync
4

Assign the custom role to the integration user

  1. Navigate to User Administration → Users and open the serval.integration user
  2. Scroll down to the Roles related list and click Edit
  3. Add the x_serval_integration role (or the OOB roles if you chose that approach)
  4. Click Save
5

(OAuth only) Enable the inbound client-credentials grant

Inbound OAuth client credentials are disabled by default on most ServiceNow instances. In System Properties (sys_properties.list), find or create:
FieldValue
Nameglide.oauth.inbound.client.credential.grant_type.enabled
Typetrue | false
Valuetrue
ApplicationGlobal
If this property is missing or false, every token request to oauth_token.do fails with access_denied, no matter how the OAuth app is configured. This is the single most common cause of a failed OAuth connection. ServiceNow may also display a banner on the Application Registry form warning that the property is disabled. ServiceNow documents this requirement in its official guide, Up your OAuth 2.0 game: Inbound Client Credentials.
6

(OAuth only) Create the OAuth Application Registry entry

Go to System OAuth → Application Registry → New. ServiceNow shows a list of OAuth application types - pick the one for inbound access (external systems calling into your instance). Do not choose Connect to a third party OAuth Provider or other outbound options; those are for ServiceNow calling external OAuth servers, not for Serval.
  • Recommended: New Inbound Integration Experience - the current UI for inbound OAuth, including client credentials. When the wizard asks Select your application connection type, choose OAuth - Client credentials grant (machine-to-machine). Do not choose Authorization code, JWT bearer, Resource owner password, or third-party ID token flows for the Serval integration.
  • Alternative: [Deprecated UI] Create an OAuth API endpoint for external clients - the older wizard for the same kind of inbound app. “Deprecated UI” refers to ServiceNow replacing the screen flow, not to the credentials; it still produces a working Client ID / Client Secret. Some teams see odd default forms here (for example, the OAuth Application User field hidden until you change the form layout); using the New Inbound Integration Experience often avoids that.
ServiceNow modal Select your application connection type showing five options; OAuth Client credentials grant is highlighted as the choice for Serval
Give the application a clear name (for example, Serval integration) and save; ServiceNow auto-generates the Client Secret. Then:
  1. Set Client type to run API calls as your integration account - for example Integration as a Service (wording varies by release). That tells ServiceNow which sys_user supplies roles, ACLs, and audit fields for requests made with a client-credentials token.
  2. Allow the Client credentials grant. Often this is not on the main form: open the OAuth Entity or OAuth Entity Profile related list on the same Application Registry record, open the related row, and confirm Client credentials is enabled. Save that record if you changed it.
  3. Set the OAuth Application User to your serval.integration user, so issued tokens act as that account.
If you don’t see an OAuth Application User field on the form, ServiceNow hides it on the default layout. Open the form’s context menu → Configure → Form Layout, move OAuth Application User from Available to Selected, save, and reload. Some instances instead expose the value as a column in the Application Registry list view, or on the related OAuth Entity Profile row. On the newer Inbound Integration UI, only the first 1,000 users (alphabetical) appear in the picker - a documented ServiceNow known error - search precisely for your account.
The integration user must already have the roles and ACLs from the earlier steps so token-backed API calls are authorized correctly.
7

(OAuth only) Copy the Client ID and Client Secret

From the OAuth application record, copy the Client ID and Client Secret (click the lock icon to reveal the secret). Store them securely - anyone holding both can mint tokens that act as your integration user.
8

Identify your ServiceNow Instance Name

Your Instance Name is the subdomain of your ServiceNow URL: if your instance lives at https://mycompany.service-now.com, the instance name is mycompany. You’ll enter it in Serval next.

Connect in Serval

In Serval, open Apps → ServiceNow → Connect and complete the form. The fields that appear depend on the Authentication method you pick.
1

Enter your Instance Name

Instance Name - the subdomain of your ServiceNow instance. For https://mycompany.service-now.com, enter mycompany.
You can paste the full URL (e.g. https://mycompany.service-now.com/now/nav) - Serval strips the scheme, path, port, and the .service-now.com suffix and lowercases what’s left. If the result isn’t a single valid subdomain, you’ll see “Instance Name must be the subdomain of your ServiceNow instance (e.g. ‘mycompany’ for mycompany.service-now.com)”.
2

Choose the authentication method and fill in credentials

Authentication method defaults to OAuth 2.0 (Client Credentials). The remaining fields switch based on your choice:
3

Save the connection

Save. Serval immediately tries to authenticate against your instance. If anything is off (wrong instance, bad credentials, or inbound OAuth not enabled), ServiceNow’s error surfaces back to you.
When you later edit the connection, secret fields show as masked placeholders. Leave them untouched to keep the stored values; only retype a field to change it. Switching auth methods (OAuth ↔ Basic) does require you to enter that method’s credentials fresh.

Verifying the connection

Open the ServiceNow app’s API Integration tab and run the health checks. They confirm Serval can authenticate as your integration user and exercise the real surfaces it uses:
  • Test ServiceNow Connection - authenticates and queries your user table (a single row). Success reads “Successfully authenticated with ServiceNow”. Failure reads “Unable to connect to ServiceNow. Please verify your credentials are valid and have the necessary permissions.”
  • List ServiceNow Users - samples up to ten rows from sys_user. Success reports the sample size; failure reads “Unable to list users from ServiceNow. Please verify the credentials have permission to read the sys_user table.”
  • List ServiceNow Incidents, List ServiceNow Knowledge Articles, List ServiceNow Catalog Items, and List ServiceNow User Criteria - the same ten-row sample pattern against incident, kb_knowledge, sc_cat_item, and user_criteria. Each failure message names the table the credentials couldn’t read.
A deeper lifecycle health check creates real records on your instance: it looks up the serval.integration user, creates a temporary incident whose short description begins with “Health Check Test Incident”, drives it through state transitions (New → In Progress → On Hold with a hold reason → In Progress), resolves it, creates additional short-lived incidents to exercise impact/urgency permutations (all resolved afterwards), and places a test catalog request against your first active catalog item, then closes the requested item. Test incidents are resolved, not deleted - seeing “Health Check Test” records appear and resolve is expected. The resolution-codes check fails if your instance has no resolution codes defined. The user lookup is hard-coded to the serval.integration user name - if you chose a different User ID in Get your credentials, this check fails at the user-lookup step even though the connection itself is healthy.
A green Test Connection but a failing list check almost always means the integration user is missing a table ACL (for example, read on kb_knowledge or user_criteria). The failing check names the table it couldn’t reach.

Gotchas and troubleshooting

This means ServiceNow rejected the token request at oauth_token.do - not a Serval storage problem. Work through these on the ServiceNow side, most common first:
  1. Inbound client credentials enabled - glide.oauth.inbound.client.credential.grant_type.enabled must be true in Global scope. This is the number-one cause.
  2. OAuth Application User set - the app must be tied to your integration user, and that user must be Active with the right roles/ACLs.
  3. Client credentials grant allowed - confirm it’s enabled on the app (or its OAuth Entity / Entity Profile record).
  4. Client ID / Secret correct - re-copy from ServiceNow with no leading or trailing spaces. If you rotated the secret in ServiceNow, update the Serval connection too.
If all four checks above pass and the token request still fails, the cause is on the ServiceNow side rather than in how Serval formats the request - contact support@serval.com.
ServiceNow’s inbound-OAuth user picker omits accounts that are missing a first or last name - a known quirk of the picker (see this ServiceNow Community thread). Populate both names on the user, confirm it’s Active, and reload. On the newer Inbound Integration experience, only the first 1,000 users alphabetically are listed - a documented ServiceNow known error - so search precisely. If the field itself is absent, add it via Configure → Form Layout as described in Get your credentials.
Serval normalizes whatever you paste down to a single subdomain. Blank input returns “Instance Name is required”. Anything that doesn’t reduce to a valid DNS label returns “Instance Name must be the subdomain of your ServiceNow instance (e.g. ‘mycompany’ for mycompany.service-now.com)”. Enter just mycompany (or paste the full URL and let Serval trim it) - don’t enter an IP, a non-service-now.com host, or extra path segments that aren’t part of the subdomain.
Serval enforces complete credentials per method:
  • OAuth: “Client ID and Client Secret are required for OAuth 2.0” (or “Client ID and Client Secret are required when switching to OAuth 2.0” if you changed methods).
  • Basic: “Username and Password are required for Basic Auth” (or “Username and Password are required when switching to Basic Auth”).
When you’re only editing an existing OAuth or Basic connection, you can leave the masked fields as-is; Serval keeps the stored values. But switching auth methods requires entering that method’s credentials in full.
ServiceNow choice fields (like close_code, category, priority) silently ignore invalid values - the API returns 200 but the field stays empty, and a downstream Data Policy then blocks the state change. Valid choice values are instance-specific, so Serval’s helpers and workflows query them rather than hard-coding. If a workflow write isn’t sticking, the usual cause is an instance Business Rule, write ACL, or Data Policy on the target table - those are configured in ServiceNow and can’t be worked around from Serval.
Change-request creation needs the Change Management - REST API plugin (com.snc.change_management.rest_api) active. Most state transitions also require an assignment group to be set - set it at creation time, since adding it later is often blocked. Change approvals live in a separate table (sysapproval_approver), so the integration user needs write access there for approval workflows to work. A 403 from the Change Management API usually means a state-model condition isn’t met; a 400 naming a rule means a Business Rule is blocking.
Each surface needs its own read ACLs on the integration user’s role. Empty knowledge sync → check read on the kb_* tables; empty catalog → check the catalog definition tables; empty access profiles → check the user_criteria and organizational tables. The matching health check (knowledge articles, catalog items, user criteria) will name the table it couldn’t read.
The Catalog filter in Sync settings is a ServiceNow encoded query that is combined (AND) with the standard active-items filter - a filter that matches nothing imports nothing. The sync also fails loudly if the integration user lacks the ACLs needed to run the query against the catalog tables, so if a previously working sync breaks right after adding a filter, check the filter syntax first and the integration user’s catalog-table read access second.
Configured intervals are a minimum interval, not an exact schedule: the platform’s scheduled ticks simply skip a sync until the configured interval has elapsed since the last completed run. Entity ingestion ticks every 4 hours, so entity-sync values at or below 4 hours behave like Automatic - only Every 6 hours, Every 12 hours, Once a day, and Once a week genuinely slow things down. Catalog sync fan-out runs every 30 minutes by default.
That’s by design. While Import to Serval Knowledge Base is enabled, mirrored documents are sync-locked - ServiceNow remains the source of truth and re-syncs update the Serval copy in place. Edit the article in ServiceNow (the document banner has a View in ServiceNow link). Disabling the toggle stops future writes but keeps already-imported pages rather than deleting them.
Criteria backed by ServiceNow match scripts show a Cannot be fully mirrored warning - Serval imports the declarative parts (groups, roles, attributes, users) but cannot execute the script. And a criterion with no captured conditions imports as an access profile that matches everyone on the team - Serval warns before you import it. Review the Proposed access profile preview before confirming.
ServiceNow’s journal and description fields render plain text only. Serval’s built-in ticket sync converts formatting automatically (see How formatting translates to ServiceNow), but a custom workflow that writes Serval-authored content directly into description, comments, work notes, or close notes should first run it through the markdown-to-ServiceNow-text helper in the ServiceNow SDK - otherwise bold markers, bullets, and link syntax appear verbatim in the record.
You can switch methods on an existing connection without recreating it - just change Authentication method and enter the new method’s credentials. Serval clears the old method’s stored secret when you switch, so there’s no stale password or client secret left behind. The Instance Name and the integration user stay the same.

Real-time comment sync via webhook (optional)

By default, Serval polls your ServiceNow instance to sync comments. For faster, near-instant comment delivery, you can configure a Business Rule that pushes new comments to Serval via webhook. This applies to comments on Incidents, HR cases, and Catalog Requested Items that were created by the Serval integration.
This requires a single Business Rule on the sys_journal_field table. No ServiceNow plugins, store apps, or additional licensing are required.

How it works

When a comment is added to a Serval-managed record, an async insert Business Rule fires on the sys_journal_field table. The rule verifies the parent record was opened by the Serval service account (the dedicated integration user you created in Get your credentials), constructs a JSON payload, and sends it asynchronously to the Serval webhook. Serval processes the event and delivers the comment to the relevant conversation in real time - entries on the comments field surface as public replies, entries on work_notes surface as internal notes.
The script filters on the parent record’s opened_by field, not on table name - it fires for journal entries on any record opened by the Serval service account. Serval’s webhook handler processes events for incident, sn_hr_core_case, and sc_req_item records.

Get your webhook token

The webhook carries a per-environment token that Serval provides during onboarding, sent in the X-Serval-Webhook-Token header (server-side verification of this token is on the roadmap; the token is forwarded with each event today). Contact support@serval.com to obtain your token before creating the Business Rule. For production, store it in a System Property (for example x_serval.webhook_token) rather than hard-coding it in the script.

Webhook endpoint

PropertyValue
URLhttps://svwebhook.api.serval.com/servicenow/new-comment
MethodPOST
Content-Typeapplication/json
AuthenticationToken via X-Serval-Webhook-Token header
The Business Rule sends a JSON body with these fields. record_sys_id, comment.sys_id, and a parseable instance_url are required; Serval routes the event to your integration install by the instance domain.
FieldTypeDescription
instance_urlstringYour ServiceNow instance URL
tablestringSource table: incident, sn_hr_core_case, or sc_req_item
record_sys_idstringsys_id of the parent record
numberstringRecord number, e.g. INC0012345, HRC0001234, RITM0056789
comment.sys_idstringsys_id of the journal entry
comment.elementstringcomments (public reply) or work_notes (internal note)
comment.valuestringThe comment body text
comment.sys_created_bystringuser_name of the commenter (must be a ServiceNow user_name - Serval resolves it to attribute the comment)
comment.sys_created_onstringServiceNow timestamp

Create the Business Rule

1

Navigate to Business Rules

In ServiceNow, go to System Definition → Business Rules and click New.
2

Configure the rule settings

Set the following fields:
FieldValue
NameServal - Sync Comment to Webhook
Tablesys_journal_field
AdvancedChecked
Whenasync
InsertChecked
UpdateUnchecked
DeleteUnchecked
QueryUnchecked
ServiceNow Business Rule configuration showing the When to run tab with async timing, Insert checked, and the sys_journal_field table selected
3

Add the script

Switch to the Advanced tab and paste the following script:
ServiceNow Business Rule Advanced tab showing the webhook script in the script editor
(function executeRule(current, previous /*null when async*/) {

    var tableName   = current.getValue('name');
    var elementType = current.getValue('element');

    // Validate: only Serval-managed records.
    // Replace 'serval.integration' with the user_name of your
    // Serval service account if it differs.
    var SERVAL_USER = 'serval.integration';

    var recordSysId = current.getValue('element_id');
    var parentRecord = new GlideRecord(tableName);
    if (!parentRecord.get(recordSysId)) {
        gs.warn('Serval webhook: parent record not found: '
                + tableName + '/' + recordSysId);
        return;
    }

    var openedBy = parentRecord.opened_by.user_name.toString();
    if (openedBy !== SERVAL_USER) {
        return;
    }

    // Build payload
    var instanceName = gs.getProperty('instance_name');
    var instanceUrl  = 'https://' + instanceName + '.service-now.com';

    var payload = {
        instance_url:  instanceUrl,
        table:         tableName,
        record_sys_id: recordSysId,
        number:        parentRecord.getValue('number'),
        comment: {
            sys_id:         current.getUniqueValue(),
            element:        elementType,
            value:          current.getValue('value'),
            sys_created_by: current.getValue('sys_created_by'),
            sys_created_on: current.getValue('sys_created_on')
        }
    };

    // Configuration
    // Replace the token value with the token provided by Serval.
    // For production, store this in a System Property
    // (e.g., x_serval.webhook_token) and retrieve it via
    // gs.getProperty('x_serval.webhook_token').
    var SERVAL_WEBHOOK_URL   = 'https://svwebhook.api.serval.com/servicenow/new-comment';
    var SERVAL_WEBHOOK_TOKEN = '<TOKEN_PROVIDED_BY_SERVAL>';

    try {
        var request = new sn_ws.RESTMessageV2();
        request.setEndpoint(SERVAL_WEBHOOK_URL);
        request.setHttpMethod('POST');
        request.setRequestHeader('Content-Type', 'application/json');
        request.setRequestHeader('X-Serval-Webhook-Token', SERVAL_WEBHOOK_TOKEN);
        request.setRequestBody(JSON.stringify(payload));

        // Run asynchronously so we don't block the user's transaction
        request.setEccParameter('skip_sensor', 'true');

        var response = request.executeAsync();

        gs.info('Serval webhook: sent ' + elementType + ' event for '
            + tableName + '/' + parentRecord.getValue('number')
            + ' (async)');
    } catch (e) {
        gs.error('Serval webhook: failed to send event: ' + e.message);
    }

})(current, previous);
The script references serval.integration as the Serval service account user_name. This must match the user_name of the integration user you created in Get your credentials. If you chose a different User ID, update the SERVAL_USER variable accordingly.
4

Save the Business Rule

Click Submit to save the rule. Comment sync will begin working immediately for Serval-managed records.

Live agent handoff via VA Bot-to-Bot (optional)

Serval can hand a conversation off to a human ServiceNow agent mid-flow, then bridge the agent’s replies back into the Serval ticket so the user keeps chatting in their original surface (Slack, Teams, web, etc.). This rides on ServiceNow’s native Virtual Agent Bot-to-Bot APIs - there is no Serval-shipped update set and no Scripted REST API to install on your instance. Setup on the ServiceNow side is roughly: install the Virtual Agent API plugin (if not already installed), grant the Serval service account a few additional permissions, create a Token Verification + Message Auth + Provider Application + Outbound REST Message pointing at a Serval-hosted webhook URL, and (optionally) tune the AWA routing for the chat channel you want to use for the handoff.
Live agent sessions appear on Serval tickets as an additional external ticket with subtype = "interaction" on the existing ServiceNow integration - they do not create a separate integration or channel type. The auto-created interaction record on the ServiceNow side is the same record type ServiceNow uses for all VA-mediated chats.

How the bridge works

The bridge has three legs:
  1. Inbound to ServiceNow - Serval opens, sends messages on, and ends a Bot-to-Bot conversation by calling ServiceNow’s Virtual Agent Bot Integration endpoint (installed by the Virtual Agent API plugin). The action discriminator travels in the request body: START_CONVERSATION, AGENT_CHAT, or END_CONVERSATION.
  2. Outbound from ServiceNow - when the live agent types a reply, ServiceNow’s Provider Application looks up its associated Outbound REST Message and POSTs the response to the URL you configured there. That URL is the Serval-hosted webhook.
  3. Agent presence pre-flight (optional) - Serval’s availability check reads the awa_agent_presence_capacity table to know whether any agents are currently online before starting a handoff.
Routing (which queue / which agent the conversation lands in) is handled entirely by AWA on the ServiceNow side. The two knobs you have from Serval workflow code are:
  • channelId on the start action - maps to a ServiceNow sys_cs_channel record. Defaults to "chat". The channel record’s “Bot to Bot Synchronous” flag and its AWA queue assignment together determine where the conversation routes.
  • contextVariables on the start action - a free-form key/value bag passed to ServiceNow on every turn. Customers configure their AWA routing rule against the variable name(s) of their choice (e.g. u_liveagent_optional_skills). See Customize routing with context variables below.

Prerequisites

  • Virtual Agent + Virtual Agent API plugins installed on your ServiceNow instance (Quebec or later).
  • The Serval ServiceNow integration must already be connected (everything above on this page).
  • Now Assist Pro licensing is not required - Bot-to-Bot is part of the base Virtual Agent API plugin.

Limitations (read before you commit)

  • Closing a Serval ticket does not automatically end the ServiceNow conversation. Your workflow should call the end-session action when the ticket resolves if you want symmetric cleanup. The conversation eventually times out on the ServiceNow side either way.
  • Agent attribution is automatic; assignment details are not. ServiceNow’s Bot-to-Bot payloads carry per-message agent information, and Serval resolves it to the ServiceNow user - so agent replies are attributed correctly on the Serval ticket with no extra work. What the asynchronous START acknowledgment does not tell you is which queue the conversation landed in or when an agent picked up; poll the auto-created interaction record’s assigned_to field from a workflow if you need that - see the example workflow below.
  • Inbound auth verification is currently advisory. The Basic-auth password on inbound webhooks is forwarded onto the event for downstream verification, but the constant-time check against the rotated token is not yet enforced server-side. The per-install webhook URL contains an unguessable UUID, which is the practical access control today. Treat the rotated token as a not-yet-load-bearing secret in the meantime - full server-side verification is on the roadmap.
  • Attachments require trusted-media-domain configuration. Files do cross the bridge in both directions, but ServiceNow refuses to download attachments from URLs whose host isn’t listed under the active Provider Application record’s Trusted media domains (the sys_cs_provider_application record - labeled Provider Channel Identity on some releases). You must add Serval’s S3 attachment host (the host of the presigned URL Serval sends for attachments - visible on a test egress) to that list, otherwise the live agent will see “We couldn’t download your file using the specified URL” instead of the image. See the ServiceNow docs: Set up trusted media domains for secure file upload for the field and ACL setup.

What agent replies look like on the Serval ticket

Three kinds of inbound items surface on the bridged ticket; everything else (topic pickers and other Virtual Agent scaffolding) is accepted and dropped:
  • Agent text surfaces as a comment, attributed to the ServiceNow agent.
  • Agent images and files surface as attachments. Serval downloads them from ServiceNow’s conversational media API using your stored integration credentials, capped at 50 MB per file. If a download fails, Serval posts the placeholder comment [attachment <name> could not be downloaded - please refresh ServiceNow Agent Workspace] instead of failing the whole message.
  • System prompts (such as AWA idle-timeout warnings) surface as comments so the user can reply and keep the chat alive.
When the conversation ends - whether the agent closes it or it times out - Virtual Agent’s closing lifecycle text (such as “no agents available” or “the conversation has ended” boilerplate) is suppressed so it doesn’t clutter the ticket. A real agent’s final message is preserved and lands before the channel disconnects; afterwards Serval posts a single “Live agent chat session has ended.” note. Duplicate webhook deliveries are deduplicated automatically.

Step 1: Grant additional permissions to the Serval service account

The standard Serval ACLs cover the Table API surface used by the rest of the integration. Live Agent needs two additional ServiceNow roles plus reads on the AWA tables and the interaction table. Add these to your Serval service-account user: Roles:
RoleWhy
virtual_agent_adminRequired for ServiceNow’s Bot Integration endpoint to accept Serval’s Bot-to-Bot calls.
interaction_agentLets Serval read the auto-created interaction record after START_CONVERSATION (for the optional assignment poll) and lets the inbound attachment-download path resolve the agent-supplied conversational-media URLs on image items.
Table reads (one ACL per table, Type: record, Operation: read, Requires role: x_serval_integration or whichever role you assigned to the Serval service account):
TablePurpose
awa_agent_presence_capacityUsed by the availability check. Without this, the check will report no agents available for everyone.
awa_agent_presenceUsed by AWA routing - presence_capacity is a view that joins this in.
awa_agent_capacitySame; joined by the presence_capacity view.
interactionRead access for the optional assigned_to poll. Already covered if you’re using the interaction_agent role above.

Step 2: Generate the shared bot token in Serval

In your Serval workspace, navigate to App Instances → ServiceNow → Ticket Sync settings. Scroll to the Live Agent (VA Bot-to-Bot) section and click Generate token. You’ll see three values you’ll need in the next step:
ValueWhat it is
Inbound Webhook URLServal-hosted URL the ServiceNow Outbound REST Message will POST agent replies to. Per-install - contains an unguessable UUID - so do not share between environments.
Authentication Usernameserval by convention. Any username is accepted - the password (the token below) is the authenticator, and it must be non-empty.
Authentication TokenOne-time-displayed opaque secret - copy it now, you cannot view it again. Click Rotate token later if you ever need a new one (rotation invalidates the previous value immediately).
One token, two destinations on the ServiceNow side. This token value lives in two places on ServiceNow:
  • The Token Verification record. Serval injects this value into the body of every Bot-to-Bot call; ServiceNow’s Message Auth middleware verifies it before processing.
  • The Outbound REST Message Basic-auth password. ServiceNow presents this value when posting agent replies to the Inbound Webhook URL above.
You’ll wire both up in the next step.

Step 3: Configure the four ServiceNow records that wire up Bot-to-Bot

ServiceNow’s Bot-to-Bot setup links four records together. The record names below are the OOB tables - the navigation labels may vary slightly by release.
1

Enable inbound auth on the VA Bot Integration Scripted REST API

Navigate to System Web Services → Scripted Web Services → Scripted REST APIs and open the VA Bot Integration record. Open the Bot Integration resource. On the Security tab, set Requires authentication = true and Requires ACL authorization = false. Save.This makes the bot integration endpoint reject anonymous calls - the Token Verification record you’ll create next is what gates it.
2

Create the Token Verification record

In the filter navigator, type token_verification.list and press Enter. Click New. Fill in:
FieldValue
NameServal (or any human-readable name)
TokenThe Authentication Token from Serval (from Step 2)
Save. This is the value ServiceNow will validate on every inbound call from Serval.
3

Create the Message Auth record

In the filter navigator, type message_auth.list and press Enter. Click New. Fill in:
FieldValue
NameServal Bot Auth
ProviderServal (free-text; only used for organization)
Inbound Message VerificationPick the Token Verification record from the previous step (Serval)
Outbound Message CreationPick the same Token Verification record
Save.
4

Create the Provider Application

In the filter navigator, type sys_cs_provider_application.list and press Enter. Click New. Fill in:
FieldValue
NameServal Bot
Inbound IDserval (or any unique slug - this is the appInboundId value, only needed in multi-bot instances; single-bot instances can pick any value)
Message AuthPick the Message Auth record from the previous step (Serval Bot Auth)
ProviderVA Bot to Bot Provider
Save. The name of this record matters for the next step. This is also the record that carries the Trusted media domains list used for attachment egress (see Limitations above).
5

Create the Outbound REST Message

In the filter navigator, type REST Message and open the table. Click New. Fill in:
FieldValue
NameMust exactly match the Provider Application name from the previous step (Serval Bot). ServiceNow finds the outbound endpoint by name lookup against the active Provider Application.
EndpointThe Inbound Webhook URL from Serval (Step 2).
Authentication typeBasic
Use mutual authenticationunchecked
Basic auth profileCreate a new Basic Auth profile (or pick an existing one) with Username: serval and the Authentication Token from Step 2 as the password.
Save. ServiceNow will now use this REST Message - looked up by name - whenever the Provider Application emits an agent reply.By convention, also create a single HTTP Method record on the REST Message named post with:
FieldValue
HTTP methodPOST
EndpointSame as the parent REST Message
HTTP HeadersContent-Type: application/json
Some ServiceNow releases auto-create this from the parent record; if your release does, you can skip the explicit creation.
6

Verify the chat channel is Bot-to-Bot enabled

In the filter navigator, type sys_cs_channel.list and press Enter. Find the channel you intend to route through (the OOB one is named chat - its sys_id is what you’ll pass as channelId from your workflows; the literal string "chat" also works on most releases).On the channel record, confirm Bot to Bot Synchronous = true. If it’s not, set it and save. Without this flag, the bot integration endpoint will reject START_CONVERSATION calls with a routing error.
If you ever click Rotate token in Serval, you must update both ServiceNow records:
  1. The Token Verification record’s Token field.
  2. The Basic Auth profile used by the Outbound REST Message (or, if you embedded the password directly on the REST Message, update it there).
Serval-to-ServiceNow calls (starting sessions, sending messages, ending conversations) fail until the Token Verification record matches the new token - ServiceNow checks it on every call. Inbound agent messages keep flowing in the meantime because Serval’s inbound verification is currently advisory (see Limitations above), but update the Basic Auth profile anyway so the inbound direction does not break once server-side verification is enforced.

Step 4: SDK building blocks

The ServiceNow SDK exposes five thin building-block actions for live agent. They are intentionally not orchestration helpers - live-agent flows vary enough between customer ServiceNow configurations (different chat experiences, custom AWA routing rules, renamed topics, varying trigger phrases for NLU) that workflow authors compose these directly rather than configure a single high-level wrapper:
  • checkLiveAgentAvailability - pre-flight: are any human agents online right now? Reads the agent-presence capacity table and returns whether agents are available plus a queue-depth figure (the sum of agents’ remaining capacity). Skill-level filtering is intentionally not exposed - routing belongs to AWA.
  • startLiveAgentSession - opens a conversation. The caller supplies the Serval ticketId, the user (a ServiceNow sys_user sys_id is preferred, plus email), the verbatim message text (including any NLU trigger phrase), channelId (defaults to "chat"), contextVariables for routing (re-sent on every turn), and optionally appInboundId for multi-bot instances. connectTicket defaults to true, which wires the ServiceNow conversation onto the Serval ticket as an interaction-subtype external ticket so user replies auto-egress as AGENT_CHAT turns. It returns the session identifiers - including clientSessionId and a liveAgentData blob that the attach action below consumes.
  • sendLiveAgentMessage - manually sends an AGENT_CHAT turn. Usually not needed: once the ticket is connected, user replies auto-egress through the standard pipeline. Use it for programmatic messages (auto-replies, scripted status updates, testing).
  • endLiveAgentSession - force-ends a session (e.g. on ticket resolve, user abandon, or SLA timeout).
  • attachInteractionSysIdToLiveAgentSession - stashes a polled interaction sys_id onto the Serval external ticket so the ticket-channels surface in the Serval UI renders a deep link to the chat in ServiceNow Agent Workspace.

Step 5: Customize routing with context variables

contextVariables is the principal knob for AWA routing. The variable names you pass are customer-specific - they must match the conditions on whichever AWA routing rule you’ve configured on your ServiceNow instance. For example, an “IT Live Agent Chat” queue whose routing rule has the condition context.u_liveagent_optional_skills = IT is targeted by passing a u_liveagent_optional_skills context variable with the value IT. Other instances might use u_skill_required, routing_skill, or any other custom variable name. To find the right variable name on your instance, check your AWA routing rule’s condition script, or ask your ServiceNow admin. Custom interaction-record fields (e.g. CMDB tag, custom assignment-group overrides, region routing) can also be projected onto the auto-created interaction record via a ServiceNow-side Business Rule that reads the contextVariables payload. This is a pure ServiceNow-side customization - no Serval changes required.

Step 6: Example workflow - START_CONVERSATION with routing and assignment poll

This mirrors the canonical pattern verified end-to-end against a real ServiceNow instance. You can drop this into a Serval workflow as a starting point and adapt the routing skills, trigger phrase, and message composition to your environment.
import { workflow, sleep } from "serval/core";
import * as servicenow from "serval/integrations/servicenow";

export const main = workflow({
  fn: async function (
    args: {
      email: string;       // The end-user's email
      userMessage: string; // The user's natural-language message
      ticketId: string;    // The Serval ticket to bridge onto
    },
    ctx: servicenow.context.ServiceNowIntegration,
  ) {
    // 1) Resolve the ServiceNow sys_user.sys_id for this user. The
    //    Bot-to-Bot endpoint accepts email/username but account-linking
    //    works best with a sys_id, and AWA assignment is more reliable.
    const userSysId = await servicenow.lookupUserSysIdByEmail(
      { email: args.email },
      ctx,
    );

    // 2) Compose the message text. The trailing trigger phrase is what
    //    ServiceNow's NLU classifies as the "Live Agent Support" topic -
    //    the OOB topic that drives the AWA handoff without going through
    //    the Greetings picker. Customers who have renamed that topic
    //    or have a custom topic should adapt the phrase to match.
    const ROUTING_TRIGGER_PHRASE =
      "I need a live agent. Please connect me with Live Agent Support - I want to talk to an agent.";
    const message =
      args.userMessage && args.userMessage.length > 0
        ? args.userMessage + "\n\n" + ROUTING_TRIGGER_PHRASE
        : ROUTING_TRIGGER_PHRASE;

    // 3) Open the conversation. `connectTicket: true` (default) wires
    //    the ServiceNow clientSessionId onto the Serval ticket as an
    //    interaction-subtype external ticket, so subsequent user
    //    replies on the Serval ticket auto-egress as AGENT_CHAT turns.
    const { clientSessionId, liveAgentData } =
      await servicenow.startLiveAgentSession(
        {
          ticketId: args.ticketId,
          user: { userId: userSysId, email: args.email },
          message,
          channelId: "chat",
          contextVariables: { u_liveagent_optional_skills: "IT" },
        },
        ctx,
      );

    // 4) Optional: poll the `interaction` table to learn which agent
    //    actually picked up the conversation. START returns only an
    //    async ack - the real assignment happens a moment later when
    //    AWA matches an available agent. Stop early once `assigned_to`
    //    is populated.
    let routingInteraction: Record<string, unknown> | null = null;
    for (let attempt = 0; attempt < 4; attempt++) {
      await sleep({ durationMs: 3000 });
      const { result } = await servicenow.tableApiRequest(
        {
          method: "GET",
          path: "/api/now/table/{tableName}",
          pathParams: { tableName: "interaction" },
          query: {
            sysparm_query:
              "opened_for=" + userSysId + "^type=chat^ORDERBYDESCsys_created_on",
            sysparm_fields:
              "sys_id,number,state,assigned_to,assignment_group,queue,opened_at",
            sysparm_display_value: "true",
            sysparm_exclude_reference_link: "true",
            sysparm_limit: "1",
          },
        },
        ctx,
      ) as { result: Array<Record<string, unknown>> };

      if (result.length > 0) {
        routingInteraction = result[0];
        if (routingInteraction["assigned_to"]) {
          break; // Agent picked up - done polling.
        }
      }
    }

    // 5) Optional: stash the polled `interaction.sys_id` onto the
    //    Serval external ticket so the ticket-channels surface in the
    //    Serval UI renders a canonical Agent Workspace deep link to
    //    the chat. Skip this if you don't run the post-START poll -
    //    the channel still surfaces, just without a clickable link.
    if (routingInteraction && typeof routingInteraction["sys_id"] === "string") {
      await servicenow.attachInteractionSysIdToLiveAgentSession(
        {
          ticketId: args.ticketId,
          clientSessionId,
          liveAgentData,
          interactionSysId: routingInteraction["sys_id"],
        },
        ctx,
      );
    }

    return {
      clientSessionId,
      assignedAgent: routingInteraction?.["assigned_to"] ?? null,
      assignmentGroup: routingInteraction?.["assignment_group"] ?? null,
    };
  },
});
While the session is active:
  • User → agent: any new message the user sends in their original surface fans out to the ServiceNow agent automatically as an AGENT_CHAT turn - no extra workflow code required; Serval’s ticket-channel egress handles it.
  • Agent → user: ServiceNow’s Outbound REST Message posts the agent’s typed reply to the Inbound Webhook URL configured in Step 3. Serval ingresses it as a comment on the bridged ticket (attributed to the agent), which surfaces back in the user’s original surface.

Step 7: Optionally end the session symmetrically

When the Serval ticket auto-resolves or the user abandons, call endLiveAgentSession with the clientSessionId saved from the START workflow output, the same user, a short reason such as ticket_resolved, and the same channelId (and contextVariables, if your routing rule expects them on every turn). This is best-effort - the ServiceNow side eventually times the conversation out either way, but an explicit END_CONVERSATION cleans up the interaction record faster and gives the agent a clean “user ended chat” signal.

Step 8: Real-time translation (optional)

Live agents and end users frequently speak different languages - a Spanish-speaking employee opens a chat in Slack, the routed IT queue is staffed by English-speaking agents, and you want both sides to read their own language without the agent having to copy/paste into a translation tool. To enable bidirectional real-time translation for the lifetime of a conversation, pass two extra fields on startLiveAgentSession: userLanguage (a BCP-47 code for the requester, e.g. es) and agentLanguage (the code your live agents read and write, e.g. en). What changes after that:
  • User → agent. Every message the user types on the upstream surface is translated from userLanguage into agentLanguage before it lands as an AGENT_CHAT turn on ServiceNow.
  • Agent → user. Every reply the live agent posts is translated from agentLanguage into userLanguage before it’s persisted on the Serval ticket and fanned back out to the user’s surface.
Behavior worth knowing:
  • Both fields must be set. If either is omitted, or both share a primary subtag (e.g. en vs en-US), the conversation runs through verbatim - no translation is attempted.
  • URLs, code blocks, ticket numbers, and @mentions are preserved verbatim across the translation.
  • Translation failures fall back to the original text and log a warning. A passthrough turn is strictly better than a dropped turn on a live chat - the agent or user can ask the other side to rephrase.
  • Translation is handled by Serval automatically - there is nothing to configure on the ServiceNow side.
Detecting the requester’s language automatically (e.g. from the user’s Serval profile locale, or from the first inbound message) is up to the workflow author - pass whatever you’ve resolved into userLanguage. If userLanguage is unknown, no translation is attempted in either direction.
Need help? Contact support@serval.com for assistance with your ServiceNow integration - including custom routing-rule design, multi-bot Provider Application setups, interaction-field Business Rules, and real-time translation rollouts.