SurveySparrow Ticket Management to HappyFox Migration Guide
Technical guide to migrating tickets, contacts, and history from SurveySparrow to HappyFox — covering API constraints, data mapping, and edge cases.
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
Migrating from SurveySparrow Ticket Management to HappyFox means translating a feedback-first ticket system — where tickets are generated from survey responses, NPS detractors, and form submissions — into a category-based helpdesk organized around structured ticket workflows, contact groups, and SLA policies. There is no native migration path between these platforms. No built-in importer exists on either side, and no major third-party migration tool currently offers a verified SurveySparrow-to-HappyFox connector. (help-desk-migration.com)
Every migration requires extracting data via SurveySparrow's REST API (v3), transforming it to match HappyFox's ticket and contact schema, and loading it through HappyFox's REST API (v1.1). This guide covers the full technical path: API constraints, data model mapping, viable migration methods, the step-by-step process, migration duration estimation, and the edge cases that cause silent data loss.
Last verified against SurveySparrow API v3 and HappyFox API v1.1 as of June 2025.
No native importer exists. Neither SurveySparrow nor HappyFox offers a built-in migration tool for this direction. HappyFox's support-assisted CSV ticket import only captures the ticket body and optionally one bundled private note — no threaded replies, no attachments. Plan for a custom API-based migration from day one. (support.happyfox.com)
For related migrations involving these platforms, see our SurveySparrow to Front Migration Guide and HappyFox to Zendesk Migration Guide.
SurveySparrow vs HappyFox: Architecture Differences
Before mapping fields, understand how these two data models diverge.
SurveySparrow Ticket Management is a module within SurveySparrow's experience management platform. Tickets are created from survey responses, workflow triggers, or manual entry. The data model is relatively flat: tickets have a subject, description, priority, status, assignee, and comments with public/private visibility. Contacts are managed through contact lists and contact properties. Workflows trigger actions on ticket creation, status change, priority change, or assignment change. (support.surveysparrow.com)
HappyFox is a purpose-built helpdesk. Tickets are organized under categories — the primary structural unit, typically representing departments or product lines. Each ticket has a status, priority, assignee, due date, tags, and category-specific custom fields. Contacts belong to contact groups, and HappyFox supports a built-in knowledge base with sections, articles, and visibility controls. Automations run through Smart Rules rather than survey-driven workflows.
| Concept | SurveySparrow | HappyFox |
|---|---|---|
| Primary ticket organizer | Flat list (no categories) | Categories (required on every ticket) |
| Ticket threading | Comments (public + internal notes) | Updates (staff reply, client reply, private note) |
| Contact organization | Contact Lists | Contact Groups |
| Custom fields | Ticket fields (text, dropdown, etc.) | Ticket + Contact custom fields (text, dropdown, number, date, multi-select, dependent) |
| Automation | Ticket Workflows | Smart Rules |
| Knowledge base | Not a core feature | Built-in KB with sections, articles, visibility controls |
| API authentication | OAuth 2.0 / Bearer token | HTTP Basic Auth (API key + auth code) |
| API version | v3 | v1.1 |
| Rate limits | Undisclosed ceiling; returns 429 | 500 GET/min, 300 POST/min; 10-min cooldown on 429 |
| Attachment size limit | 15 MB per file | 25 MB per request (total across all files) |
Why teams make this move: Licensing consolidation — keeping SurveySparrow for survey distribution while HappyFox becomes the support system of record. Common drivers include scaling into advanced SLA management, omni-channel routing (email, chat, phone, social), cleaner agency-style client segmentation via contact groups, and the need for a purpose-built knowledge base. SurveySparrow remains useful as the feedback layer; HappyFox becomes the operational helpdesk. (surveysparrow.com)
Migration Approaches: What Works
1. Native Export/Import (CSV-Based)
How it works: Export ticket data from SurveySparrow via Settings → Ticket Management → Export Data in XLSX or JSON format. Transform the file to match HappyFox's schema. Import contacts via HappyFox's bulk contact import (CSV/XLS). For tickets, HappyFox documents a support-assisted CSV ticket migration path that treats the first message as the ticket body and can at best collapse remaining correspondence into one private note. Attachments are not imported. (support.surveysparrow.com, support.happyfox.com)
When to use it: Very small datasets (under 500 tickets) where losing conversation history is acceptable.
Pros: No code required for the export step. Fast start.
Cons: Lowest fidelity. No threaded reply timeline. Attachments excluded. Does not preserve ticket-to-contact relationships automatically. No custom field mapping beyond what the CSV columns support.
Complexity: Low
2. API-to-API Migration (Recommended)
How it works: Authenticate to SurveySparrow via OAuth 2.0, paginate through tickets, comments, contacts, and ticket field metadata using the v3 REST API, then create or match contacts in HappyFox and load ticket shells plus replayed replies and notes through HappyFox's v1.1 REST API. (developers.surveysparrow.com)
When to use it: Any migration where you need to preserve ticket history, comments, custom field values, attachments, and contact relationships.
Pros: Full control over data fidelity. Preserves comments and internal notes with correct author attribution. Handles custom field mapping. Can be scripted and repeated for test runs.
Cons: Requires engineering time (typically 2–3 weeks for development, testing, and execution). Must handle rate limits on both sides. Attachment migration requires multipart/form-data POST requests.
Complexity: High
3. Custom ETL Pipeline
How it works: Extract SurveySparrow data into staging storage (PostgreSQL, S3, or similar), transform with your own mapping rules, and load HappyFox in controlled batches with retries, audit logs, and dead-letter queues.
When to use it: Dedicated engineering team, mixed helpdesk and CRM data, strict audit requirements, or regulated industries requiring full migration traceability.
Pros: Maximum control, repeatability, extensibility. Dead-letter queues capture failed records for manual review. Audit logs provide compliance evidence.
Cons: Most expensive to build and operate. Design batch sizes around SurveySparrow's page limits (100/page) and HappyFox's bulk ticket create limit (100/POST), then serialize per-ticket updates because HappyFox does not support concurrent update calls against the same ticket. (developers.surveysparrow.com)
Complexity: High
4. Middleware / iPaaS (Zapier, Make)
How it works: Wire event triggers to ticket creation or field updates through no-code connectors. SurveySparrow's outgoing webhooks expose response and contact events and cap each unique event payload at 5,000 per day per webhook. Zapier exposes a HappyFox + SurveySparrow pairing for trigger/action automation. (developers.surveysparrow.com)
When to use it: Forward-only sync after migration cutover. Not suitable for historical data transfer.
Pros: No-code setup for ongoing data flow.
Cons: Cannot migrate historical data. Limited field mapping. No batch processing. Costs scale with volume. Does not support comment replay or attachment transfer.
Complexity: Low (but limited scope)
Comparison Table
| Method | Historical Data | Comments Preserved | Attachments | Complexity | Best For |
|---|---|---|---|---|---|
| CSV Export + Support-Assisted Import | Partial (thread collapsed into 1 note) | No | No | Low | Small backfills, no history needed |
| API-to-API | Yes | Yes (public + private, correct author type) | Yes (multipart) | High | Full-fidelity migration |
| Custom ETL | Yes | Yes | Yes | High | Enterprise with audit/compliance requirements |
| Middleware (Zapier/Make) | No | N/A | No | Low | Forward-only sync after cutover |
Decision rule: Use CSV only if you can accept ticket shells with no comment threads and no attachments. If you have more than 5,000 tickets, need per-comment fidelity, or have compliance requirements dictating audit trails, use an API-based approach. For more context on migration tooling, see our guide on Best Tools & Services for Help Desk Data Migration.
Migration Duration Estimation
The primary bottleneck is HappyFox's POST rate limit (300/min with a 10-minute cooldown on 429). To avoid triggering the cooldown, target 200 POST requests/min.
Formula:
Total POST requests = tickets + contacts + (tickets × avg_comments_per_ticket)
Estimated API time = Total POST requests ÷ 200 requests/min
Example: 10,000 tickets, 8,000 unique contacts, average 5 comments per ticket:
POSTs = 10,000 (tickets) + 8,000 (contacts) + 50,000 (comments) = 68,000
API time = 68,000 ÷ 200 = 340 minutes ≈ 5.7 hours
Add 30–50% overhead for attachment uploads (multipart requests are slower), retry logic, pagination on the extraction side, and transform processing. Realistic wall-clock time for 10,000 tickets: 8–10 hours of API execution time.
For 50,000+ tickets, expect 40–60 hours of execution time spread across multiple days, with checkpoint/restart logic required.
These estimates assume no 429 cooldowns. A single cooldown adds 10 minutes of dead time — three cooldowns in a run add 30 minutes. This is why staying well under the 300 POST/min ceiling matters.
Pre-Migration Planning
Before writing any code or mapping fields, audit the source and establish a baseline.
- Data audit: Count tickets, public comments, private comments, contacts, attachments, custom fields, teams, statuses, and priorities in SurveySparrow. Export a sample (Settings → Ticket Management → Export Data) to understand field coverage. Record the counts — these become your validation baseline.
- Scope definition: Decide if you are migrating all history or only tickets from the last 24 months. Archiving older data reduces API load and migration time. Identify inactive, spam, or junk tickets to exclude. A common rule: exclude tickets with status "Spam" or "Junk" and tickets older than 3 years.
- Category strategy: Because HappyFox requires categories, audit your SurveySparrow forms, surveys, and teams. Decide how these map to HappyFox Categories (e.g., "NPS Survey" → "Customer Success," "Billing Survey" → "Billing," "Support Form" → "Technical Support"). Document this mapping in a spreadsheet before writing code.
- CRM-like data: If your SurveySparrow environment includes accounts, leads, or opportunities through custom fields or external syncs, decide where they land. HappyFox does not have a first-class CRM company model — these typically map to contact groups, contact custom fields, or stay in CRM with external ID references stored in a custom field.
- Unused data: Closed junk tickets, empty custom fields, stale tags, and orphan contacts are cheaper to leave behind than to migrate and clean up later.
- Migration strategy: Big bang for small desks (under 5,000 tickets); phased for department-by-department moves; incremental when the source must stay live through cutover.
- Risk mitigation: Plan a delta migration. Migrate historical data first, let the team test HappyFox for 3–5 business days, then run a final delta sync to capture tickets created during the testing phase.
- API access verification: Confirm your SurveySparrow plan includes API access (Business tier and above). Confirm your HappyFox plan includes API access — generate credentials via Apps → Goodies → API → Enable. HappyFox sandbox environments are available on Enterprise tiers; request one before starting test migrations.
If the SurveySparrow desk was fed by survey submissions or NPS workflows, capture the original submission IDs in staging even if HappyFox never surfaces them to agents. They are invaluable for audit and rollback. Store them as a custom text field (e.g., source_submission_id) in HappyFox.
Data Model and Field Mapping
This is where most migrations silently fail. SurveySparrow's flat ticket model must be translated into HappyFox's category-based structure. (developers.surveysparrow.com)
Core Object Mapping
| SurveySparrow Object | HappyFox Equivalent | Notes |
|---|---|---|
| Ticket | Ticket | Must be assigned to a HappyFox category (required field) |
| Ticket Comment (public, by requester) | User Reply (user_reply) |
Map by author type; use author role/email to distinguish |
| Ticket Comment (public, by agent) | Staff Update (staff_update) |
Disable notifications during replay via notify_user: false |
| Ticket Comment (internal) | Private Note (staff_pvtnote) |
Preserved via staff update API with private flag |
| Contact | Contact (User) | Email is the unique key on both sides; lowercase and dedupe |
| Contact List | Contact Group | Create groups first, then associate contacts |
| Ticket Field (custom) | Ticket Custom Field | Type matching required; picklist values must pre-exist |
| Contact Property | Contact Custom Field | Map by data type |
| Team | Category or ticket custom field | Routing vs. metadata — decide before migration |
| Ticket Workflow | Smart Rule | Cannot be migrated programmatically; document and rebuild |
| SLA Configuration | SLA Policy | Must be manually recreated; historical SLA breach data does not transfer |
The Category Constraint
In HappyFox, every ticket must belong to a Category. Since SurveySparrow does not use categories natively, your migration script must include routing logic that maps each ticket to a valid HappyFox Category ID — typically based on the originating form, survey, or team. Pre-create target categories and ensure they are public, because HappyFox's create-ticket endpoint requires a public category ID. Verify categories exist via GET /api/1.1/json/categories/ before starting ticket load.
Field-Level Mapping
| SurveySparrow Field | HappyFox Field | Type | Transformation | Mapping Type |
|---|---|---|---|---|
ticket.id |
Custom field source_ticket_id |
String | Keep original ID for audit, delta sync, and rollback | Approximation |
subject |
subject |
String | Direct copy | Native |
description / description_html |
text / html |
HTML/String | Sanitize HTML (strip <script>, fix broken tags), keep plain text fallback |
Native |
status |
status |
Enum → ID | Map source label to HappyFox status ID via GET /api/1.1/json/statuses/ |
Approximation |
priority |
priority |
Enum → ID | Map source label to HappyFox priority ID via GET /api/1.1/json/priorities/ |
Approximation |
assignee |
assigned_to |
User → Staff ID | Match by email, resolve to HappyFox staff ID via GET /api/1.1/json/staff/ |
Approximation |
requester.email |
email |
String | Lowercase, strip whitespace, dedupe; contact must exist before ticket creation | Native |
created_at |
created_at |
ISO 8601 | HappyFox API supports setting this on ticket creation; both platforms use UTC | Native |
tags |
tags |
Array → String | HappyFox accepts comma-separated tags | Native |
| Custom dropdown | Custom field (choice) | Enum | Picklist values must exist in HappyFox first; payload uses target option ID, not source label | Approximation |
| Custom text | Custom field (text) | String | Direct copy | Native |
created_at (per comment) |
Embedded in message body | Timestamp | HappyFox reply endpoints do not support per-update timestamp override; prepend [Originally posted: 2024-03-15T14:30:00Z] to message body, or store in a custom field |
Workaround |
Create custom fields and picklist values in HappyFox before importing tickets. The HappyFox API returns a 400 error with body {"error": "Invalid value for custom field <field_name>"} when a ticket payload includes a custom field value that doesn't match pre-configured options. Use GET /api/1.1/json/ticket_custom_fields/ to retrieve field IDs and valid choices. Dropdown and multi-choice payloads must use target option IDs (integers), not source labels (strings). (developers.surveysparrow.com)
API Constraints and Rate Limits
SurveySparrow API (v3)
- Authentication: OAuth 2.0 Bearer token via private app
- Base URL: Region-specific (e.g.,
https://api.surveysparrow.comfor US; confirm your region with SurveySparrow support or check your app settings) - Key endpoints:
GET /v3/tickets,GET /v3/tickets/{id},GET /v3/tickets/{id}/comments,GET /v3/ticket-fields - Pagination: Page-based; up to 100 records per page for tickets and comments. Response includes
has_next_pageboolean. - Comment visibility filter: The comments endpoint exposes a
privatefilter, but the docs do not clearly guarantee both visibilities in one pass. Extract public and private comments in separate calls to ensure complete coverage. (developers.surveysparrow.com) - Rate limits: API access is rate-limited; the public docs document 429 responses but do not publish a numeric ceiling. In practice, build adaptive throttling: start at 60 requests/min, increase until you observe 429s, then back off by 50%. Log every 429 with the
Retry-Afterheader value if present.
HappyFox API (v1.1)
- Authentication: HTTP Basic Auth using API key and auth code (generate via Apps → Goodies → API → Enable)
- Base URL:
https://{subdomain}.happyfox.com/api/1.1/json/ - Rate limits: 500 GET requests/minute, 300 POST requests/minute. Exceeding this triggers a 429 response and a 10-minute cooldown — the entire API is blocked, not just the endpoint that triggered it. (support.happyfox.com)
- Bulk ticket creation: Up to 100 tickets per single POST request via
POST /api/1.1/json/tickets/— but attachments are not supported in bulk mode. Bulk payload format uses a JSON array of ticket objects. - Single ticket creation:
POST /api/1.1/json/tickets/with a single JSON object. Supportsmultipart/form-datafor attachments. - Attachments: Require
multipart/form-dataPOST — cannot be included in bulk ticket payloads. File parameter name:attachments. - Timestamp behavior: The API lets you set ticket
created_aton creation. However, reply and private-note endpoints (staff_update,staff_pvtnote,user_reply) do not document a per-update timestamp override. Per-reply timestamps must be preserved by embedding the source timestamp into the imported message body (e.g., prepend[Originally posted: {ISO timestamp}]). An alternative: store original comment timestamps in a custom text field per ticket, formatted as a JSON array. Both platforms use UTC, but verify timezone handling — off-by-one-hour errors from DST conversion are common. (support.happyfox.com) - Concurrent updates: HappyFox does not support concurrent update calls against the same ticket. Serialize all comment replay calls per ticket. Attempting parallel updates produces 409 Conflict or corrupted thread ordering.
The 10-minute cooldown on HappyFox's 429 response is the single biggest bottleneck for large migrations. If you hit the rate limit, your entire API access is blocked for 10 full minutes — not just the offending endpoint. Implement exponential backoff and keep POST requests well under 300/min. Target 200/min as a safe operating ceiling. A single cooldown in a 10,000-ticket migration adds 10 minutes of dead time; three cooldowns add 30 minutes.
API Endpoint Reference
# SurveySparrow extraction
GET /v3/tickets?limit=100&page=1
GET /v3/tickets/{id}
GET /v3/tickets/{id}/comments?private=false&limit=100&page=1
GET /v3/tickets/{id}/comments?private=true&limit=100&page=1
GET /v3/ticket-fields
# HappyFox reference data
GET /api/1.1/json/categories/
GET /api/1.1/json/statuses/
GET /api/1.1/json/priorities/
GET /api/1.1/json/staff/
GET /api/1.1/json/ticket_custom_fields/
GET /api/1.1/json/users/?q=email:user@example.com
# HappyFox loading
POST /api/1.1/json/tickets/ # single or bulk (array)
POST /api/1.1/json/ticket/<ticket_number>/staff_update/
POST /api/1.1/json/ticket/<ticket_number>/staff_pvtnote/
POST /api/1.1/json/ticket/<ticket_number>/user_reply/Step-by-Step Migration Process
Step 1: Audit and Plan
- Count tickets, comments, contacts, attachments, and custom fields in SurveySparrow
- Export a sample (Settings → Ticket Management → Export Data) to understand field coverage and identify data quality issues
- Document SLA policies, workflows, and assignment rules for manual rebuild in HappyFox
- Define migration scope, exclusion rules, and category mapping strategy
- Establish baseline counts for validation (e.g., "4,832 tickets, 22,150 public comments, 3,410 private notes, 6,200 contacts, 1,890 attachments")
Step 2: Set Up HappyFox
- Create categories that map to your SurveySparrow ticket groupings (verify via
GET /api/1.1/json/categories/) - Create all custom fields (ticket and contact) with matching types and picklist values
- Create contact groups mirroring your SurveySparrow contact lists
- Generate API credentials (Apps → Goodies → API → Enable)
- Request a sandbox environment if on Enterprise tier — run all test migrations there before touching production
- Create a "Migration Bot" staff account to attribute imported updates to, keeping migration activity distinguishable from organic activity
Step 3: Extract Data from SurveySparrow
Use SurveySparrow's v3 API to paginate through tickets, then fetch comments per ticket. Extract public and private comments in separate calls.
import requests
import time
import json
BASE_URL = "https://api.surveysparrow.com/v3"
HEADERS = {"Authorization": "Bearer YOUR_ACCESS_TOKEN"}
def extract_tickets(per_page=100):
all_tickets = []
page = 1
while True:
resp = requests.get(
f"{BASE_URL}/tickets",
headers=HEADERS,
params={"page": page, "per_page": per_page}
)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 60))
print(f"Rate limited. Sleeping {retry_after}s")
time.sleep(retry_after)
continue
resp.raise_for_status()
data = resp.json()
tickets = data.get("data", [])
if not tickets:
break
all_tickets.extend(tickets)
if not data.get("has_next_page", False):
break
page += 1
time.sleep(1) # Adaptive throttle
return all_tickets
def extract_comments(ticket_id, private=False):
all_comments = []
page = 1
while True:
resp = requests.get(
f"{BASE_URL}/tickets/{ticket_id}/comments",
headers=HEADERS,
params={"private": str(private).lower(), "page": page, "limit": 100}
)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 60))
time.sleep(retry_after)
continue
resp.raise_for_status()
data = resp.json()
comments = data.get("data", [])
if not comments:
break
all_comments.extend(comments)
page += 1
return all_commentsStage raw JSON in a local database (SQLite for small migrations, PostgreSQL for large ones) or secure cloud storage. Download attachments immediately — do not rely on source URLs remaining valid after migration. Store them locally with a mapping file linking each attachment to its source ticket and comment ID.
Step 4: Transform Data
Map each SurveySparrow ticket to a HappyFox payload:
- Resolve
statusandprioritystrings to HappyFox integer IDs (pre-fetch viaGET /api/1.1/json/statuses/andGET /api/1.1/json/priorities/). Build a lookup dictionary:{"Open": 1, "Pending": 2, "Closed": 3}. - Match
assigneeemail to HappyFox staff IDs (pre-fetch viaGET /api/1.1/json/staff/). If no match, assign to a default staff member or leave unassigned. - Convert comments into HappyFox update payloads — distinguish requester replies from agent updates from private notes based on the comment author's email compared to the ticket requester's email and the staff email list.
- Map custom field names to HappyFox custom field IDs. Dropdown values map by target option ID, not source label.
- Assign each ticket a valid HappyFox Category ID based on your routing logic (form name, team, tag, or custom field value).
- Sanitize HTML: strip
<script>tags, fix unclosed tags, convert inline base64 images to placeholder text (or extract and re-upload as attachments).
Step 5: Load Contacts into HappyFox
Create contacts before tickets — HappyFox requires a valid contact email to associate with each ticket. Deduplicate by email (lowercase, strip whitespace) before loading.
from requests.auth import HTTPBasicAuth
import time
HF_BASE = "https://yourcompany.happyfox.com/api/1.1/json"
HF_AUTH = HTTPBasicAuth("API_KEY", "AUTH_CODE")
def find_or_create_contact(name, email):
email = email.strip().lower()
# Check if contact exists
resp = requests.get(
f"{HF_BASE}/users/",
auth=HF_AUTH,
params={"q": f"email:{email}"}
)
resp.raise_for_status()
users = resp.json()
if users:
return users[0] # Return existing contact
# Create new contact
resp = requests.post(
f"{HF_BASE}/users/",
auth=HF_AUTH,
json={"name": name, "email": email}
)
if resp.status_code == 429:
time.sleep(600) # 10-minute cooldown
return find_or_create_contact(name, email) # Retry
resp.raise_for_status()
return resp.json()Step 6: Load Tickets into HappyFox
Use the bulk endpoint for tickets without attachments (up to 100 per batch). For tickets with attachments, use individual multipart/form-data requests.
def create_happyfox_ticket(ss_ticket, mapped_category, mapped_contact_email, mapped_staff_id, source_ticket_id):
payload = {
"category": mapped_category,
"email": mapped_contact_email,
"subject": ss_ticket["subject"],
"text": ss_ticket.get("description", ""),
"html": ss_ticket.get("description_html"),
"assignee": mapped_staff_id,
"priority": map_priority(ss_ticket["priority"]),
"status": map_status(ss_ticket["status"]),
"t-cf-source_ticket_id": str(source_ticket_id) # Custom field for audit
}
resp = requests.post(
f"{HF_BASE}/tickets/",
auth=HF_AUTH,
json=payload
)
if resp.status_code == 429:
print("Rate limited. Sleeping 600s.")
time.sleep(600)
return create_happyfox_ticket(ss_ticket, mapped_category, mapped_contact_email, mapped_staff_id, source_ticket_id)
if resp.status_code == 400:
print(f"Validation error: {resp.json()}") # Log exact error for debugging
return None
resp.raise_for_status()
return resp.json()
# Bulk creation (no attachments, up to 100 per POST)
def create_tickets_bulk(ticket_payloads):
"""ticket_payloads: list of dicts, max 100"""
resp = requests.post(
f"{HF_BASE}/tickets/",
auth=HF_AUTH,
json=ticket_payloads # Array of ticket objects
)
resp.raise_for_status()
return resp.json()Step 7: Replay Ticket Updates (Comments)
For each ticket, replay the comment history in chronological order. Map public requester comments to user_reply, public agent comments to staff_update, and private comments to staff_pvtnote. Serialize updates per ticket — HappyFox does not support concurrent update calls against the same ticket.
def migrate_ticket(ss_ticket, category_id, staff_id, staff_emails, ledger):
# Skip if already migrated (idempotency)
if ss_ticket["id"] in ledger:
return ledger[ss_ticket["id"]]
hf_contact = find_or_create_contact(
ss_ticket["requester"]["name"],
ss_ticket["requester"]["email"]
)
hf_ticket = create_happyfox_ticket(
ss_ticket, category_id, hf_contact["email"], staff_id, ss_ticket["id"]
)
if not hf_ticket:
dead_letter.append(ss_ticket) # Failed — add to dead-letter queue
return None
public_comments = extract_comments(ss_ticket["id"], private=False)
private_comments = extract_comments(ss_ticket["id"], private=True)
timeline = sorted(public_comments + private_comments, key=lambda c: c["created_at"])
for comment in timeline:
files = download_attachments(comment.get("attachments", []))
# Prepend original timestamp to message body
timestamp_prefix = f"[Originally posted: {comment['created_at']}]\n\n"
comment_text = timestamp_prefix + comment.get("text", "")
if comment.get("private"):
add_private_note(hf_ticket["id"], comment_text, files)
elif comment["author"]["email"] == ss_ticket["requester"]["email"]:
add_user_reply(hf_ticket["id"], comment_text, files)
elif comment["author"]["email"] in staff_emails:
add_staff_update(hf_ticket["id"], comment_text, files, notify_user=False)
else:
# Unknown author — default to private note to avoid sending notifications
add_private_note(hf_ticket["id"], comment_text, files)
time.sleep(0.3) # ~200 POST/min rate control
ledger[ss_ticket["id"]] = hf_ticket["id"]
save_ledger(ledger)
return hf_ticket["id"]Disable outbound customer notifications and survey sends during replay — HappyFox documents notify_user and send_survey controls on staff update endpoints. Set both to false for all migrated updates. (support.happyfox.com)
Step 8: Validate
- Compare total ticket and contact counts: source vs. target
- Spot-check at least 5–10% of tickets (minimum 100 tickets) for comment completeness
- Verify custom field values match — script a comparison between source staging data and HappyFox API responses
- Confirm contact-to-ticket associations
- Validate attachment presence on tickets that had them — compare attachment counts per ticket
- Run validation by category, not just random sampling
- Have an agent reply to a migrated ticket to confirm contact routing and notification delivery work correctly
Edge Cases and Failure Modes
Duplicate contacts: SurveySparrow may have contacts across multiple contact lists with the same email. HappyFox uses email as a unique key — deduplicate before import. Normalize case, strip whitespace, and store one canonical email per contact. Use HappyFox's contact search (GET /api/1.1/json/users/?q=email:user@example.com) for idempotent re-runs. (support.happyfox.com)
Missing requester email: If SurveySparrow tickets lack a stable requester email (common with anonymous survey-generated tickets), decide the rule before migration. HappyFox's API treats email as a required field for new contacts. Create a fallback "migration-orphan@yourcompany.com" contact and assign orphaned tickets to it. Tag these tickets with migration_orphan for post-migration remediation. Do not silently skip these tickets — they belong in a remediation queue.(support.happyfox.com)
Comment author attribution: SurveySparrow comments may not clearly distinguish between requester and agent in all cases. Build author classification logic: compare the comment author's email against the ticket requester's email and your pre-fetched staff email list. If the author matches neither, default to staff_pvtnote (private note) to avoid accidentally sending notifications to external users.
Attachment size limits: SurveySparrow comment creation allows specific file types up to 15 MB per file. HappyFox accepts attachments with a total size limit of 25 MB per request (across all files in one API call). A single SurveySparrow comment with three 10 MB files (30 MB total) exceeds HappyFox's per-request limit. Split into multiple update calls or log for manual review. Your script must download files into local storage and push via multipart/form-data upload. (developers.surveysparrow.com)
Custom field type mismatches: A text field in SurveySparrow mapped to a dropdown in HappyFox will fail if the value doesn't match an existing picklist option exactly (case-sensitive). HappyFox returns {"error": "Invalid value for custom field <field_name>"}. Pre-validate all custom field values against HappyFox's allowed options before attempting ticket creation. Log mismatches to a remediation file.
Duplicate ticket prevention: Network timeouts will happen. If a POST request times out, the ticket may or may not have been created on HappyFox's side. Your script must query HappyFox (search by source_ticket_id custom field or subject + email combination) before retrying. Maintain a source-to-target ID ledger (JSON file or database table) and keep the migration script idempotent. Every ticket creation should check the ledger first.
Timestamps: HappyFox's API lets you set ticket created_at, but reply endpoints do not document a per-update timestamp override. You can preserve original ticket creation time cleanly. For per-reply timestamps, three options: (1) prepend [Originally posted: {timestamp}] to each message body, (2) store all original comment timestamps as a JSON array in a custom text field on the ticket, or (3) accept that reply timestamps will reflect migration time. Both platforms use UTC, but verify timezone handling — off-by-one-hour errors from DST conversion are common. Test with a known timestamp before bulk migration. (support.happyfox.com)
HTML sanitization: SurveySparrow comment bodies may contain inline base64-encoded images (<img src="data:image/png;base64,..."/>). These render correctly in SurveySparrow but may not display in HappyFox or may bloat the ticket body. Extract base64 images, save as files, and re-upload as standard attachments. Replace the inline <img> tag with a placeholder like [See attached: image_001.png].
What Cannot Be Migrated Programmatically
- Automations and workflows: SurveySparrow ticket workflows and HappyFox Smart Rules operate on different logic engines with different trigger types, conditions, and actions. Neither platform exposes automation rules in a format the other can consume. Document every active SurveySparrow workflow (trigger, conditions, actions) in a spreadsheet and rebuild equivalent Smart Rules in HappyFox manually. Budget 1–2 days for a team with 10–20 active workflows.
- SLA policies: Historical SLA breach data from SurveySparrow will not map into HappyFox's SLA engine or reporting. Recreate SLA policies in HappyFox after migration. SLA timers reset — there is no way to import "time already elapsed" on open tickets.
- Inline images: Depending on how SurveySparrow formats inline base64 images in comments, they may need to be extracted and re-uploaded as standard attachments in HappyFox.
- CRM-like objects: HappyFox's public API does not document a general-purpose custom-object layer. If your SurveySparrow environment behaves like a mini-CRM with accounts, opportunities, or activities, that model must stay in CRM or be flattened into custom fields and notes during migration. Custom-field-heavy environments usually move cleanly; custom-object-heavy environments do not. (support.happyfox.com)
- Agent activity logs and audit trails: SurveySparrow's internal ticket activity logs (who changed status, who reassigned) cannot be exported via API and have no equivalent import endpoint in HappyFox.
Rollback Procedure
If validation fails, you need a way to clean up HappyFox and re-run:
- Use the source-to-target ledger to identify all tickets created during the failed run.
- HappyFox does not expose a bulk delete endpoint in its public API. Individual ticket deletion is available via
DELETE /api/1.1/json/ticket/<ticket_id>/— but this is rate-limited like all other endpoints. - For large failed runs (1,000+ tickets), contact HappyFox support to request a bulk purge of tickets created by your Migration Bot account within a specific date range.
- Contacts do not need rollback — they are reusable across runs. Your idempotent contact creation logic handles this.
- Before re-running, clear the ledger entries for failed tickets only. Do not delete the entire ledger.
Validation and Post-Migration
Validation checklist
Validation should be mechanical and thorough:
- Compare ticket count, public comment count, private note count, contact count, and attachment count between source staging database and HappyFox (query via API)
- Run field-level checks for every required field and every mapped custom field — script a comparison, don't do this manually
- Sample at least 100 tickets or 5% of volume, whichever is larger — verify complete comment threads, correct author attribution, and attachment presence
- Run validation by category to catch mapping errors that affect entire ticket groups
- Have support agents reply to a migrated ticket to confirm contact routing, notification delivery, and SLA timer activation work
- Verify that no customer-facing notifications were sent during migration (check HappyFox's outbound email log)
- Keep SurveySparrow accessible in read-only mode until the target passes sign-off
For a complete QA runbook, see our Post-Migration QA Checklist.
Post-migration checklist
- Rebuild Smart Rules to match SurveySparrow ticket workflows
- Configure SLA policies in HappyFox
- Set up email forwarding from old SurveySparrow channels to HappyFox
- Re-create canned responses and templates
- Train agents on HappyFox's category-based workflow
- Monitor for orphaned tickets (missing contacts,
migration_orphantag) in the first 48 hours - Verify contact group memberships match original SurveySparrow contact list assignments
- Keep SurveySparrow in read-only mode for 30 days post-migration for compliance referencing
- Archive the source-to-target ID ledger for future audit reference
For automation rebuild planning, see our Help Desk Data Migration Playbook.
Best practices
- Never test in production. Run a sandbox migration first. HappyFox provides sandbox environments for Enterprise tiers. If you're on a lower tier, create a separate test category and delete test tickets after validation.
- Freeze SurveySparrow schema. Institute a change freeze on custom fields, statuses, and priorities two weeks before migration. Any changes after the mapping is built require re-mapping and re-testing.
- Plan a delta migration. Migrate historical data first, test for 3–5 business days, then run a final delta sync to capture tickets created during the testing phase. Use
created_atfiltering on the SurveySparrow API to extract only new tickets. - Disable notifications during replay. HappyFox sends customer-facing emails when you create updates via API. Set
notify_user: falseandsend_survey: falseon every staff update and private note call during migration. - Keep code modular. Split into
auth,extract,transform,load, andvalidatemodules. Do not mix mapping logic into API client code — that is how one-off migration scripts become unmaintainable. - Log everything. Every API call, response status, response body (truncated), and timing should be logged. When a migration fails at ticket 4,200 of 10,000, you need to know exactly why and exactly where to resume.
When to Use a Managed Migration Service
Build it yourself if you have an engineer who can dedicate 2–3 weeks, your dataset is under 10,000 tickets, and you are comfortable with edge-case data loss around attachments on bulk imports and approximate timestamp preservation on comments.
Use a managed service when:
- You have 50,000+ tickets and HappyFox's 10-minute rate-limit cooldown makes DIY scripts fragile (estimated 40–60 hours of API execution time at this scale)
- You need full comment thread fidelity including private notes, inline attachments, and correct author attribution
- Your team's engineering bandwidth is better spent on product work than a one-off migration
- You need validation guarantees — record-count reconciliation, field-level spot checks, and rollback planning
- You operate in a regulated industry (healthcare, finance) where migration audit trails and data residency compliance are non-negotiable
The hidden cost of a DIY migration is not the first API script. It is weeks of mapping decisions, retries, audit logging, idempotency, UAT, and re-runs under HappyFox's rate limits and per-ticket sequencing rules.
Frequently Asked Questions
- Can I migrate SurveySparrow tickets to HappyFox with CSV alone?
- Only for low-fidelity moves. SurveySparrow can export ticket data in XLSX/JSON, and HappyFox documents a support-assisted CSV ticket migration path, but it treats the first message as the ticket body, can bundle remaining correspondence into one private note, and does not import attachments. An API-based migration is required for full history.
- What are HappyFox's API rate limits for ticket migration?
- HappyFox allows 500 GET requests per minute and 300 POST requests per minute. Exceeding either triggers a 429 error with a 10-minute cooldown. For bulk ticket creation, you can send up to 100 tickets per request, but attachments are not supported in bulk mode — those require individual multipart/form-data requests.
- How do I map SurveySparrow tickets to HappyFox categories?
- SurveySparrow does not use categories natively, so you must create routing logic in your migration script. Typically, teams map SurveySparrow form names, survey types, or team assignments to specific HappyFox Category IDs. Pre-create target categories and ensure they are public before loading tickets.
- Will historical ticket timestamps be preserved during migration?
- HappyFox's API lets you set ticket created_at on ticket creation, so original ticket timestamps can be preserved. However, reply and private-note endpoints do not document a per-update timestamp override, so exact per-reply chronology may require embedding the source timestamp into the message body.
- Can I migrate SurveySparrow workflows to HappyFox Smart Rules?
- No. SurveySparrow ticket workflows and HappyFox Smart Rules operate on different logic engines. Neither platform exposes automation rules in a format the other can consume. You need to manually document your SurveySparrow workflows and rebuild equivalent Smart Rules in HappyFox after migration.

