Launched:self-serve migrations intoSuperhuman Docs (Coda)
Try it now
01Agent-first
Runs where you already work
Plug it into Claude, ChatGPT or Cursor. Describe the move in plain English; the agent runs it.
02Engineer-led
Our production engine, unlocked
The pipeline our engineers use on managed enterprise migrations — the same code, now something you can drive yourself.
03Pricing
Try 10 pages free, then $1 a page
Credit-based, pay-as-you-go. No scoping call, no quote — sample it on your own docs before you spend anything.
04Sources
NotionSlabConfluenceSoonGoogle DocsSoon
Skip to content

Freshservice to HaloITSM Migration: Data Mapping, API Limits & Methods

Technical guide to migrating from Freshservice to HaloITSM — covering object-by-object data mapping, Freshservice API rate limits by plan, and methods that work at scale.

Roopendra Talekar Roopendra Talekar · · 21 min read
Freshservice to HaloITSM Migration: Data Mapping, API Limits & Methods
TALK TO AN ENGINEER

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

Freshservice to HaloITSM Migration: Data Mapping, API Limits & Methods

A Freshservice to HaloITSM migration moves tickets (incidents, service requests), problems, changes, assets, CMDB configuration items, agents, requesters, groups, knowledge base articles, conversations, and attachments out of Freshservice and into HaloITSM. Both platforms expose REST APIs that cover all core objects, so the transport itself is straightforward. The hard part is translating Freshservice's relatively flat ticket model into HaloITSM's granular ITIL ticket types, managing Freshservice's per-plan rate limits during extraction, and loading data into HaloITSM in the right order so referential integrity holds.

This guide covers the object-by-object data mapping, API constraints on both sides, field-level translation, required fields by HaloITSM ticket type, error handling, and the migration methods that work at scale.

For a broader comparison of how these platforms differ at an architectural level, see our Freshservice vs HaloITSM: Architecture, TCO, and Migration.

What data moves from Freshservice to HaloITSM?

Every migration-grade transfer covers more than tickets. Here is the full object map between the two platforms:

Freshservice object API endpoint HaloITSM target Notes
Tickets (Incidents) /api/v2/tickets Tickets (type: Incident) Map Freshservice type field value
Tickets (Service Requests) /api/v2/tickets Tickets (type: Service Request) Requested items need separate extraction
Problems /api/v2/problems Problems Separate ITIL object in both platforms
Changes /api/v2/changes Changes HaloITSM supports standard, normal, and emergency change types
Assets / CMDB CIs /api/v2/assets Assets / CMDB CIs Both platforms use a unified asset-CI model
CMDB relationships /api/v2/relationships (account-wide) or /api/v2/assets/[display_id]/relationships (one asset) CI relationships Remap IDs after assets load
Agents /api/v2/agents Agents Role mapping required
Requesters /api/v2/requesters Users Deduplicate by email before loading
Groups /api/v2/groups Teams Assignment routing may need reconfiguration
Departments /api/v2/departments Departments / Sites HaloITSM uses a Customer → Site → User hierarchy
Knowledge base articles /api/v2/solutions/articles Knowledge base articles Folder structure maps to KB categories
Conversations /api/v2/tickets/[id]/conversations Actions (ticket updates) Thread order must be preserved by created_at
Attachments Embedded in conversation objects Attachments Returned as attachment_url links — download during extraction
Service catalog items /api/v2/service_catalog/items Service catalogue items Use as an inventory; rebuild the catalog in HaloITSM

Where the data models diverge

Freshservice and HaloITSM both claim ITIL alignment, but their internal models differ in ways that directly affect mapping and load logic.

Ticket typing. Freshservice tickets are a single object with a type property distinguishing Incidents from Service Requests. Problems, Changes, and Releases are separate API resources. HaloITSM treats every ITIL process — incidents, service requests, changes, problems — as a distinct ticket type with its own required fields and workflow paths. Each Freshservice record must be routed to the correct HaloITSM ticket type, and the required fields for that type must be populated or the API rejects the write.

CMDB and asset model. Both platforms use a unified asset-CI record — an asset becomes a configuration item when a service depends on it. The mapping here is closer to 1:1 than in migrations to platforms like ServiceNow (which separates them). The main challenge is remapping CMDB relationships. Freshservice lists every relationship in the account at /api/v2/relationships, and each one references its primary and secondary items by Freshservice ID (api.freshservice.com). Every referenced asset, requester and agent must exist in HaloITSM first. Asset types, customers, and sites must also exist before you import asset records.

Conversation model. Freshservice stores replies and notes as "conversations" on a ticket. HaloITSM stores them as "actions" — an append-only history on the ticket record. The mapping is conceptually simple but operationally expensive: each ticket's conversations require a separate paginated API call to extract from Freshservice.

Custom fields. Freshservice supports custom fields on tickets, problems, changes, releases, and assets. The custom_fields JSON object acts as a flat key-value store. HaloITSM custom fields enforce data types at write time — push a string into a field typed as a date or number and the API rejects it. If a custom field is a dropdown in HaloITSM, the exact string value from Freshservice must exist in the HaloITSM custom field dictionary, or the payload is rejected. Map and validate every custom field type before loading.

What are the Freshservice API rate limits for migration?

Freshservice rate limits are account-wide, enforced per minute, and vary by plan. These limits are the primary throughput constraint during extraction.

Plan Overall limit (req/min) List All Tickets (req/min) List All Assets (req/min)
Starter 100 40 40
Growth 200 70 70
Pro 400 120 120
Enterprise 500 140 140

These limits apply to the entire account, irrespective of the number of agents or IP addresses making the calls (api.freshservice.com). Every integration, custom app, and script calling the API on the account draws from the same pool, and invalid requests count too. Exceeding the limit returns HTTP 429 with a Retry-After header giving the seconds to wait. Your extraction script must read this header and pause.

Freshservice authenticates with an API key over basic auth (key as username, "X" as password) (api.freshservice.com).

Warning

Freshservice's filter endpoint (/api/v2/tickets/filter) returns a fixed 30 records per page. For migration-scale extraction, use the standard list endpoint (/api/v2/tickets) with updated_since time windows instead. Always pass updated_since: without it, the list endpoint returns only tickets created in the past 30 days (api.freshservice.com).

Pagination rules

Freshservice v2 API uses page-number pagination with page (starting at 1) and per_page (maximum 100). The response includes a link header pointing to the next page, and the header is absent on the last page (api.freshservice.com). Always follow that header rather than guessing sequential pages — deleted records can cause pagination drift. An empty results array signals the last page.

import requests
import time
 
BASE_URL = "https://yourdomain.freshservice.com/api/v2"
API_KEY = "your_api_key"
 
def extract_tickets(updated_since):
    page = 1
    all_tickets = []
    while True:
        resp = requests.get(
            f"{BASE_URL}/tickets",
            auth=(API_KEY, "X"),
            params={"per_page": 100, "page": page, "updated_since": updated_since}
        )
        if resp.status_code == 429:
            wait = int(resp.headers.get("Retry-After", 60))
            time.sleep(wait)
            continue
        resp.raise_for_status()
        tickets = resp.json().get("tickets", [])
        if not tickets:
            break
        all_tickets.extend(tickets)
        page += 1
    return all_tickets

Practical throughput math

On a Pro plan (120 List Tickets calls/min, 100 records/page), you can extract roughly 12,000 ticket records per minute from the list endpoint alone. But each ticket's conversations require a separate call — and with 400 total requests/min shared across all operations, conversation extraction is where the bottleneck hits. A 10,000-ticket backlog means at least 10,000 additional API calls just for conversation history, one per ticket, plus one more for each extra page on long threads.

Freshservice offers rate limit add-on packs on Pro and Enterprise plans: Add-on Pack 1 raises the overall limit to 1,000 req/min, and Pack 2 to 2,000 req/min (api.freshservice.com). For large extractions, these add-ons can cut migration time significantly.

updated_since coverage across object types

The updated_since parameter is available on tickets but not uniformly supported across all Freshservice object types. For the standard list endpoint:

  • Tickets (/api/v2/tickets): updated_since supported — use for delta syncs
  • Problems (/api/v2/problems): no documented updated_since filter — paginate the full list and compare updated_at
  • Changes (/api/v2/changes): supports updated_since
  • Assets (/api/v2/assets): filter by updated_at using the filter query parameter, not updated_since
  • Requesters / Agents: no updated-since filter — paginate the full list and compare against a local snapshot

For assets specifically, use /api/v2/assets?filter="updated_at:>'YYYY-MM-DD'" (URL-encoded) to scope delta extractions. The asset filter works at date granularity, returns 30 records per page, and returns at most 40 pages (api.freshservice.com). Failing to account for per-object filter support differences causes delta syncs to miss changed asset records while correctly capturing ticket changes.

How does HaloITSM's API handle data import?

HaloITSM exposes a REST API secured with OAuth 2.0 (Client Credentials flow) that accepts and returns JSON only. Each HaloITSM instance publishes its own API reference as a Swagger page on the instance itself. Confirm endpoint names, authentication methods, and any instance-specific constraints there before building import scripts.

To set up API access:

  1. Navigate to Configuration → Integrations → HaloITSM API
  2. Click View Applications and create a new application
  3. Select Client ID and Secret (Services) as the authentication method
  4. Note your Resource Server, Authorization Server, and Tenant values
  5. Use these credentials to obtain a bearer token, then call endpoints like POST /api/Tickets or POST /api/Asset
Info

This guide does not quote a HaloITSM rate limit. Confirm any write limits for your own instance on its Swagger page or with Halo, then measure real throughput during the sample migration. On-premise instances are also bounded by the host server's capacity.

HaloITSM import order

HaloITSM enforces referential integrity at write time. Importing a ticket assigned to an agent who doesn't exist yet results in a rejected write or an orphaned record. Load data in this order:

  1. Customers and sites — HaloITSM uses a Customer → Site → User hierarchy. These must exist first.
  2. Agents and users — Must be linked to an existing customer/site.
  3. Teams — Map from Freshservice groups.
  4. Asset types — Must exist before importing assets.
  5. Assets and CMDB CIs — Require customer, site, and asset type to already exist.
  6. Tickets (incidents, service requests, problems, changes) — Reference agents, users, teams, and optionally assets.
  7. Actions (conversations) — Appended to existing tickets, preserving chronological order.
  8. Attachments — Uploaded and linked to the relevant ticket or action.
  9. Knowledge base articles — Independent of ticket data; can be loaded in parallel.

HaloITSM API error codes and common failure modes

Status codes and error text vary by HaloITSM version and configuration. Log the full response body for every failed write during the sample migration, and check expected responses on your instance's Swagger page. These failure modes come up most often in bulk loads:

Failure Cause Fix
Missing mandatory field A field the target ticket type requires is empty Check required fields per ticket type (see section below)
Invalid field value Type mismatch — string in a date/number field, or unrecognized dropdown value Validate and cast all custom field values before POST
Missing customer or agent Ticket or asset references a customer, site, or agent not yet imported Load in the order above
Expired token Bearer token reached the end of its lifetime mid-run Implement token refresh; re-authenticate and retry
Missing parent ticket Posting an action to a ticket that was never created Confirm ticket was successfully created before posting actions
Duplicate on re-run A retry creates the same record twice Check for the stored Freshservice ID before each create
Throttling or timeouts Write throughput too high for the instance Back off, honour Retry-After when present, reduce batch concurrency

Handling HTML bodies: Malformed tags, unescaped special characters, and very large inline base64-encoded images are common causes of failed writes to the details field. Sanitize ticket description HTML through a parser (e.g., Python's html.parser or BeautifulSoup) before loading, and strip or replace malformed tags.

Required fields by HaloITSM ticket type

HaloITSM enforces different mandatory fields depending on the ticket type, and administrators set which fields are mandatory. Missing a required field rejects the write. The lists below are a starting point for the fields a migration usually has to populate. Confirm field names and mandatory flags on your instance's Swagger page and in each ticket type's configuration.

Incident (default required fields)

  • summary — ticket subject/title
  • details — description body
  • tickettype_id — must match the Incident type ID in your HaloITSM instance
  • user_id or reportedby — requester identity
  • status_id — must be a valid status for the Incident type

Service Request (default required fields)

  • summary
  • details
  • tickettype_id — Service Request type ID
  • user_id
  • status_id
  • catalogue_id (if the request maps to a service catalog item) — optional depending on configuration

Change (default required fields)

  • summary
  • details
  • tickettype_id — Change type ID; HaloITSM distinguishes Standard, Normal, and Emergency by type ID
  • status_id — Change statuses are a separate list from Incident statuses
  • user_id

Problem (default required fields)

  • summary
  • details
  • tickettype_id — Problem type ID
  • status_id
  • user_id
Warning

Administrators often add mandatory fields to HaloITSM Change ticket types during ITIL configuration, such as risk assessment, impact, implementation plan, and backout plan. These fields will cause rejections if your source data doesn't map to them. Audit your HaloITSM Change form configuration before migration and decide whether to populate these from Freshservice's change description or leave them as placeholder text.

How to map Freshservice fields to HaloITSM

The HaloITSM field names in this guide's mapping tables are common API names. Confirm each one on your instance's Swagger page, because names and required flags vary by version and configuration.

Ticket field mapping

Freshservice field HaloITSM field Mapping notes
id thirdpartyid or custom field Store the original Freshservice ID for audit trail
subject summary Direct map
description / description_html details HaloITSM accepts HTML in the details field; sanitize before POST
type (Incident / Service Request) tickettype_id Route to the correct HaloITSM ticket type ID
status status_id Map status integers to HaloITSM status IDs (configurable per type)
priority priority_id Freshservice uses 1–4 (Low to Urgent); map to HaloITSM priority IDs
source Custom field or drop HaloITSM doesn't have an equivalent "source" field by default
group_id team_id Requires a lookup table built during team import
responder_id agent_id Requires agent ID mapping
requester_id user_id Requires user ID mapping
department_id department_id or site_id Depends on your HaloITSM hierarchy setup
category / sub_category / item_category category_1 / category_2 / category_3 HaloITSM supports multi-level categorization
created_at dateoccurred Preserve original timestamps
updated_at lastupdate Preserve original timestamps
due_by targetdate SLA due date
custom_fields.* Custom fields (by API name) Validate data types match; HaloITSM rejects type mismatches

Priority mapping

Freshservice uses numeric priority values (1 = Low, 2 = Medium, 3 = High, 4 = Urgent) (api.freshservice.com). HaloITSM priorities are configurable and referenced by ID. The numeric ordering may be inverted — Freshservice uses ascending priority (1 = lowest) while many HaloITSM instances default to descending (1 = highest). Confirm your HaloITSM priority configuration before loading.

{
  "priority_map": {
    "1": {"halo_id": 4, "label": "Low"},
    "2": {"halo_id": 3, "label": "Medium"},
    "3": {"halo_id": 2, "label": "High"},
    "4": {"halo_id": 1, "label": "Urgent"}
  }
}

Status mapping

Freshservice uses hardcoded integers for default statuses. HaloITSM allows administrators to create custom statuses with custom IDs, configured per ticket type — meaning Incidents and Changes have separate, independent status lists. There is no universal map, but the common defaults:

Freshservice status (integer) Typical HaloITSM equivalent Notes
Open (2) New / Open HaloITSM status ID varies by instance
Pending (3) Awaiting Information No direct equivalent by default; may need a custom status
Resolved (4) Resolved
Closed (5) Closed

Build a translation table in your middleware that maps the integer 2 from Freshservice to the specific status_id representing "Open" in your target HaloITSM ticket type. Repeat this for every ticket type — a status ID of 3 on an Incident may mean something entirely different from status ID 3 on a Change.

Conversations and actions

Freshservice stores ticket history as Conversations, split into Notes (private) and Replies (public). HaloITSM stores these as Actions.

When mapping a Freshservice private Note to a HaloITSM Action, set the action's hidden-from-user flag (confirm its field name on your Swagger page) — otherwise internal technician notes are exposed to end-users on the HaloITSM self-service portal. The timestamp of the original reply must be explicitly set in the API payload; if you omit it, HaloITSM stamps every historical action with the migration date.

Service catalog and requested items

Freshservice Service Requests can contain multiple Requested Items — line items from the service catalog with their own custom fields, accessed through /api/v2/tickets/[id]/requested_items. These represent the specific hardware, software, or access that was requested.

In HaloITSM, these map to specific child tickets, linked assets, or service request custom fields. Extract requested items separately and map them to the appropriate HaloITSM structure. Plan to rebuild the catalog hierarchy itself in HaloITSM. Freshservice's API lists catalog items and categories (/api/v2/service_catalog/items, /api/v2/service_catalog/categories), which gives you a source inventory (api.freshservice.com).

Knowledge base migration

Freshservice structures knowledge as Categories → Folders → Articles. HaloITSM uses a similar hierarchical structure but allows for more granular permission controls.

When migrating articles, map the status (Draft vs. Published) and the approval_status. The bigger challenge is internal links: links between articles in Freshservice use internal Freshservice URLs. These URLs break post-migration. Your middleware must build a mapping table of old Article IDs to new HaloITSM Article IDs, then parse the HTML body of every article to rewrite internal hyperlinks to point to the new HaloITSM portal URLs.

CMDB and asset mapping

Freshservice stores configuration items in a unified asset model via /api/v2/assets. When extracting, pay attention to the asset_type_id — a laptop has different fields than a software license. You must recreate these asset types in HaloITSM before importing records.

Asset field mapping (example: hardware asset)

Freshservice asset field HaloITSM asset field Notes
name name Direct map
asset_type_id assettype_id Requires pre-created asset type in HaloITSM
display_id thirdpartyid or custom field Preserve for audit trail
description comment
department_id site_id Map via department→site lookup
assigned_on dateassigned
created_at dateinstalled
custom_fields.* Custom asset fields Type enforcement applies; validate before POST
ci_type Asset → CI flag HaloITSM promotes asset to CI via service dependency configuration

Extract relationships through the account-wide endpoint (/api/v2/relationships) rather than one call per asset. Keep the per-asset endpoint (/api/v2/assets/[display_id]/relationships) for spot checks during validation (api.freshservice.com).

Store the Freshservice display_id on each HaloITSM asset and check for it before each create. This prevents duplicate records on re-runs.

Attachments and inline images

Attachments are the most bandwidth-intensive part of an ITSM migration. Freshservice returns attachments as URLs (attachment_url), not file contents. Download attachments inline during conversation extraction, not as a separate phase, and re-fetch the ticket or conversation if a stored URL no longer downloads.

To migrate an attachment:

  1. Parse the Freshservice ticket or conversation JSON for the attachments array
  2. Issue a GET request to the attachment_url
  3. Download the binary payload to a secure staging environment
  4. Base64-encode the payload or use multipart/form-data to POST it to the HaloITSM /api/Attachment endpoint
  5. Associate the returned HaloITSM attachment ID with the corresponding ticket or action

Inline images embedded in HTML bodies require an extra step. Download the image, upload it to HaloITSM, then programmatically rewrite the src attribute in the HTML body to point to the new HaloITSM hosting URL. Failing to do this results in broken image icons when Freshservice decommissions your old instance.

Attachment size limits: In Freshservice, the total size of all of a ticket's attachments cannot exceed 40 MB (api.freshservice.com). Check your HaloITSM instance's attachment size setting before the load, and validate files near that limit before upload.

What are the migration methods?

Three approaches, ranked by fidelity:

Extract from Freshservice's REST API v2, transform in a middleware layer, and load into HaloITSM's REST API. This is the only method that preserves conversations, attachments, CMDB relationships, and ticket threading with full fidelity.

Best for: Any migration where conversation history, attachments, or CMDB data matters — which is most production ITSM environments.

Trade-off: Requires scripting, rate limit management, and error handling. Freshservice's extraction rate limits are typically the bottleneck during the extraction phase. HaloITSM's write throughput depends on your instance, so measure it during the sample migration. API-based migrations allow for multi-stage validation with checksums, record-count parity, field-level validation, and referential integrity checks.

2. CSV import

Export from Freshservice's UI (ticket list view or Analytics), transform in a spreadsheet, and import via HaloITSM's built-in import tools. Confirm which objects your HaloITSM version imports from CSV before you plan around this method.

Best for: Small datasets under 5,000 records where conversation history is not required.

What you lose: Freshservice's UI export does not include conversations, internal notes, or attachments. You also lose CMDB relationships, service catalog structure, and multi-line HTML bodies with inline images. CSV imports struggle with referential integrity, often resulting in orphaned records. Customers, sites, and asset types must already exist in HaloITSM before asset rows can reference them.

3. Managed migration service

A dedicated migration engineer handles the extraction, mapping, transformation, loading, validation, and delta sync. This gives you API-to-API fidelity without building and maintaining the tooling yourself.

How long does a Freshservice to HaloITSM migration take?

Migration duration is determined by three independent factors: Freshservice extraction throughput (rate-limited by plan tier), HaloITSM write throughput (instance-dependent), and data complexity (conversation density, CMDB relationship depth, custom field volume).

Most migrations are completed within 1–5 business days, depending on data volume, complexity, and API rate limits. Small datasets (under 10,000 records) can finish in under 24 hours.

Key variables that extend timelines:

  • Plans without rate limit add-on packs: conversation extraction becomes the dominant time cost
  • Deep CMDB relationship graphs (many assets with many-to-many CI relationships): every relationship needs ID remapping after the assets load
  • Large attachment volumes: download + re-upload is network-bound and cannot be parallelized beyond API limits
  • Custom fields requiring data type remediation discovered during sample migration

These figures describe the data migration ClonePartner performs, not your HaloITSM implementation. Platform configuration — workflow setup, SLA configuration, approval processes, portal customization — is a separate project that your team or your HaloITSM partner runs on its own timeline.

Edge cases and gotchas

Freshservice's shifting window during extraction

If tickets are created or updated while you're paginating through the list endpoint, the result set can shift — causing duplicates or missed records. Mitigate this by extracting within a strict updated_since time window and sorting by updated_at ascending. Run a delta sync afterward to catch anything that changed during extraction.

HaloITSM custom field type enforcement

HaloITSM custom fields enforce strict data types at write time. A field configured as a date rejects a string that doesn't parse as a valid ISO 8601 date. A number field rejects any non-numeric text, including formatted numbers with commas (e.g., "1,500" fails; 1500 succeeds). Boolean fields reject "Yes"/"No" strings — they require true/false. Build a type-casting layer in your transformation scripts and validate every custom field mapping against HaloITSM's type constraints during sample migration.

Disable automations before import

Before loading data into HaloITSM, disable approval processes, SLA timers, email notifications, and any AI/automation rules. Imported historical tickets should not trigger new SLA breaches, send emails to requesters, or fire approval workflows. Re-enable them after validation.

Duplicate detection

Both platforms identify users primarily by email address. If a user exists as both a requester and an agent in Freshservice (different records, same email), decide how to handle the merge before loading into HaloITSM.

Deleted requester handling

Freshservice tickets may reference requesters who have since been deleted from the account. The API returns these tickets with a requester_id that resolves to null or to a deleted user record. Before loading into HaloITSM, decide on a default user (e.g., a "Migrated Records" placeholder account) to assign these tickets to — HaloITSM requires a valid user_id on every ticket.

Article bodies, ticket descriptions, and conversation HTML may contain internal Freshservice URLs — article links, ticket references, inline images. All of these break after decommissioning. Your middleware needs a URL rewriting pass that maps old Freshservice URLs to new HaloITSM equivalents. This includes:

  • freshservice.com/a/solutions/articles/[id] → new HaloITSM KB article URL
  • freshservice.com/a/tickets/[id] → new HaloITSM ticket URL
  • Inline image src attributes pointing to Freshservice CDN → HaloITSM hosted attachment URLs

HaloITSM on-premise vs. cloud behavioral differences

On-premise HaloITSM instances differ from cloud in two relevant ways: (1) write throughput is bounded by host server specs rather than platform throttling, meaning both higher sustained throughput and unpredictable degradation under load are possible; (2) the instance's Swagger API documentation may reflect an older version than the cloud release, and some endpoint behaviors documented here (particularly OAuth token TTL and attachment size limits) may differ. Test your specific on-premise instance behavior during sample migration.

Validation after migration

Data migration is only complete when the target data passes validation:

  • Record-count parity — Total tickets, assets, users, and articles in HaloITSM should match Freshservice source counts by type.
  • Field-level spot checks — Sample 50–100 records across each object type. Verify custom fields, statuses, priorities, and assignments mapped correctly.
  • Conversation integrity — For sampled tickets, confirm all conversations (replies and notes) are present in correct chronological order with correct author attribution and correct private/public flag.
  • Attachment verification — Confirm attachments are downloadable and linked to the correct ticket/conversation in HaloITSM.
  • CMDB relationship verification — Spot-check that CI-to-CI and asset-to-ticket relationships survived the migration.
  • Referential integrity — Every ticket should reference a valid agent, user, and team. No orphaned foreign keys.
  • Custom field type validation — Confirm custom field values rendered correctly in HaloITSM's UI, not just that the write succeeded without error.

Conversation extraction with checkpointing

Conversation extraction is the most API-intensive part of the migration. The pattern below extracts all conversations for a list of tickets with exponential backoff on rate limits and checkpoint recovery:

import requests
import time
import json
 
BASE_URL = "https://yourdomain.freshservice.com/api/v2"
API_KEY = "your_api_key"
 
def extract_conversations(ticket_id, max_retries=5):
    conversations = []
    page = 1
    while True:
        for attempt in range(max_retries):
            resp = requests.get(
                f"{BASE_URL}/tickets/{ticket_id}/conversations",
                auth=(API_KEY, "X"),
                params={"page": page, "per_page": 100}
            )
            if resp.status_code == 429:
                wait = int(resp.headers.get("Retry-After", 60))
                time.sleep(wait + (2 ** attempt))  # exponential backoff
                continue
            resp.raise_for_status()
            break
        page_data = resp.json().get("conversations", [])
        if not page_data:
            break
        conversations.extend(page_data)
        page += 1
    return conversations
 
def migrate_with_checkpoint(ticket_ids, checkpoint_file="checkpoint.json"):
    try:
        with open(checkpoint_file) as f:
            completed = set(json.load(f))
    except FileNotFoundError:
        completed = set()
    
    for tid in ticket_ids:
        if tid in completed:
            continue
        convos = extract_conversations(tid)
        # ... transform and load into HaloITSM ...
        completed.add(tid)
        with open(checkpoint_file, "w") as f:
            json.dump(list(completed), f)

Checkpointing is critical because a 10,000-ticket extraction can take hours when rate-limited. Without it, a single failure at ticket 8,000 means restarting from scratch.

Cutover and delta sync

Run the bulk historical load during the working days before go-live, while your team keeps working in Freshservice. Plan go-live for a Monday morning. Before the working day starts, freeze Freshservice and run a final delta sync of the records created or modified since the bulk extraction. That pass takes about 30 minutes, and agents start Monday in HaloITSM.

Delta sync scope by object type:

Object Delta sync method Notes
Tickets updated_since parameter on /api/v2/tickets Most reliable; captures creates and edits
Problems Full re-paginate and compare updated_at No documented updated_since filter
Changes updated_since parameter Supported
Assets filter on updated_at at /api/v2/assets Date granularity; 30 per page, 40 pages max
Conversations Re-fetch conversations for all tickets in delta window No direct endpoint for changed conversations only
Agents / Users Full re-paginate and diff against local snapshot No time-window filter available
Knowledge base Re-fetch and diff by updated_at Manual comparison required

Validate the delta results, then re-enable HaloITSM automations.

The ClonePartner process

ClonePartner is an engineer-led migration service. We charge one fixed price per project, starting at $500 — no hourly billing, no per-record metering, no change orders. Pricing reflects data volume, object complexity, and the platforms involved.

Our process:

  1. Connect accounts
  2. Migration checklist — scope the mapping, identify edge cases
  3. Sample migration — unlimited runs until the data is right
  4. Full migration
  5. Delta sync — catch records created during the migration window

A dedicated migration engineer owns each project — you work directly with one person.

There is no free demo migration. A free 30-minute scoping call produces a fixed-price proposal.

Security and compliance

We maintain SOC 2 Type II and ISO 27001 certifications (independently audited), alongside GDPR, CCPA, and HIPAA safeguards. Data is encrypted using AES-256 at rest and TLS 1.2+ in transit. Staging databases auto-purge no later than 30 days after project close. NDAs and DPAs are available on request; a BAA is required before handling any PHI.

For more on exporting your Freshservice data, see How to Export Data from Freshservice: API Limits, Methods & Portability. For HaloITSM's export and portability story, see How to Export Data from HaloITSM: API Limits, Methods & Portability.

Frequently Asked Questions

How long does a Freshservice to HaloITSM migration take?
The data migration itself typically completes within 1–5 business days, depending on data volume, complexity, and API rate limits. Small datasets under 10,000 records can finish in under 24 hours. This covers the data transfer only — not the broader HaloITSM implementation or configuration project, which is a separate effort.
What are the Freshservice API rate limits for migration?
Freshservice rate limits are account-wide and per-minute: Starter 100 req/min, Growth 200, Pro 400, Enterprise 500. Sub-limits apply — List All Tickets is capped at 40, 70, 120, or 140 req/min depending on plan. Exceeding limits returns HTTP 429 with a Retry-After header.
Does HaloITSM have a built-in Freshservice migration tool?
No. Neither platform offers a built-in migration tool for this specific path. A full-fidelity Freshservice migration requires API-to-API extraction and transformation via custom scripts or a managed migration service.
What data is lost in a CSV-only Freshservice to HaloITSM migration?
A CSV-only migration loses ticket conversations, internal notes, attachments, CMDB relationships, service catalog structure, and ticket threading. Freshservice's UI export includes ticket metadata only. For full-fidelity migration, use the API.
Can I migrate my Freshservice CMDB to HaloITSM?
Yes. Freshservice assets and configuration items can be extracted via the API and mapped to HaloITSM's asset model, preserving relationships and custom fields. Both platforms use a unified asset-CI model, making the mapping closer to 1:1 than migrations to platforms like ServiceNow.

More from our Blog