How to Migrate from HaloITSM to ServiceNow: Technical Guide
A complete technical guide to migrating from HaloITSM to ServiceNow, covering object mapping, API limits, CMDB splitting, and step-by-step migration architecture.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
What Is a HaloITSM to ServiceNow Migration?
A HaloITSM to ServiceNow migration is the process of extracting tickets (Incidents, Service Requests, Problems, Changes), assets, configuration items, users, agents, teams, clients, knowledge base articles, actions (ticket history), and attachments from HaloITSM and loading them into ServiceNow as incidents, requested items (RITMs), problems, change requests, CMDB CIs, assets, users, groups, companies, knowledge articles, journal entries, and attachments.
The transport is straightforward. The hard part is translating HaloITSM's flexible ticket and asset model into ServiceNow's more segmented task, request, CMDB, and asset model. (usehalo.com)
Why enterprise teams move from HaloITSM to ServiceNow
The drivers are platform-specific, not generic dissatisfaction:
- Scale and multi-instance governance. ServiceNow supports domain separation and multi-instance architectures that HaloITSM does not offer natively, making it the default choice for organizations managing 500+ agents across multiple business units.
- ITOM and CMDB depth. ServiceNow's Common Service Data Model (CSDM), Discovery, and Service Mapping provide a level of CMDB automation and service topology that HaloITSM requires third-party tools like Lansweeper to replicate.
- Platform consolidation. Organizations standardizing on ServiceNow for ITSM, ITOM, HR Service Delivery, and SecOps often retire HaloITSM to reduce platform sprawl.
For a broader comparison of ServiceNow's architecture against alternatives, see our ServiceNow vs Jira Service Management Architecture Guide.
What makes this migration non-trivial
Two architectural differences create the majority of the mapping complexity:
-
Asset/CI split. HaloITSM stores assets and configuration items in a unified model. ServiceNow separates financial/lifecycle tracking (Assets in
alm_assetand subclasses likealm_hardware) from technical/relationship tracking (CIs incmdb_ciand subclasses likecmdb_ci_computer,cmdb_ci_server). Every HaloITSM asset record must be analyzed and potentially written to two ServiceNow tables with a reference link between them. (usehalo.com) -
Ticket history model. HaloITSM stores all ticket activity — notes, status changes, email replies, time entries — as "Actions" attached to a ticket. ServiceNow uses journal fields (
sys_journal_field) for work notes and comments, plus separate tables for time cards (time_card) and state transitions (sys_audit). This is not a 1:1 field copy.
HaloITSM to ServiceNow Object and Field Mapping
The core mapping is not ticket-to-ticket. It is HaloITSM object to ServiceNow table set. Decide the destination table before you load a single row, or you will create history that cannot be reported or automated correctly later. (usehalo.com)
| HaloITSM Object | ServiceNow Object | Notes / Caveats |
|---|---|---|
| Tickets (type: Incident) | Incident (incident) |
Map HaloITSM ticket_type_id to filter incidents from other ticket types |
| Tickets (type: Service Request) | Requested Item (sc_req_item) |
Requires a parent Request (sc_request) record in ServiceNow |
| Tickets (type: Problem) | Problem (problem) |
Linked incidents must be migrated first to preserve relationships |
| Tickets (type: Change) | Change Request (change_request) |
CAB approval history and implementation plans need manual rebuild |
| Actions (on tickets) | Journal Fields (sys_journal_field) |
Public actions → "Additional Comments"; private actions → "Work Notes" |
| Assets | Asset (alm_hardware) + CI (cmdb_ci_*) |
Single HaloITSM record splits into two linked ServiceNow records |
| Configuration Items | CMDB CI (cmdb_ci_* subclass) |
Map to appropriate CI class (Server, Computer, Application, etc.) |
| CI Relationships | CI Relationship (cmdb_rel_ci) |
HaloITSM uses flat parent/child links; ServiceNow uses typed relationship classes |
| Users (end users) | User (sys_user) |
Match on email address; map HaloITSM site_id to ServiceNow Location |
| Agents | User (sys_user) with ITIL role |
Assign appropriate ServiceNow roles during import |
| Teams | Group (sys_user_group) |
Map team membership and assignment rules |
| Clients | Company (core_company) |
HaloITSM Clients map to ServiceNow Companies; required for domain separation |
| Sites | Location (cmn_location) |
Nested site hierarchy must be flattened or mapped to ServiceNow location tree |
| Knowledge Base Articles | Knowledge Article (kb_knowledge) |
Map to correct Knowledge Base and Category; re-host inline images |
| Attachments | Attachment (sys_attachment) |
Must use ServiceNow Attachment API; subject to glide.rest.max_content_length limit |
| Contracts | Contract (ast_contract) |
Requires ServiceNow ITAM licensing |
| SLA Configurations | SLA Definition (contract_sla) |
Cannot be exported from HaloITSM API; must be rebuilt |
| Business Rules / Workflows | Flow Designer / Business Rules | Cannot be exported; must be rebuilt from documentation |
Field-level mapping considerations
- Picklist/enum values. HaloITSM uses numeric IDs for priority, status, and category. ServiceNow uses integer-based priority (1–5) and string-based state values. Build a lookup table mapping each HaloITSM enum to its ServiceNow equivalent before loading any data. If a HaloITSM status value does not exist in the ServiceNow state dictionary, the Import Set API will reject the record or insert a blank value — check the
sys_import_set_rowstaging table for records inerrorstate. - Date/time formats. HaloITSM returns ISO 8601 timestamps (e.g.,
2024-03-15T14:30:00Z). ServiceNow expects its internal format (yyyy-MM-dd HH:mm:ss) in the instance's configured timezone. Convert during the transform phase using anonBeforetransform script, not inside a late-stage fix script. - Reference fields. ServiceNow relies on Sys IDs (32-character GUIDs) to link records. Resolve requester, assignee, company, group, and CI references from a crosswalk table, not from display names at insert time. Display name lookups are fragile and fail silently when duplicates exist.
- Custom fields. HaloITSM custom fields come through the API as part of the ticket JSON payload (e.g.,
customfieldsarray withid,name, andvalueproperties). These must be mapped to ServiceNow custom fields (u_prefix) or dictionary entries created on the target table before import. - HTML content. HaloITSM Actions and Knowledge Articles contain HTML. ServiceNow journal fields accept HTML, but Knowledge Articles require content compatible with the ServiceNow CMS editor. Inline images referencing HaloITSM URLs (e.g.,
<img src="https://yourdomain.haloservicedesk.com/...">) must be extracted, uploaded to ServiceNow's attachment store, and have theirsrcattributes rewritten. - Source IDs. Keep a persistent external key such as
u_halo_idor a separate crosswalk table for every migrated object. You will need this for delta syncs, post-migration troubleshooting, and audit trail continuity.
What has no clean equivalent on ServiceNow
- HaloITSM Opportunities and Quotes. These are PSA/CRM objects with no ITSM equivalent in ServiceNow. If needed, they require ServiceNow custom tables or a separate CRM system.
- HaloITSM "Actions" as a unified activity stream. ServiceNow splits activity across journal fields (
sys_journal_field), audit history (sys_audit), time cards (time_card), and email logs (sys_email). A single HaloITSM Action may need to be written to multiple ServiceNow records depending on itsaction_type_id. - Inline workflow automation. HaloITSM's built-in rules engine does not export via the API. Every automation must be re-implemented in ServiceNow Flow Designer or Business Rules. (usehalo.com)
Migration Approaches: Import Sets vs API vs Managed Service
For most real migrations, HaloITSM API extraction plus ServiceNow Import Sets is the right default. CSV is acceptable only for small and simple scopes. Direct Table API loading works for lookups and repair jobs, not as the main bulk-history path. (servicenow.com)
| Approach | How It Works | Best For | Complexity | Key Risk |
|---|---|---|---|---|
| CSV Export + Import Sets | Export CSVs from HaloITSM UI, upload to ServiceNow Import Set tables, transform via Transform Maps | Small datasets under 5K records | Low | No attachment handling; loses ticket threading and CMDB relationships |
| API-to-API (Custom Scripts) | Extract via HaloITSM REST API, transform in code, load via ServiceNow Import Set API | Mid-size migrations with dev resources | High | Must handle pagination, OAuth token refresh, and ServiceNow semaphore throttling |
| Integration Platform (ZigiWave, Exalate) | Bidirectional sync tools designed for ITSM coexistence | Ongoing sync during parallel-run periods | Medium | Not designed for high-volume historical data loads; hits ServiceNow concurrency limits quickly |
| Managed Migration Service | Dedicated team builds extraction, transformation, and load pipelines with validation | Enterprise migrations, complex CMDB, compliance requirements | Low (for client) | Cost; requires vendor due diligence |
ZigiWave and Exalate are useful when both desks must coexist for a period. They are less compelling as the sole mechanism for a full historical backfill because they still operate inside ServiceNow's concurrency envelope. (zigiwave.com)
Clear picks by scenario:
- Small business, under 5K tickets, no CMDB: CSV Export + Import Sets
- Mid-size, 5K–50K tickets, minimal CMDB: API-to-API with a dedicated developer
- Enterprise, 50K+ tickets, deep CMDB, compliance: Managed migration service
- Parallel run / coexistence period: Integration platform for active tickets only, combined with API migration for historical data
HaloITSM API Extraction Architecture
The HaloITSM REST API is the primary extraction method for any migration beyond trivial volumes. (haloitsm.com)
- Authentication: OAuth 2.0 client credentials flow. Request tokens from the authorization endpoint (e.g.,
https://yourdomain.haloservicedesk.com/auth/token). Tokens expire (typically after 3600 seconds) and must be refreshed programmatically. Cache the token and refresh 60 seconds before expiry to avoid mid-batch failures. - Endpoints:
/api/Tickets,/api/Assets,/api/Users,/api/Agents,/api/Clients,/api/Teams,/api/KnowledgeBase,/api/Attachments,/api/Actions,/api/Sites,/api/Contracts - Pagination: Uses
page_noandpage_sizequery parameters. Always paginate large queries to avoid timeouts. Set your page size to 100–500 records and iterate sequentially. The response includes arecord_countfield — use it to calculate total pages and detect premature termination. (powershellgallery.com) - Filtering: Supports query parameters for filtering by ticket type (
tickettype_id), status, date range (dateoccurred_start,dateoccurred_end), and custom field filters likecf_266, which is useful for delta pulls and UAT subsets. - Rate limits on the extraction side: HaloITSM cloud-hosted instances enforce API rate limits that vary by subscription tier. On standard plans, expect approximately 100 requests per minute before receiving HTTP 429 responses. For environments with 50K+ tickets, extraction alone can take 8–12 hours when including per-ticket Action calls. Factor this into your timeline and implement retry logic with 30-second backoff on the extraction side, not just the ServiceNow ingestion side.
# Paginated extraction of HaloITSM tickets with token refresh and retry
import requests
import time
def get_token(base_url, client_id, client_secret):
response = requests.post(
f"{base_url}/auth/token",
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "all"
}
)
token_data = response.json()
return token_data["access_token"], time.time() + token_data.get("expires_in", 3600) - 60
def extract_halo_tickets(base_url, token, token_expiry, client_id, client_secret, ticket_type_id, page_size=100):
page = 1
all_tickets = []
while True:
# Refresh token if near expiry
if time.time() >= token_expiry:
token, token_expiry = get_token(base_url, client_id, client_secret)
response = requests.get(
f"{base_url}/api/Tickets",
headers={"Authorization": f"Bearer {token}"},
params={
"page_no": page,
"page_size": page_size,
"tickettype_id": ticket_type_id
}
)
if response.status_code == 429:
time.sleep(30)
continue
data = response.json()
tickets = data.get("tickets", [])
if not tickets:
break
all_tickets.extend(tickets)
page += 1
return all_ticketsFor each ticket, you also need to extract Actions (the full conversation and activity history) by calling /api/Actions?ticket_id={id}. This is a per-ticket call. Extracting 50,000 tickets with an average of 8 actions each requires roughly 50,000 additional API calls. At 100 requests/minute with rate limiting, that is approximately 8.3 hours of extraction time for Actions alone. Plan for this in your timeline and consider parallelizing across ticket ID ranges if your HaloITSM tier permits it. (usehalo.com)
HaloITSM API edge cases
- Deleted records. The HaloITSM API does not return soft-deleted tickets by default. If your compliance requirements include deleted ticket history, you must set the
includeDeletedparameter where available, or export directly from the database. - Large custom field payloads. Tickets with 20+ custom fields return nested JSON objects in the
customfieldsarray. Parse these byidrather thannamebecause field names can contain special characters and may not be unique across field groups. - Attachment download. Attachments must be downloaded individually via
/api/Attachments/{id}. The API returns the binary file content. For large environments (10,000+ attachments), download attachments to local/cloud storage first, then upload to ServiceNow in a separate phase. This decouples extraction from ingestion and prevents a single HaloITSM timeout from stalling the ServiceNow load.
ServiceNow Ingestion Limits and Version Considerations
ServiceNow offers three primary inbound APIs for migration loads. Each has real constraints that drive migration architecture decisions. The behavior described here applies to ServiceNow Washington DC (March 2024) and Xanadu (September 2024) releases. Earlier releases (Vancouver, Utah) may have different default semaphore configurations and Import Set API behavior. (servicenow.com)
Import Set API (insertMultiple)
- ServiceNow recommends starting at 200 records per request and increasing gradually while monitoring instance performance via the
sys_semaphoretable and transaction logs. - Staging tables must extend
sys_import_set_row. - Transform Maps (
sys_transform_map) and Field Maps (sys_transform_entry) must be configured before loading. - Supports
run_afterwhen one import must wait for another. - Synchronous imports block until all records are processed, which can timeout on large batches. For batches over 500 records, consider using asynchronous import with the
run_afterparameter to chain dependent loads.
Table API
- Inserts one record per REST call.
- Simpler for small volumes but impractical for bulk loads over 10,000 records.
- Useful for reference data lookups and post-load repair updates.
Attachment API
- Required for file attachments. Use the endpoint
/api/now/attachment/filewith theContent-Typeheader set to the file's MIME type andtable_name/table_sys_idparameters to link the attachment to its parent record. - The REST payload limit (
glide.rest.max_content_length) defaults to 10MB per request, with a configurable maximum of 25MB. - Attachments exceeding the configured limit must be compressed before upload or handled by adjusting this property via System Properties > REST API.
Semaphore-based concurrency
- ServiceNow uses semaphores to manage concurrent API transactions. A typical configuration allows 16 active + 150 queued = 166 concurrent transactions per node.
- The 167th concurrent request returns an HTTP 429 error with the response header
Retry-Afterindicating the recommended wait time. - Fast transactions (under 200ms) cycle through semaphores quickly. Import Set transforms with business rules hold semaphores longer and fill the queue faster.
- Monitor semaphore usage in real time via the
sys_semaphoretable:GET /api/now/table/sys_semaphore?sysparm_query=nameSTARTSWITHrest. (servicenow.com)
DIY migration scripts that fire concurrent API calls without throttling will hit 429 errors within minutes on most ServiceNow instances. Implement exponential backoff with a minimum 2-second retry delay and limit concurrent connections to 4–6 threads. Log every 429 response with timestamp and batch ID to identify saturation patterns.
Throughput estimates:
- Table API, single-threaded: ~1,000–2,000 records per hour (depends on field count and business rule complexity)
- Import Set API at 200 records per batch, 4 threads: ~5,000–8,000 records per hour
- With attachments: reduce estimates by 40–60% depending on average file size
Business rules and workflows trigger on insert, so these are realistic numbers after accounting for database commit overhead. To improve throughput during migration windows, temporarily disable non-essential business rules on target tables (set active to false on rules that send notifications, trigger integrations, or update related records). Re-enable immediately after the load completes.
For a deeper look at ServiceNow ingestion bottlenecks, see 10 ServiceNow Data Migration Challenges and How to Overcome Them.
Step-by-Step Migration Process
The order of operations matters more than the code language. Loading tickets before users exist in ServiceNow creates orphaned records. Loading CIs before their parent CIs exist breaks the relationship tree. (servicenow.com)
Phase 0: Freeze the source configuration
Stop new field creation and workflow edits in HaloITSM during the mapping and extraction phase. If the ServiceNow team adds a new mandatory field mid-migration, your scripts will fail on the next load. Implement a strict change freeze on both platform schemas during the migration window. Document every custom field, picklist value, and workflow rule in HaloITSM before extraction begins — this documentation is your rebuild reference for ServiceNow automations.
Phase 1: Extract and load foundation data
- Extract and load Companies (HaloITSM Clients → ServiceNow
core_company) - Extract and load Locations (HaloITSM Sites → ServiceNow
cmn_location) - Extract and load Users (HaloITSM Users + Agents → ServiceNow
sys_user), linking to Companies and Locations. Agents become users with ITIL roles. - Extract and load Groups (HaloITSM Teams → ServiceNow
sys_user_group), then assign group membership viasys_user_grmember
Store every returned ServiceNow Sys ID in a crosswalk table alongside the source HaloITSM ID and table name. You will use this to resolve reference fields in every subsequent phase.
Clean the data before loading: remove duplicate email addresses and inactive accounts. ServiceNow requires unique email addresses for inbound email actions to map correctly to the caller field. Append a prefix (e.g., dup_) to duplicate emails in HaloITSM or merge the records before extraction.
Phase 2: Migrate CMDB and assets
- Extract HaloITSM Assets and CIs
- Split each record: write financial/lifecycle data (purchase date, cost, vendor, warranty, depreciation) to
alm_hardwareor the appropriate asset subclass. Write technical/relationship data (hostname, IP address, OS, installed software, service dependencies) to the correctcmdb_ci_*subclass based on the asset type. - Create the Asset-to-CI reference link using the
assetfield on the CI record - Load CI Relationships (
cmdb_rel_ci) after all CIs exist. Use a two-pass approach: first load all CIs, then load all relationships using the ServiceNow Sys IDs generated in step 6
For deep hierarchies (5+ levels), use a topological sort on the relationship graph before loading to ensure parent CIs always exist before their children reference them.
Configure IRE identification rules before loading. For each CI class you are importing, set up identification rules in ServiceNow's Identification and Reconciliation Engine using stable identifiers (serial number for hardware, hostname for servers, name + version for applications). Without these rules, re-running a migration batch will create duplicates instead of updating existing records.
Phase 3: Migrate tickets and history
- Load Incidents (HaloITSM tickets where
ticket_type_id= Incident), referencing the now-existing Users, Groups, and CIs - Load Service Requests — note that a single HaloITSM request may need to become both a parent
sc_requestand one or moresc_req_itemrecords - Load Problems, linking to related Incidents via
problem_idon the incident record - Load Change Requests
- For each ticket, load Actions as journal field entries (Work Notes or Additional Comments), preserving original timestamps using ServiceNow's
sys_created_onoverride
To override sys_created_on and sys_created_by on journal entries, the importing user needs elevated privileges. On Washington DC and Xanadu releases, this requires the import_admin role or a specific ACL grant on sys_journal_field.sys_created_on. Without this, all journal entries will show the import user and import timestamp, destroying the audit trail.
Disable ServiceNow notifications during the import by setting the glide.email.outbound.active property to false or by disabling specific email notifications on the target tables. This prevents spamming users with alerts for historical tickets. Re-enable immediately after the load.
Phase 4: Migrate attachments and knowledge
- Load Attachments via the ServiceNow Attachment API, linking each to its parent record using the Sys ID mapping from Phase 3. Keep attachments on a separate queue so a single bad file does not stall ticket history. Log every attachment by source filename, size, parent record Sys ID, and upload status.
- Load Knowledge Articles, re-hosting any inline images to ServiceNow's attachment store and rewriting
<img src>URLs in the article HTML body
Phase 5: Delta sync and validation
- Run a delta sync for records modified after the initial bulk load. Use HaloITSM's
dateoccurred_startfilter to extract only records created or modified since the bulk extraction timestamp. Migrate historical closed tickets first, then refresh open records near cutover. - Run record-count reconciliation across all object types
- Perform field-level spot checks on a 5–10% random sample
- Validate CMDB relationship integrity
- Verify attachment accessibility
Transform Map Configuration
Transform Maps are the critical bridge between your staging table and the target ServiceNow table. Configure them before loading any data:
- Create a staging table extending
sys_import_set_rowwith columns matching your source data structure (e.g.,u_halo_incident_stgwith fieldsu_halo_ticket_id,u_summary,u_caller_email,u_priority,u_status,u_team_name,u_ci_halo_id). - Create a Transform Map linking the staging table to the target table (e.g.,
incident). - Configure Field Maps (
sys_transform_entry) for each source-to-target field pair. For reference fields, use coalesce on a unique identifier — for example, coalesceu_caller_emailagainstsys_user.emailto resolve thecaller_idreference. - Set a coalesce field on the Transform Map to enable idempotent loads. Coalesce on
u_halo_ticket_idmapped to a custom fieldu_halo_idon the target table. This ensures re-running the import updates existing records rather than creating duplicates. - Add onBefore transform scripts for data conversions that Field Maps cannot handle:
// onBefore transform script: convert HaloITSM priority and datetime
(function runTransformScript(source, target, map, log, isUpdate) {
// Priority mapping: HaloITSM numeric → ServiceNow 1-5
var priorityMap = {1: '1', 2: '2', 3: '3', 4: '4'};
target.priority = priorityMap[source.u_priority] || '4';
// Status mapping: HaloITSM status ID → ServiceNow state
var stateMap = {1: '1', 2: '2', 8: '6', 9: '7'}; // New→New, Open→In Progress, Closed→Resolved, etc.
target.state = stateMap[source.u_status] || '1';
// DateTime conversion: ISO 8601 → ServiceNow internal format
if (source.u_dateoccurred) {
var gdt = new GlideDateTime();
gdt.setValue(source.u_dateoccurred.replace('T', ' ').replace('Z', ''));
target.opened_at = gdt.getDisplayValue();
}
// Resolve CI reference from crosswalk
if (source.u_ci_halo_id) {
var ciGr = new GlideRecord('cmdb_ci');
ciGr.addQuery('u_halo_id', source.u_ci_halo_id);
ciGr.query();
if (ciGr.next()) {
target.cmdb_ci = ciGr.sys_id;
}
}
})(source, target, map, log, isUpdate);- Add onAfter transform scripts to update the crosswalk table with the newly created Sys ID for downstream loads (e.g., storing the incident Sys ID for subsequent Action/journal entry loading).
# Loading a ticket to ServiceNow via Table API with retry logic
import requests
import time
import logging
logger = logging.getLogger(__name__)
def load_incident(sn_instance, auth, incident_data, id_map, max_retries=3):
payload = {
"short_description": incident_data["summary"],
"description": incident_data["details"],
"caller_id": id_map["users"].get(incident_data["user_id"]),
"assignment_group": id_map["groups"].get(incident_data["team_id"]),
"priority": map_priority(incident_data["priority_id"]),
"state": map_state(incident_data["status_id"]),
"cmdb_ci": id_map["cis"].get(incident_data["asset_id"]),
"opened_at": convert_datetime(incident_data["dateoccurred"]),
"u_halo_id": str(incident_data["id"])
}
for attempt in range(max_retries):
response = requests.post(
f"https://{sn_instance}.service-now.com/api/now/table/incident",
auth=auth,
json=payload,
headers={"Content-Type": "application/json"}
)
if response.status_code == 201:
result = response.json()
return result["result"]["sys_id"]
elif response.status_code == 429:
wait = int(response.headers.get("Retry-After", 2 ** (attempt + 1)))
logger.warning(f"429 received, waiting {wait}s (attempt {attempt+1})")
time.sleep(wait)
else:
logger.error(f"Failed: {response.status_code} - {response.text}")
break
return None# Bulk loading via ServiceNow Import Set API
curl "https://instance.service-now.com/api/now/import/u_halo_incident_stg/insertMultiple" \
--request POST \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--user "admin:password" \
--data '{
"records": [
{
"u_halo_ticket_id": "12345",
"u_ticket_kind": "incident",
"u_summary": "VPN access fails",
"u_caller_email": "jane@example.com",
"u_priority": "2",
"u_status": "1",
"u_dateoccurred": "2024-03-15T14:30:00Z"
}
]
}'Make every batch idempotent. Log source IDs, batch IDs, import set IDs, and retry counts. Use run_after when dependent loads must stay in sequence.
How Long Does a HaloITSM to ServiceNow Migration Take?
| Phase | Small (under 10K records) | Mid (10K–100K records) | Enterprise (100K+) |
|---|---|---|---|
| Discovery and mapping | 3–5 days | 5–7 days | 7–14 days |
| Script development and testing | 5–7 days | 10–15 days | 15–25 days |
| Test migration (dry run) | 2–3 days | 3–5 days | 5–7 days |
| Data cleanup and fixes | 2–3 days | 3–5 days | 5–10 days |
| Production migration | 1–2 days | 2–4 days | 3–7 days |
| Validation and UAT | 2–3 days | 3–5 days | 5–7 days |
| Total | 3–4 weeks | 5–7 weeks | 8–12 weeks |
The largest time sink is usually script development and the iterative test-fix-retest cycle, not the actual data transfer.
Migration strategies
- Big-bang: Migrate everything over a weekend. Works for datasets under 50K records with a team that has completed at least one successful dry run.
- Phased: Migrate CMDB and historical tickets first, then active tickets with a delta sync. Preferred for enterprise volumes because it reduces the cutover window to hours rather than days.
- Parallel run: Keep both systems active during a 1–2 week overlap. Requires bidirectional sync or manual dual-entry. Higher operational cost but lower risk. Typically adds $15K–30K in operational overhead for mid-size organizations due to agent dual-entry and sync tooling costs.
Risk register
| Risk | Likelihood | Mitigation |
|---|---|---|
| Broken CMDB relationships | High | Two-pass CI load; validate relationship counts post-migration |
| Orphaned ticket references | Medium | Enforce load order: users and groups before tickets |
| ServiceNow 429 throttling | High | Limit concurrent threads to 4–6; implement exponential backoff |
| Attachment data loss (10MB limit) | Medium | Use Attachment API with adjusted glide.rest.max_content_length |
| Stale data at cutover | Medium | Run a delta sync for records modified after the initial bulk load |
| Wrong ticket classification | Medium | Sample by ticket type and business area before production load |
| Duplicate CIs | High | Configure IRE identification rules before loading; preload identifiers |
| HaloITSM API rate limiting during extraction | Medium | Implement 30-second backoff; parallelize across ticket ID ranges |
Dependencies and rollback
Your primary dependency is the ServiceNow environment configuration. A strict change freeze on both platform schemas during the migration window prevents scripts from breaking mid-run.
Rollback procedure:
- Keep HaloITSM active in read-only mode for 30 days post go-live. Never decommission HaloITSM until validation is complete and stakeholders have signed off.
- If a partial load must be reversed on ServiceNow, identify all imported records by querying the
u_halo_idcustom field or thesys_import_setreference on each record. - Delete imported records in reverse dependency order: journal entries → attachments → tickets → CI relationships → CIs → assets → users → groups → companies. Use background scripts or scheduled jobs to bulk-delete, not the UI.
- Clean up staging tables: delete all
sys_import_set_rowrecords associated with the migration import sets. - If critical data loss is discovered post-cutover, re-extract from the source HaloITSM instance (which remains in read-only mode) and re-run the affected phases.
Preserving Ticket History in ServiceNow
HaloITSM Actions contain the full resolution history of every ticket: agent notes, customer replies, status transitions, and time entries. This history matters for compliance auditing, trend analysis, and SLA reporting.
To preserve it in ServiceNow:
- Map public Actions (where
action_ishtmlis true andwho_typeis "customer" or the action is flagged as public) to the Additional Comments (comments) journal field on the target ticket - Map private/internal Actions to the Work Notes (
work_notes) journal field - Override
sys_created_onandsys_created_byon each journal entry to preserve the original author and timestamp. This requires theimport_adminrole on Washington DC and Xanadu releases. Without this role, journal entries will show the import user's credentials and current timestamp. - Time entries should be mapped to ServiceNow Time Cards (
time_card) if the Time Recording plugin (com.snc.time_card) is active - Status transition Actions should be logged as Work Notes with a standardized prefix (e.g.,
[Status Change] New → In Progress) so they are searchable in ServiceNow
Always verify that journal entry timestamps appear in the correct order after migration. ServiceNow sorts journal entries by sys_created_on, so any timestamp conversion errors will scramble the visible conversation thread. Run this validation query on a sample: sys_journal_field.element_id={incident_sys_id}^ORDERBYsys_created_on and compare the sequence against the HaloITSM Action order.
Operational impacts for agents and end users
- Ticket continuity. Migrated tickets retain their full history but receive new ServiceNow ticket numbers (e.g., INC0012345). Store the original HaloITSM ticket ID in a custom field (
u_halo_id) on the ServiceNow record and maintain a cross-reference lookup table for the transition period. - SLA timers. Active SLAs cannot be migrated mid-timer because ServiceNow's
task_slatable calculates elapsed time from its ownsys_created_onandstart_timefields. Best practice is to close all active SLAs in HaloITSM and start fresh in ServiceNow, documenting the prior SLA performance in a separate report for continuity. - Downtime. A well-planned migration achieves zero end-user downtime by running the bulk load against a non-production ServiceNow instance, validating, then performing a final delta sync to production during a maintenance window of 2–4 hours.
- Agent communication. Communicate the cutover date to end users three weeks in advance. Provide agents with a mapping guide showing their old HaloITSM queue names alongside the corresponding ServiceNow assignment groups, and schedule a 1-hour walkthrough session the week before cutover.
For strategies on maintaining uptime during the switch, see our zero-downtime help desk data migration guide.
Edge Cases: CMDB Relationships, Attachments, and Duplicates
HaloITSM assets must split into ServiceNow CIs and assets
HaloITSM stores all asset information (serial number, purchase date, warranty, installed software, relationships) in a single asset record. ServiceNow separates this data into two linked records:
- Asset record (
alm_hardware): purchase date, cost, vendor, warranty, depreciation, contract references - CI record (
cmdb_ci_computer,cmdb_ci_server, etc.): hostname, IP address, OS, installed software, service dependencies
Your transform logic must read each HaloITSM asset, determine the correct ServiceNow CI class based on the asset type (use a lookup table mapping HaloITSM asset type IDs to ServiceNow CI class names), write the CI record, write the Asset record, and create the reference link between them using the asset field on the CI record.
Multi-level CMDB relationships
HaloITSM supports parent/child CI relationships and service dependency mapping. ServiceNow uses typed relationships in the cmdb_rel_ci table with a relationship type reference (cmdb_rel_type). Common ServiceNow relationship types include:
Runs on::Runs(sys_id: varies by instance)Depends on::Used byContains::Contained byMembers Of::Member of
You must map HaloITSM's generic relationship types to ServiceNow's predefined relationship types. Query cmdb_rel_type to get the Sys IDs for each relationship type in your target instance. If HaloITSM uses relationship types that have no ServiceNow equivalent, create custom relationship types before loading. If the mapping is not explicit, ServiceNow dependency views and Service Maps will not render correctly.
Load order matters: parent CIs must exist before child CIs can reference them. For deep hierarchies (5+ levels), use a topological sort on the relationship graph before loading. (servicenow.com)
Attachment size limits
ServiceNow's REST API has a default maximum content length of 10MB per request, controlled by the glide.rest.max_content_length property. This property can be increased to a maximum of 25MB via System Properties > REST API.
Use the ServiceNow Attachment API (/api/now/attachment/file) for binary uploads rather than base64-encoding attachments into JSON payloads, which inflates file size by approximately 33% (a 7.5MB file becomes ~10MB in base64, potentially exceeding the limit).
For attachments exceeding 25MB (uncommon but possible with screenshots bundles or log files), consider storing them in a linked cloud storage location (e.g., Azure Blob, S3) and creating a URL reference on the ServiceNow record.
Duplicate detection
ServiceNow's Identification and Reconciliation Engine (IRE) can prevent duplicate CI creation when multiple data sources feed the CMDB. Configure IRE identification rules for each CI class before loading migration data. Common identifiers:
| CI Class | Recommended Identifier Fields |
|---|---|
cmdb_ci_computer |
serial_number, name |
cmdb_ci_server |
serial_number, name, ip_address |
cmdb_ci_appl |
name, version |
cmdb_ci_service |
name |
Import order directly affects data quality because IRE uses relationships and dependency rules to evaluate records. If you load CIs out of order, the engine may create duplicate records instead of matching to existing ones. (servicenow.com)
For ticket data, deduplicate on the HaloITSM side before migration. Common duplicate sources include test tickets, auto-generated alert tickets, and tickets created by both email and portal simultaneously. Query for tickets with identical summary, user_id, and dateoccurred within a 5-minute window.
Service requests require decomposition
A HaloITSM service request ticket may need to become both a parent sc_request and one or more sc_req_item records in ServiceNow. This is not a 1:1 copy. Analyze your HaloITSM request types and decide whether each maps to a single RITM or needs decomposition before loading. The load sequence is: create sc_request first, store its Sys ID, then create sc_req_item records referencing the parent request via the request field.
Domain separation mapping
If your ServiceNow instance uses domain separation (common in MSP and multi-business-unit environments), you must map each HaloITSM Client to both a ServiceNow Company (core_company) and a ServiceNow Domain (domain). Set the sys_domain field on every imported record to ensure data visibility boundaries are correct. Incorrect domain assignment causes records to be invisible to the wrong user population — a critical issue that often surfaces only during UAT when agents report "missing" data.
Validation and Testing
Never trust a successful HTTP 200 response. A record might insert successfully but contain truncated data or incorrectly mapped references.
Record-count reconciliation
Compare total record counts per object type between HaloITSM and ServiceNow. Break this down by state, priority, and month. A mismatch greater than 0.1% warrants investigation.
Validation queries for ServiceNow (GlideRecord / encoded query):
// Count incidents by priority — run in Scripts - Background
var counts = {};
var gr = new GlideRecord('incident');
gr.addQuery('u_halo_id', '!=', ''); // Only migrated records
gr.query();
while (gr.next()) {
var p = gr.priority.toString();
counts[p] = (counts[p] || 0) + 1;
}
gs.info(JSON.stringify(counts));// Encoded query to find orphaned incident references (missing caller)
incident?sysparm_query=u_halo_id!=^caller_idISEMPTY
// Find CIs with missing relationships (had relationships in HaloITSM but none in ServiceNow)
cmdb_ci?sysparm_query=u_halo_id!=^sys_idNOT INcmdb_rel_ci.parent^sys_idNOT INcmdb_rel_ci.child
Field-level validation
Randomly sample 5–10% of migrated records and compare field values against the HaloITSM source. Focus on:
- Priority and status mappings
- User and group assignments
- CI references on tickets
- Attachment counts per ticket
- Journal entry order and timestamps
- Custom field values (especially picklist mappings)
CMDB relationship validation
For every CI that had relationships in HaloITSM, verify the equivalent relationships exist in ServiceNow. Run a query on cmdb_rel_ci grouped by parent CI and compare counts:
// Count relationships per CI and compare against HaloITSM source counts
var relCounts = {};
var gr = new GlideRecord('cmdb_rel_ci');
gr.query();
while (gr.next()) {
var parentId = gr.parent.u_halo_id.toString();
if (parentId) {
relCounts[parentId] = (relCounts[parentId] || 0) + 1;
}
}
gs.info(JSON.stringify(relCounts));Validate from exported relationship data, not from UI screenshots — HaloITSM can suppress full dependency loading in some large asset views.
User acceptance testing
Have 3–5 agents work in the migrated ServiceNow instance for 2–3 days before go-live. Provide lead agents with a checklist of specific scenarios:
- Search for known tickets by description keyword
- Verify attached files open correctly and are not corrupted (check file size matches source)
- Confirm that CMDB lookups on incidents return the expected CIs
- Verify VIP user flags migrated to
sys_user.vipfield - Check that knowledge base article inline images load without requiring a HaloITSM login
- Test open incidents with linked CIs — verify the CI popover shows correct data
- Test change tickets with approval history (verify approval records exist in
sysapproval_approver) - Test multi-attachment tickets and request tickets that became both
sc_requestandsc_req_item - Verify SLA definitions fire correctly on new test tickets created in ServiceNow
For a wider scoping framework on what data to move and what to leave behind, see our Help Desk Data Migration Playbook.
Security and Compliance During Migration
Data in transit
- Use HTTPS (TLS 1.2+) for all API calls to both HaloITSM and ServiceNow. Both platforms enforce this by default on cloud-hosted instances.
- If your transformation layer runs on intermediate infrastructure (e.g., an EC2 instance or Azure VM), ensure the machine's disk is encrypted at rest (AES-256) and that extracted data is purged after successful load and validation.
- For environments handling PII, PHI, or data subject to GDPR, document the data flow path from HaloITSM → transformation layer → ServiceNow as part of your Data Processing Impact Assessment (DPIA).
Audit trail continuity
- The crosswalk table (HaloITSM ID → ServiceNow Sys ID) serves as the chain of custody record. Retain it for the duration of your compliance retention period (typically 7 years for SOX, 6 years for GDPR).
- Preserve original
sys_created_ontimestamps on all journal entries to maintain the audit timeline. Failure to do this breaks SOX audit trail requirements for change management tickets. - Export a pre-migration snapshot of HaloITSM record counts, field checksums, and relationship counts. Store this alongside the post-migration ServiceNow validation report as evidence of migration completeness.
Service account security
- Create a dedicated ServiceNow service account for the migration with only the required roles (
import_admin,itil,asset,cmdb_write). Do not use a personal admin account. - Rotate the service account password and the HaloITSM OAuth client secret immediately after migration completes.
- Restrict the service account's access via IP address ACLs if your ServiceNow instance supports it.
Estimated Migration Costs
Neither HaloITSM nor ServiceNow charges a per-record migration fee, but actual costs depend on the approach:
| Cost Category | DIY (In-House) | Managed Service |
|---|---|---|
| Developer time (5K–50K records) | 160–280 hours at internal rate | Included in service fee |
| ServiceNow sub-production instance | Typically included with enterprise license | Same |
| Integration platform licensing (if used) | $500–2,000/month for ZigiWave/Exalate | N/A |
| Managed service fee (5K–50K records) | N/A | $10K–40K typical range |
| Managed service fee (100K+ records, complex CMDB) | N/A | $40K–100K+ depending on scope |
| Parallel-run operational overhead | $15K–30K for 2-week overlap | Similar |
| HaloITSM contract termination | Varies by contract terms | Same |
The hidden cost of DIY is not the initial build. It is the 2–3 rounds of re-migration after discovering broken CI relationships, missing journal entries, or incorrectly mapped picklist values. Each re-run burns 40–80 additional engineering hours and extends the parallel-run period.
Build In-House vs. Use a Managed Service
Build in-house when:
- Total dataset is under 10,000 tickets with fewer than 500 assets
- No CMDB relationships need to be preserved
- Your team has at least one developer experienced with both the HaloITSM API and ServiceNow Import Sets/Transform Maps
- You have 4–6 weeks of dedicated engineering time available
- No compliance audit trail requirements beyond basic record preservation
Use a managed service when:
- Dataset exceeds 50,000 records across ticket types
- CMDB has multi-level CI relationships that must be preserved
- Compliance requirements (SOX, ITIL audit trails, GDPR) demand verified data integrity and chain of custody documentation
- Your engineering team is already at capacity and pulling developers off product work for 6–8 weeks is not feasible
- You need weekend cutover, delta sync, or high-volume attachment handling (10,000+ files)
Frequently Asked Questions
How long does a HaloITSM to ServiceNow migration take?
A HaloITSM to ServiceNow migration typically takes 3 to 4 weeks for small environments with under 10,000 records, 5 to 7 weeks for mid-size datasets (10K–100K records), and 8 to 12 weeks for enterprise migrations with complex CMDB relationships. The largest time sink is usually script development and the iterative test-fix-retest cycle, not the actual data transfer.
What data cannot be migrated from HaloITSM to ServiceNow?
HaloITSM Business Rules, workflow automations, SLA timer configurations, report definitions, and dashboard layouts cannot be exported via the API and must be rebuilt manually in ServiceNow. PSA objects like Opportunities and Quotes have no native ITSM equivalent in ServiceNow. Data moves — process logic is rebuilt because the two platforms model tickets, requests, assets, and relationships differently. (usehalo.com)
Can I migrate HaloITSM to ServiceNow without losing CMDB data?
Yes, but it requires careful handling. HaloITSM's unified asset model must be split into separate ServiceNow Asset (alm_hardware) and CI (cmdb_ci_*) records with a linking reference via the asset field. CI relationships must be loaded after all CIs exist, using a two-pass approach. Configure IRE identification rules before loading to prevent duplicates. Data loss typically occurs when teams skip the relationship-loading phase or fail to map HaloITSM asset types to the correct ServiceNow CI classes.
Is there a native HaloITSM to ServiceNow migration tool?
No. Neither HaloITSM nor ServiceNow offers a built-in migration tool for this specific path. ServiceNow's Import Sets provide a generic data loading framework, but all extraction from HaloITSM and data transformation must be handled externally via the HaloITSM REST API and custom scripts or a managed migration service. (servicenow.com)
Does ServiceNow throttle API requests during migration?
ServiceNow uses semaphore-based concurrency management rather than a fixed rate limit. A typical instance allows 16 active and 150 queued concurrent transactions per node. When the queue is full, the 167th request receives an HTTP 429 error with a Retry-After header. Migration scripts must implement retry logic with exponential backoff and limit parallel threads to 4–6 to avoid saturating the instance. Monitor semaphore usage via the sys_semaphore table during the migration window. (servicenow.com)
Does HaloITSM throttle API requests during extraction?
Yes. HaloITSM cloud-hosted instances enforce rate limits that vary by subscription tier. Standard plans typically allow approximately 100 requests per minute before returning HTTP 429 responses. For large environments (50K+ tickets), factor 8–12 hours of extraction time when including per-ticket Action calls. Implement retry logic with 30-second backoff on the extraction side.
Can HaloITSM and ServiceNow run in parallel during migration?
Yes. Many enterprise teams run a 1 to 2 week parallel period where both platforms are active. Integration platforms like ZigiWave or Exalate can sync active tickets bidirectionally during this period. Historical data is migrated separately via API scripts. The parallel-run approach reduces risk but increases operational overhead (typically $15K–30K for mid-size organizations) because agents must work in two systems.
How do I handle PII/PHI data during the migration?
Use HTTPS (TLS 1.2+) for all API calls. If your transformation layer runs on intermediate infrastructure, encrypt the disk at rest (AES-256) and purge extracted data after successful load and validation. Document the data flow path as part of a DPIA for GDPR compliance. Create a dedicated ServiceNow service account with minimum required roles and rotate credentials immediately after migration completes.
Frequently Asked Questions
- How long does a HaloITSM to ServiceNow migration take?
- A HaloITSM to ServiceNow migration typically takes 3 to 4 weeks for small environments with under 10,000 records, 5 to 7 weeks for mid-size datasets, and 8 to 12 weeks for enterprise migrations with complex CMDB relationships.
- What data cannot be migrated from HaloITSM to ServiceNow?
- HaloITSM Business Rules, workflow automations, SLA timer configurations, report definitions, and dashboard layouts cannot be exported via the API and must be rebuilt manually in ServiceNow. PSA objects like Opportunities and Quotes have no native ITSM equivalent in ServiceNow.
- Can I migrate HaloITSM to ServiceNow without losing CMDB data?
- Yes, but HaloITSM's unified asset model must be split into separate ServiceNow Asset and CI records with a linking reference. CI relationships must be loaded after all CIs exist using a two-pass approach to preserve integrity.
- Is there a native HaloITSM to ServiceNow migration tool?
- No. Neither HaloITSM nor ServiceNow offers a built-in migration tool for this path. ServiceNow Import Sets provide a generic loading framework, but extraction from HaloITSM and all transformation must be handled via the HaloITSM REST API and custom scripts.
- Does ServiceNow throttle API requests during migration?
- ServiceNow uses semaphore-based concurrency management. A typical instance allows 16 active and 150 queued concurrent transactions per node. When the queue is full, the 167th request receives an HTTP 429 error. Migration scripts must implement exponential backoff and limit parallel threads to 4-6.

